In SQLite's WAL (Write-Ahead Logging) mode, changes do not overwrite the main database file directly. Instead, transactions append new committed pages to a separate .wal file.
When a reader begins a transaction, it records a read-mark in the shared-memory WAL index (.shm file), pointing to the last valid commit frame at that exact moment.
That read-mark freezes the reader's view to a point-in-time snapshot. When reading a page, SQLite checks the WAL index to see if a newer frame exists up to that read-mark. If not found, it reads the page from the main database file.
Writers continue appending newer frames to the end of the log:
-- Writer appends frame 42, 43 to .wal
-- Active reader's read-mark remains fixed at frame 40
Writers never block readers because writers append to the end of the log file, while readers only scan frames up to their established read-mark.
Later comes checkpointing, which transfers committed WAL pages back into the main database file:
Checkpoint rule: SQLite cannot truncate or overwrite WAL frames beyond the oldest active reader's read-mark.
If any reader is still active on an older frame, earlier frames remain pinned in the WAL.
This design gives snapshot isolation without read locks, but a stalled reader prevents WAL truncation, causing the log file to grow on disk.