diff --git a/src/db.rs b/src/db.rs index 26394c6..f4da47c 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,7 +1,7 @@ use anyhow::Result; use sqlx::{ sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions}, - SqlitePool, + ConnectOptions, Connection, SqlitePool, }; use std::str::FromStr; use std::time::Duration; @@ -11,16 +11,26 @@ pub type Pool = SqlitePool; pub async fn init_pool(database_url: &str, max_connections: u32) -> Result { info!("Connecting to database: {}", database_url); - // Per-connection options so EVERY pooled connection gets them (a one-off - // `PRAGMA` on the pool only configures whichever connection served it): - // WAL for reader/writer concurrency, foreign keys on, and a busy_timeout so - // a transient SQLITE_BUSY under concurrent access waits-and-retries instead - // of failing the transaction. + // Per-connection options so EVERY pooled connection gets them: WAL for + // reader/writer concurrency, foreign keys on, and a busy_timeout so a + // transient SQLITE_BUSY under concurrent access waits-and-retries. let opts = SqliteConnectOptions::from_str(database_url)? .create_if_missing(true) .journal_mode(SqliteJournalMode::Wal) .foreign_keys(true) .busy_timeout(Duration::from_secs(5)); + // Establish WAL on the file via ONE connection BEFORE the pool opens. + // Switching a fresh DB to WAL is a one-time file-level change; letting + // several pooled connections do it concurrently at warm-up races that + // switch and can surface a spurious lock. Serialize it here so every + // pooled connection thereafter only re-asserts an already-WAL file. + { + let mut conn = opts.clone().connect().await?; + sqlx::query("PRAGMA journal_mode=WAL") + .execute(&mut conn) + .await?; + conn.close().await?; + } let pool = SqlitePoolOptions::new() .max_connections(max_connections) .connect_with(opts)