From 224d16a3796dd7127b1fb924d71728060f75dba3 Mon Sep 17 00:00:00 2001 From: HC Cheng Date: Thu, 28 May 2026 19:47:36 -0400 Subject: [PATCH] fix(store): checkpoint WAL on close and startup to prevent orphan accumulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add WAL checkpoint on store close and after WAL-mode enable at startup. This ensures graceful shutdown leaves a clean WAL, and crash recovery merges stale WAL on next open. Both use PASSIVE mode (non-blocking, no ftruncate). Best-effort — silently skip if concurrent reader holds a lock. Fixes #277 --- src/store/store.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/store/store.c b/src/store/store.c index 0ced405d..be4da299 100644 --- a/src/store/store.c +++ b/src/store/store.c @@ -339,6 +339,10 @@ static int configure_pragmas(cbm_store_t *s, bool in_memory) { if (rc != CBM_STORE_OK) { return rc; } + /* Recover stale WAL from previous crash (best-effort). + * PASSIVE never blocks readers and never ftruncates. + * May fail with SQLITE_BUSY if another process holds a lock. */ + (void)sqlite3_exec(s->db, "PRAGMA wal_checkpoint(PASSIVE)", NULL, NULL, NULL); rc = exec_sql(s, "PRAGMA synchronous = NORMAL;"); if (rc != CBM_STORE_OK) { return rc; @@ -725,6 +729,12 @@ void cbm_store_close(cbm_store_t *s) { return; } + /* Checkpoint WAL before close to prevent orphan WAL accumulation. + * Best-effort — silently skips if concurrent reader holds a lock. */ + if (s->db && s->db_path) { + (void)sqlite3_wal_checkpoint_v2(s->db, NULL, SQLITE_CHECKPOINT_PASSIVE, NULL, NULL); + } + /* Finalize all cached statements */ finalize_stmt(&s->stmt_upsert_node); finalize_stmt(&s->stmt_find_node_by_id);