durable-actors 0.7.14

Standalone regional durable-actors control plane, host, and durability runtime
use super::super::cursor::{Metadata, ReplayPage};
use crate::request_traces::{TracePage, TraceRecord, replay::ReplayQuery};
use anyhow::Result;
use rusqlite::{Connection, Transaction, params};

pub(super) fn query(
    connection: &mut Connection,
    project_id: &str,
    query: &ReplayQuery,
) -> Result<TracePage> {
    let transaction = connection.transaction()?;
    let page = ReplayPage::new(project_id, query, metadata(&transaction, project_id)?)?;
    let records = select(
        &transaction,
        project_id,
        query.limit,
        page.after(),
        page.head(),
    )?;
    page.finish(query.limit, records)
}

pub(super) fn metadata(transaction: &Transaction<'_>, project_id: &str) -> Result<Metadata> {
    Ok(transaction.query_row(
        "SELECT generation, COALESCE(p.pruned, 0),
                MAX(0, COALESCE(p.total, 0) - (
                    SELECT COUNT(*) FROM traces WHERE project_id = ?1
                )), COALESCE(p.head, 0)
         FROM trace_meta LEFT JOIN trace_projects p ON p.project_id = ?1",
        [project_id],
        |row| {
            Ok(Metadata {
                generation: row.get(0)?,
                pruned: row.get::<_, i64>(1)? as u64,
                evicted: row.get::<_, i64>(2)? as u64,
                head: row.get::<_, i64>(3)? as u64,
            })
        },
    )?)
}

fn select(
    transaction: &Transaction<'_>,
    project_id: &str,
    limit: usize,
    after: Option<u64>,
    head: u64,
) -> Result<Vec<TraceRecord>> {
    let sql = if after.is_some() {
        "SELECT position, event FROM traces
         WHERE project_id = ?4 AND position > ?1 AND position <= ?2
         ORDER BY position ASC
         LIMIT ?3"
    } else {
        "SELECT position, event FROM traces
         WHERE project_id = ?4 AND position > ?1 AND position <= ?2
         ORDER BY started_at_ms DESC, position DESC
         LIMIT ?3"
    };
    let mut statement = transaction.prepare(sql)?;
    let rows = statement.query_map(
        params![
            after.unwrap_or(0) as i64,
            head as i64,
            (limit + 1) as i64,
            project_id
        ],
        |row| Ok((row.get::<_, i64>(0)? as u64, row.get::<_, String>(1)?)),
    )?;
    rows.map(|row| {
        let (sequence, json) = row?;
        Ok(TraceRecord {
            sequence,
            event: serde_json::from_str(&json)?,
        })
    })
    .collect()
}