getnobody.org / labs

SQLite WAL checkpoint starvation

Everything behind the post Why your SQLite WAL file never shrinks.

Files

Standard library only. Needs Python 3 with its built-in sqlite3 module.

Scenarios

What runs next to the 2,000 commits/s writer
anothing
b2 readers, 0.4 s snapshot every 1.0 s (about 20% of the time nobody reads)
c3 readers, 1.0 s snapshot, 0.2 s idle, staggered (at least 2 always open)
dc + PRAGMA wal_checkpoint(TRUNCATE) every 5 s, 5 s busy timeout
ec + PRAGMA journal_size_limit 4 MiB
fb + PRAGMA journal_size_limit 4 MiB
gc with wal_autocheckpoint=0 on the writer; the once-a-second PASSIVE probe is then the only checkpointer

Raw results

Run it

# print the SQLite version your Python links
python3 -c 'import sqlite3; print(sqlite3.sqlite_version)'

# full matrix: 7 scenarios x 3 runs x 30 s, about 12 minutes
python3 run.py

# a quick look at two scenarios
python3 run.py --only c,d --runs 1 --duration 10

# turn off the once-a-second PASSIVE probe
python3 run.py --probe-every 0

# where the databases live (default: the OS temp dir)
python3 run.py --workdir /path/on/another/disk

Results go to results/ next to the script and overwrite the files above. Scenario c writes a WAL of about 360 MiB per 30 s run; databases are deleted after each run, and a run stops early if the WAL passes --max-wal-gb (default 20).

Tested on an Apple M5 Pro (18 cores, 48 GiB), macOS 26.6.2, APFS on the internal SSD, Python 3.14.4 with SQLite 3.53.2. Source links in the post are pinned to version-3.53.2.