Skip to main content

faucet_cli/serve/history/
sqlite.rs

1//! SQLite-backed run history (`serve-history-sqlite`, Phase 5 of #127).
2//! Connection setup only — the schema, statements, and `RunHistory` impl are
3//! shared with Postgres via [`impl_sql_history!`](super::sql).
4
5use super::HistoryError;
6use super::sql::{DDL, Dialect, Stmts, impl_sql_history};
7use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions};
8use std::str::FromStr;
9use std::time::Duration;
10
11impl_sql_history!(SqliteHistory, sqlx::SqlitePool);
12
13impl SqliteHistory {
14    /// Connect (creating the database file if missing), create the schema if
15    /// absent, and return the backend. WAL + a busy timeout let the connection
16    /// pool tolerate concurrent run writes. `lease_ttl` and `instance_id` drive
17    /// instance-fenced orphan recovery (#146 H7).
18    pub async fn connect(
19        url: &str,
20        idem_retention: Duration,
21        lease_ttl: Duration,
22        instance_id: String,
23    ) -> Result<Self, HistoryError> {
24        let opts = SqliteConnectOptions::from_str(url)
25            .map_err(|e| HistoryError::Backend(format!("invalid sqlite url '{url}': {e}")))?
26            .create_if_missing(true)
27            .journal_mode(SqliteJournalMode::Wal)
28            .busy_timeout(Duration::from_secs(5));
29        let pool = SqlitePoolOptions::new()
30            .max_connections(5)
31            .connect_with(opts)
32            .await
33            .map_err(|e| HistoryError::Backend(format!("SQLite connection failed: {e}")))?;
34        for stmt in DDL {
35            sqlx::query(stmt)
36                .execute(&pool)
37                .await
38                .map_err(|e| HistoryError::Backend(format!("creating run-history schema: {e}")))?;
39        }
40        Ok(Self::from_parts(
41            pool,
42            idem_retention,
43            lease_ttl,
44            instance_id,
45            Stmts::new(Dialect::Sqlite),
46        ))
47    }
48}