Repository navigation
perf(cache): use synchronous=NORMAL in WAL mode - #871
Open
magurotuna wants to merge 6 commits into
Open
magurotuna wants to merge 6 commits into
magurotuna wants to merge 6 commits into
Conversation
The cache database runs in WAL mode with SQLite's default `synchronous=FULL`, so every commit waits for a sync of the WAL. Saving a task's result commits twice (the cache entry and the task fingerprint), so each task that misses the cache waits for two syncs before the run moves on. Set `synchronous=NORMAL` when WAL mode is on. In WAL mode this never corrupts the database: a power loss or an OS crash can only roll back the most recent commits, which become cache misses. The WAL is still synced at checkpoints, including the one when the last connection closes. If SQLite can't use WAL (for example, on a network share on macOS), `synchronous` stays `FULL`, because `NORMAL` with a rollback journal can corrupt the database after a power loss. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Every cache commit in
vp runwaits for an fsync of the WAL. The cache opens the database in WAL mode but leaves SQLite's defaultsynchronous=FULL, and saving one task's result commits twice: once forcache_entriesand once fortask_fingerprints. Each cache miss therefore adds two disk syncs before the task counts as finished, so the tasks that depend on it start later.Each run in the chart starts right after
vt cache clean, so every task misses the cache and saves its result. The two workspaces are synthetic pnpm workspaces. Each package has one cached task,build, which is a Node.js script that joins five one-line files intodist/out.js. The 3 packages form a chain (app→lib→utils). Each of the 100 packages depends on 0–3 other packages, chosen by a seeded random generator.Notice that macOS gains almost nothing with this change. This is because a sync there is already fast; SQLite calls
fsync(), and on macOS,fsync()moves the data to the drive, but the drive can keep it in its own cache and write it later (fsync(2)).I haven't measured Windows.
Changes
enable_walfunction, which runsPRAGMA journal_mode=WALand checks the returned mode. It setssynchronous=NORMALonly if the result iswal. WithNORMAL, commits no longer sync; SQLite syncs the WAL at checkpoints, including the checkpoint when the last connection closes.Safety
synchronous=NORMALcan't corrupt the database after a power loss or an OS crash, but it can roll back the most recent commits (SQLite docs). For this cache, a rolled-back commit means a cache miss on the next run, so the task runs again. The cache exists only to save time, so this is acceptable.vp runfirst deletes the old entry's output archive. If the new entry is rolled back, the old entry points to the deleted archive, and the run fails with "Cache restore failed. Runvp cache cleanto clear the cache". This can already happen onmain, because nothing syncs the archives.PRAGMA journal_mode=WALdoes not report an error when SQLite cannot use WAL. It keeps the current mode, usually a rollback journal, and returns that mode's name. With a rollback journal,NORMALhas a small chance of corrupting the database after a power loss, sosynchronousstaysFULLunless the result iswal.synchronousin the file. I checked thatmainand this PR can use the cache entries that the other wrote.