Run database maintenance under the database lock (closes #94) #97

Merged
clawbot merged 1 commits from issue-94-maintenance-lock into next 2026-10-19 13:26:37 +02:00
Collaborator

Vacuum, Analyze and Checkpoint now take Database.lock, as every batch write does, so maintenance and batch writes wait for each other instead of one failing with "database is locked" after SQLite's 5-second busy timeout (#94).

ANALYZE on 4 million routes (a 1.7 GB file, in the page cache) took about 4 s with no limit and 1.1 s with analysis_limit=1000. Even limited, it grows with the file: SQLite counts every index entry. So Analyze runs PRAGMA optimize=0x10002 with that limit, which analyzes only tables without statistics or whose size changed tenfold: about 1 ms on that file when nothing needed it, 1 to 2 s on the first run.

Not visible in the diff:

  • A wait on a sync.Mutex cannot be abandoned: a maintenance call whose context ends while it waits returns the context's error once it gets the lock, and releases it at once.
  • Checkpoints every 5 seconds cannot starve batch writes: Go's mutex serves goroutines waiting over 1 ms in arrival order. TestBatchWriteDuringCheckpoint now covers this.
  • The tests learn that an operation waits for or holds the lock from the lock's debug messages.

Disclosures:

  • Judgement call: PRAGMA optimize instead of an hourly ANALYZE of every table.
  • Removed PRAGMA analysis_limit=0 from Initialize: it set no limit.
  • Timings from the sqlite3 shell 3.53.3; the driver bundles 3.50.3.

Model: opus-5-5

`Vacuum`, `Analyze` and `Checkpoint` now take `Database.lock`, as every batch write does, so maintenance and batch writes wait for each other instead of one failing with "database is locked" after SQLite's 5-second busy timeout (https://git.eeqj.de/sneak/routewatch/issues/94). ANALYZE on 4 million routes (a 1.7 GB file, in the page cache) took about 4 s with no limit and 1.1 s with `analysis_limit=1000`. Even limited, it grows with the file: SQLite counts every index entry. So `Analyze` runs `PRAGMA optimize=0x10002` with that limit, which analyzes only tables without statistics or whose size changed tenfold: about 1 ms on that file when nothing needed it, 1 to 2 s on the first run. Not visible in the diff: - A wait on a `sync.Mutex` cannot be abandoned: a maintenance call whose context ends while it waits returns the context's error once it gets the lock, and releases it at once. - Checkpoints every 5 seconds cannot starve batch writes: Go's mutex serves goroutines waiting over 1 ms in arrival order. `TestBatchWriteDuringCheckpoint` now covers this. - The tests learn that an operation waits for or holds the lock from the lock's debug messages. Disclosures: - Judgement call: `PRAGMA optimize` instead of an hourly ANALYZE of every table. - Removed `PRAGMA analysis_limit=0` from `Initialize`: it set no limit. - Timings from the `sqlite3` shell 3.53.3; the driver bundles 3.50.3. Model: opus-5-5
clawbot added the needs-review label 2026-10-19 13:13:45 +02:00
clawbot self-assigned this 2026-10-19 13:13:45 +02:00
clawbot added 1 commit 2026-10-19 13:13:45 +02:00
Vacuum, Analyze and Checkpoint now take the database lock the batch
writes hold, so maintenance and batch writes wait for each other
instead of meeting at SQLite's write lock, where whichever waited past
the 5-second busy timeout failed with "database is locked". Analyze
runs ANALYZE through PRAGMA optimize, only on tables without statistics
or whose size changed tenfold, reading about 1000 rows of each index:
even limited, ANALYZE counts every index entry, so an hourly ANALYZE of
every table would hold the lock longer as the tables grow. The
analysis_limit=0 in Initialize, which set no limit, is gone.

Model: opus-5-5
Author
Collaborator

Review passed.

Model: opus-5-5

Review passed. Model: opus-5-5
clawbot merged commit 5806ddaef7 into next 2026-10-19 13:26:37 +02:00
clawbot deleted branch issue-94-maintenance-lock 2026-10-19 13:26:38 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/routewatch#97