8000 feat(storage): generation pins and page quarantine; fact logs no longer defer checkpoints (#543, PR 2 of 3) by adityamukho · Pull Request #579 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

Skip to content

feat(storage): generation pins and page quarantine; fact logs no longer defer checkpoints (#543, PR 2 of 3) - #579

Merged
adityamukho merged 2 commits into
v3from
feat/543-pins
Oct 13, 2026
Merged

adityamukho merged 2 commits into
v3from
feat/543-pins

Conversation

@adityamukho
@adityamukho 8000 adityamukho commented Oct 13, 2026
Copy link
Copy Markdown
Collaborator

Part of #543 (streaming engine 1/9, #473): PR 2 of 3, plan steps 2, 3, 4, 6, 7 (docs/superpowers/plans/2026-10-11-543-snapshots-and-quarantine.md). PR 1 was #578.

What changes

  • src/storage/pins.rs — Pins maps each generation to its open pins, and each generation g to the page ids its checkpoint freed (the quarantine entry). GenerationPin is RAII. An entry stays in force while some lower generation is pinned. It is dropped at publish if no lower generation is pinned, or later when the last such pin is released.
  • PageAllocator::with_quarantine — never hands out an id in force, free-list chain reads included. finish_free_list lists skipped ids again, so nothing leaks, and a crash or reopen (no pins) reuses them at once.
  • save() / rebuild_indexes() — allocate around pages_in_force(). Each records its freed pages in publish under FactData's write lock (save: freed; rebuild: the old referenced pages minus the new reached ones). The rebuild pool also leaves out ids in force.
  • FactStorage::snapshot() — takes the committed reader, its pin, and the pending store with its length under one FactData lock. Snapshot only has what the fact log needs for now; PR 3 (step 5) extends it for queries and cursors.
  • Fact log — reads from its Snapshot. Minigraf::fact_log no longer takes the write lock. Removed: LogPin, pin_log, log_pins, log_snapshot, the API-013 refusal in save/rebuild, and the auto-checkpoint deferral in execute and WriteTransaction::commit.
  • API-013 deprecated — still registered and documented, no longer emitted (same treatment as STG-003/004). Updated: ERROR_REFERENCE.md, DURABILITY.md, CHANGELOG, ROADMAP, CLAUDE.md, TEST_COVERAGE.

Behaviour change (v3-only, unreleased): checkpoints now run while a fact log is open. Instead of a growing WAL, the file grows by the pages checkpoints replace until the log closes; later checkpoints reuse those pages.

Tests (1458 total: 1448 passing, 10 ignored)

  • pins unit tests: entry in force under a lower pin, dropped at once with no pin, two pins released in either order.
  • Allocator: quarantined ids are never handed out, from a list or from the chain; they are listed again, and the count matches the chain.
  • PersistentFactStorage (page-id level): saves, rebuilds, and the two alternated never write a page the pinned generation reads. After each one the pinned reader returns the same facts, assert_space_accounted holds and verify() is clean. After the pin is dropped, the held pages are reused.
  • fact_log_test:
    • checkpoints, rebuilds and auto-checkpoints with a log open
    • file growth with no log, with a log held, and after close
    • logs opened while another thread writes and checkpoints see whole transactions only
    • an in-memory log opens while another thread holds a WriteTransaction
    • the close-time checkpoint runs while a log outlives its handle
  • Mutation check: with in_force() returning nothing, 3 fact-log tests and all 3 PFS pin tests fail.
  • snapshot_atomicity_test + fact_log_test looped 15×: no failures. Clippy is clean for native, wasm32-unknown-unknown (browser) and wasm32-wasip1.

Codex review

One finding: save() hands the browser listed, which includes quarantined ids, and the browser drops those pages from IndexedDB. It can't happen in this PR: the only pin taker is Minigraf::fact_log, and BrowserDb has no fact log. It becomes reachable when cursors pin in PR 3, whose step 8 (take_released withholds ids in force) fixes it. There's a comment at the spot. No other findings.

Not in this PR

PR 3 (feat/543-snapshots-read): query/cursor snapshots, BrowserDb withholding, crash test with an open snapshot, docs-site and #421 updates.

No public API change and no new error code, so no binding cascade.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HyLscCUuJ1GQpdAeX79EdR

adityamukho and others added 2 commits October 13, 2026 20:55
…er defer checkpoints (#543)

PR 2 of 3 for #543 (plan steps 2, 3, 4, 6, 7).

- src/storage/pins.rs: Pins (generation -> open pins, generation -> ids its
  checkpoint freed) and the RAII GenerationPin. An entry stays in force while
  a lower generation is pinned; it is dropped at publish or on release.
- PageAllocator::with_quarantine: ids in force are never handed out and are
  listed again by finish_free_list.
- save() and rebuild_indexes() allocate around pages_in_force() and record
  their freed pages in publish (under FactData's write lock).
- FactStorage::snapshot() takes the committed reader, its pin and the pending
  store with its length under one FactData lock. FactLog reads from it;
  fact_log() no longer takes the write lock. LogPin, pin_log, log_pins and
  log_snapshot are gone, and so is the API-013 refusal in save/rebuild and
  the auto-checkpoint deferral in execute and WriteTransaction::commit.
- API-013 deprecated: still registered, no longer emitted.

Tests: pins unit tests, allocator quarantine tests, PFS tests that check by
page id that saves and rebuilds never write a pinned generation's pages and
reuse them after the pin is dropped; fact log tests for checkpoints, rebuilds,
auto-checkpoints and concurrent writers with a log open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HyLscCUuJ1GQpdAeX79EdR
…andle (#543)

Also notes at the browser release point that quarantined ids must be
withheld once browser reads pin (plan step 8, Codex review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HyLscCUuJ1GQpdAeX79EdR
@codecov
codecov Bot commented Oct 13, 2026
Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.59091% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/fact_log.rs 83.33% 2 Missing ⚠️
src/storage/persistent_facts.rs 92.30% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@adityamukho
adityamukho merged commit 27951e3 into v3 Oct 13, 2026
23 of 24 checks passed
@adityamukho
adityamukho deleted the feat/543-pins branch October 13, 2026 15:46
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.

1 participant

0