Skip to content

fix(store): opportunistic PASSIVE WAL checkpoint to bound the content-store WAL (#985)#988

Open
alove20 wants to merge 1 commit into
mksglu:mainfrom
alove20:fix/985-wal-checkpoint-timer
Open

fix(store): opportunistic PASSIVE WAL checkpoint to bound the content-store WAL (#985)#988
alove20 wants to merge 1 commit into
mksglu:mainfrom
alove20:fix/985-wal-checkpoint-timer

Conversation

@alove20

@alove20 alove20 commented Jul 22, 2026

Copy link
Copy Markdown

Addresses the WAL-bloat / reader-starvation half of #985, within the constraints of ADR 0001 (no per-DB locking).

Problem. closeDB()'s wal_checkpoint(TRUNCATE) is the only WAL-truncation path and only runs on graceful shutdown. A server killed hard — crash, reboot, SIGKILL, or a Windows parent-death before the lifecycle guard fires — never reaches it. Under multi-session load the shared per-project content/<hash>.db WAL then grows unbounded (observed 30 MB+ regrowth within an hour; the same reader-starvation failure ADR 0001 attributes to #560, where a lingering reader lock prevents any checkpoint from resetting the WAL).

Change. Add startWalCheckpointTimer(db, intervalMs) — an .unref()'d periodic PRAGMA wal_checkpoint(PASSIVE). PASSIVE reclaims whatever WAL frames it can between reader gaps; it never blocks and adds no locking, so it stays fully within ADR 0001's multi-writer contract (no EXCLUSIVE, no lockfile). The server starts it for the content store only (session DBs are per-session and uncontended); default 60 s, CONTEXT_MODE_WAL_CHECKPOINT_MS tunes it (0 disables). ContentStore.close()/cleanup() stop the timer.

Complements the parent-death fix (#982 / #987): that removes the lingering-reader root cause on graceful paths; this bounds the WAL on the hard-exit paths that never checkpoint at all.

Adds tests/util/wal-checkpoint-timer.test.ts (fires PASSIVE on interval, stop works, disabled on non-positive/non-finite, swallows pragma errors, and asserts it never issues TRUNCATE/EXCLUSIVE). Full build + typecheck + store/timer suites green.

…re WAL (mksglu#985)

closeDB()'s wal_checkpoint(TRUNCATE) is the only WAL-truncation path and only
runs on graceful shutdown. A server killed hard (crash, reboot, SIGKILL, or a
Windows parent-death before the lifecycle guard fires) never reaches it, so
under multi-session load the shared per-project content-store WAL grows
unbounded — the reader-starvation failure ADR 0001 attributes to mksglu#560.

Add startWalCheckpointTimer(db, intervalMs): an .unref()'d periodic PASSIVE
checkpoint. PASSIVE never blocks and adds no locking, so it stays within
ADR 0001's multi-writer contract (no EXCLUSIVE, no lockfile). The server starts
it for the content store (default 60s, CONTEXT_MODE_WAL_CHECKPOINT_MS tunes it,
0 disables); ContentStore.close()/cleanup() stop it. Adds unit coverage.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant