Skip to content

fix: bump the file change counter and version-valid-for on commit (#710) - #722

Open
dpsiderius wants to merge 1 commit into
mainfrom
fix/710-change-counter
Open

dpsiderius wants to merge 1 commit into
mainfrom
fix/710-change-counter

Conversation

@dpsiderius

Copy link
Copy Markdown
Contributor

What

Header offset 24 (the file change counter) is now incremented, and offset
92 (version-valid-for) mirrored to the same value, on every commit that
writes page 1. Before this, both fields were preserved rather than
zeroed/bumped, so a long-lived stock sqlite3 reader had no signal that
our write happened and kept serving its cached page image — no error,
no corruption, just stale answers.

Fixed a real bug found while implementing this: a retried flush() after
lock contention would double-bump the counter, because a dirty page 1
survives the failed attempt. Guarded with a change_counter_bumped flag
reset at every dirty-clear site.

Why this needed a new test harness

Proving it requires a long-lived oracle process spanning our write — every
existing corpus test invokes sqlite3 fresh per assertion, which can't
observe a cache going stale. Added a reusable persistent-session helper
(OracleSession) to tests/corpus/oracle.rs.

Before/after

  • Long-lived oracle session reads a table (populating its page cache), our
    engine writes a row, the same session reads again: before, it returns
    the stale row count; after, it sees the new row.
  • Raw header bytes at offsets 24 and 92 match the oracle's for the same
    write sequence, on both a freshly-created database and one we mutate.

Test plan

  • tests/corpus/change_counter_test.rs — the scenario above, byte-exact
    header match vs. oracle, fresh-database match vs. oracle.
  • 3 new pager unit tests for the double-bump guard.
  • make test, make lint, make test-corpus, make check-mvl-limit, make check-mod-files, make assurance all pass.

Refs: #193, #705
Closes #710

spend: matched estimate

Pager::flush preserved header offset 24 (change counter) and offset 92
(version-valid-for) across every commit instead of incrementing them, so
a long-lived sqlite3 reader that had already cached page 1 kept serving
stale rows after our write with no error and integrity_check ok. flush
now bumps both fields exactly once per committed transaction — guarded
against double-bumping when a flush is retried after lock contention —
wrapping past u32::MAX rather than erroring, matching stock sqlite3.

Proven with a persistent sqlite3 session (tests/corpus/oracle.rs's new
OracleSession, reusable for #706) that reads before and after our write,
plus byte-exact comparison of both header fields against the oracle for
matching write sequences and for a freshly created database.

spend: matched estimate (small)
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.

bug: the file change counter (header offset 24) is never incremented — a long-lived sqlite3 reader keeps serving a stale cache

1 participant