8000 save() is not crash-atomic: new fact pages overwrite old index pages before header commit · Issue #374 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

Skip to content

save() is not crash-atomic: new fact pages overwrite old index pages before header commit #374

Closed

Description

@adityamukho
8000

Summary

Split out of #370. PersistentFactStorage::save() is not crash-atomic: it writes new fact pages at 1 + old_fact_page_count, which is where the old index pages live, and rebuilds the new indexes in place, all before writing the header. A kill anywhere between the first page write and the header write leaves the old header pointing at overwritten index pages.

Current behaviour

Ask

Make save() crash-safe without a second file: e.g. write the new indexes to fresh pages beyond the old ones (or a shadow region) and only then flip the header, so the old header always references an intact, consistent tree. Evaluate interaction with page reuse/free-list, file growth, and #315 (incremental index update).

Philosophy check

Aligned: reliability over features, single-file. Must not change the file format in a breaking way (v2.x stability); if it needs a format change it belongs in v3.0.0 (v8).

Refs #370

Activity

  1. added this to the v3.0.0 milestone on Sep 25, 2026
  2. adityamukho commented on Sep 26, 2026

    @adityamukho
    CollaboratorAuthor

    Scope note from #315 (checkpoint cost, mitigated on v2.x in #402):

    On v7 every checkpoint must relocate the index. New fact pages must stay contiguous at 1..=fact_page_count, so they always overwrite the start of the old index region. #402 cut the per-checkpoint cost by roughly 6–10×: untouched leaves are copied raw, only the leaves that receive new entries are re-encoded, and unchanged fact pages are no longer re-hashed. But checkpoint cost still grows with graph size, because every index page is copied on every checkpoint.

    The fix for this issue removes that remaining term. With copy-on-write B+tree pages and a free list, save() can write only the changed leaves and their root paths, then commit by swapping the header. That makes the save crash-atomic and makes checkpoint cost O(delta · log n) instead of O(index size). Whoever designs the v8/v3 layout here should treat O(delta) checkpoints as part of the requirement.

    Two pieces of #402 carry over:

    • rebuild_btree_incremental's routing and balanced-split logic, which avoids leaf fragmentation.
    • The cached fact-page CRC prefix.

    A full-file CRC over rewritten pages is itself O(file) and would need rethinking, for example per-page checksums.

  3. added
    known-issueBug present in a published release and not yet fixed in one; listed in the pinned known-issues issue
    on Sep 27, 2026
  4. added 2 commits that reference this issue on Oct 5, 2026
  5. adityamukho commented on Oct 6, 2026

    @adityamukho
    CollaboratorAuthor

    Fixed on v3 for v3.0.0 by the v8 storage format (#444 crash-atomic save with alternating meta pages; #451 copy-on-write checkpoints). A crash or torn write at any point of a checkpoint leaves the previous checkpoint intact, and open never rebuilds an index. v2.x is still affected and stays listed in #421 until v3.0.0 ships.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdata-integrityData integrity issuedurabilityData durability concernknown-issueBug present in a published release and not yet fixed in one; listed in the pinned known-issues issue

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0