Skip to content

fix(store): remove destination WAL/SHM sidecars before installing a fresh DB#1016

Merged
DeusData merged 2 commits into
mainfrom
fix/stale-wal-sidecars
Jul 11, 2026
Merged

fix(store): remove destination WAL/SHM sidecars before installing a fresh DB#1016
DeusData merged 2 commits into
mainfrom
fix/stale-wal-sidecars

Conversation

@DeusData

Copy link
Copy Markdown
Owner

Closes #897.

All three install paths from the report now clear the destination's -wal/-shm first via a shared cbm_remove_db_sidecars helper (compat_fs, _wunlink-backed on Windows): the pipeline page writer, artifact import (it cleaned the tmp file's sidecars, not the destination's), and cbm_store_dump_to_file — whose 'sidecars become stale and get recreated' comment the report correctly called out as wrong.

Reproduce-first (the issue's own recipe): hot-copy a live WAL aside, clean close, restore it as the crashed-session leftover, install generation 2 via dump_to_file, reopen. On unguarded code the replay clobbered the fresh generation entirely (the fresh row was unqueryable) — even nastier than the resurrected-stale-rows the report predicted. GREEN with the cleanup; the test asserts the hot WAL is non-empty so it can never pass vacuously.

Full suite 5,990/0, lint-ci clean. Related: #790 (stale sidecars making a project unqueryable after restart) is the same family but a different scenario (same-generation crash recovery, not fresh-install) — kept separate.

Thanks @kriswill for an unusually precise report — the repro recipe went into the test almost verbatim.

@DeusData DeusData added this to the 0.9.1-rc milestone Jul 11, 2026
@DeusData DeusData enabled auto-merge July 11, 2026 00:40
DeusData added 2 commits July 11, 2026 02:40
…resh DB

Three code paths install a brand-new DB file where a previous
generation lived without removing the destination's -wal/-shm:
the pipeline page writer (cbm_writer_open), artifact import (which
cleaned the TMP file's sidecars but not the destination's), and
cbm_store_dump_to_file (whose comment wrongly asserted stale sidecars
'become stale and get recreated'). SQLite validates a WAL against the
sidecar's own header/checksums — not the main file — so a crashed
session's leftover WAL is replayed ON TOP of the freshly installed
file at the next open, splicing old-generation pages into it.

New shared helper cbm_remove_db_sidecars (compat_fs, wide-path safe on
Windows) called at all three install points.

Reproduce-first: dump_install_ignores_stale_wal_sidecar follows the
issue's recipe (hot-copy a live WAL, clean close, restore the copy,
install generation 2, reopen). RED on the unguarded code — the replay
clobbered the fresh generation entirely (fresh row unqueryable);
GREEN with the cleanup. Non-vacuous: asserts the hot WAL is non-empty
before the copy.

Closes #897

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The new stale-sidecar guard exposed a pre-existing Windows gap in the
same code path: rename() fails with EEXIST when the destination
exists, so cbm_store_dump_to_file could never install over a previous
DB generation on Windows. New cbm_rename_replace uses
MoveFileExW(MOVEFILE_REPLACE_EXISTING) with wide paths (raw
MoveFileExA would re-mangle non-ASCII cache paths); POSIX keeps plain
atomic rename.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
@DeusData DeusData merged commit 1a38d0a into main Jul 11, 2026
21 checks passed
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.

Installing a fresh DB file leaves the old -wal/-shm behind — SQLite replays the stale WAL over the new file

1 participant