8000 feat(storage): statement bases and large write transactions (#550 PR 3) by adityamukho · Pull Request #692 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

Skip to content

feat(storage): statement bases and large write transactions (#550 PR 3) - #692

Merged
adityamukho merged 3 commits into
v3from
feat/550-statement-bases
Oct 23, 2026
Merged

adityamukho merged 3 commits into
v3from
feat/550-statement-bases

Conversation

@adityamukho
@adityamukho adityamukho commented Oct 22, 2026 •
Copy link
Copy Markdown
Collaborator

#550 PR 3 of 3 (part of #473): statement bases, large WriteTransactions, snapshot reads, the chunk-size measurement, docs. Plan: PR 3 section of docs/superpowers/plans/2026-10-21-550-streaming-transactions.md (decisions 25–36, deviations 1–8, results).

What changes

  • Large WriteTransactions. On a file database, the statement that takes a transaction's staged facts past TX_CHUNK_FACTS (or a larger statement) makes it large. The staged statements are reduced by keep_last_statement, checked at the transaction's tx_id first (a failure rejects the statement and leaves the transaction small, nothing to undo), then written as one chunk. Commit / rollback / drop go through commit_large / rollback_large.
  • Statement bases (spec §5). Each statement records roots, counters, fact_count, freed length and the ids handed out since (PageAllocator::begin_base). A rejected statement returns to its base; the transaction stays usable. An earlier statement's keys of a triple the statement writes are deleted from all four index trees (cow_update deletes).
  • Claims tree (deviation 3). Base membership alone cannot tell an earlier statement's key from the same key re-written by this statement in an earlier chunk, so a second window of that fact would slip past API-011. Each statement keeps a copy-on-write tree of the triples whose base keys it deleted; its pages are released when the statement ends.
  • A failed statement truncates freed (deviation 4). Pages of g it replaced are referenced again by the restored roots.
  • Pending store written at the switch (deviation 2), so queries inside a large transaction read the working trees alone. The engine's shadow rule and the executor's net-assert both assume committed is older than pending.
  • Reads (spec §7). Every WriteTransaction query reads a Snapshot: staged facts are merged with the pending ones by index order (PendingView), or a large transaction's working-tree OnDiskReader. merged_query_facts, facts_override and read_now_floor are gone; these queries run on the engine (oracle under test) and count toward query_memory_limit.
  • A large transaction in which no statement wrote a fact commits nothing and takes no tx_count (deviation 7, found by the model-based test).

Measurement → OpenOptions::transaction_chunk_facts, default 65,536

1M facts, idle bench machine, opt-level = 3, v3 before #550 (d24fb585) → 4,096 / 16,384 / 65,536. Full table in the plan.

Shape v3 before 4,096 16,384 65,536
execute, sequential, short 9.1 s, 1,312 MiB 4.9 s, 0 4.2 s, 0 3.9 s, 0
execute, random, short 11.4 s, 1,293 MiB 36.4 s, 0 18.5 s, 0 9.5 s, 0
10 stmts, random, short 12.0 s, 1,372 MiB 38.2 s, 0 22.5 s, 7 10.1 s, 41
10 stmts, random, long 22.3 s, 2,402 MiB 51.3 s, 0 41.3 s, 0 34.0 s, 54

RSS is the rise above the input text's peak. The chunk size is now an open option, OpenOptions::transaction_chunk_facts (kept between 1 and 1,000,000, the WAL entry limit), defaulting to 65,536 for speed. Two costs: random entities are slower than before (spec §8.2), and a multi-statement transaction over random entities can leave the file up to 1.8× larger (held base pages). The space is reused by later checkpoints. 16,384 holds 40–55 MiB less and takes up to twice as long with random entities.

Tests

1767 tests (1753 passing, 14 ignored). test-oracle: 1767 passing, 0 failed. New: 9 large_tx tests (incl. the open option) (WriteTx differential at chunk sizes 1–4 with API-011 cases incl. the claims case, in-tx reads against the committed result of every statement prefix, g-page restore, rollback, empty commit, step-5 API-019, STG-046 at the switch and at commit), 4 fault-matrix ops, base-id and tree_pages unit tests. Crash-kill Large variant runs every third transaction as a 3-statement WriteTransaction (180 rounds locally). Mutating the claims check, the freed truncation, or the held/claims release fails tests. Codex review: no findings.

Docs: CHANGELOG.md, docs/TEST_COVERAGE.md, CLAUDE.md; docs site PR to follow.

Not here: #686, #687, #551. No closing keywords: #550 is closed by hand after merge; #473 stays open.

🤖 Generated with Claude Code

https://claude.ai/code/session_019kY61Y11gcRD91Y6b7jbGT

adityamukho and others added 2 commits October 22, 2026 22:10
Decisions 25-36, deviations, steps 16-23 and the chunk-size
measurement results, checked against v3 at 75a3fac.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019kY61Y11gcRD91Y6b7jbGT
A WriteTransaction on a file database becomes large when its staged
facts would pass TX_CHUNK_FACTS: the staged statements are checked at
its tx_id, then written as one chunk, and every later statement writes
its chunks under a statement base (roots, counters, freed length, ids
handed out since). A rejected statement returns to its base and the
transaction stays usable. An earlier statement's keys of a triple a
statement writes are deleted, and the triple joins the statement's
claims tree, so a repeat of an earlier fact does not hide a second
window of it (plan decision 30). A failed statement truncates freed.

The pending store is written when a transaction becomes large, so
queries inside it read the working trees alone. Every WriteTransaction
query now reads a Snapshot (staged facts merged with the pending ones
by index order) and runs on the engine; merged_query_facts and the
executor's facts_override are gone.

TX_CHUNK_FACTS stays 16,384 after measurement. Fault matrix and
crash-kill cover large WriteTransactions.

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

Codecov Report

❌ Patch coverage is 90.88398% with 33 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/db.rs 85.71% 17 Missing ⚠️
src/storage/persistent_facts/large_tx.rs 89.31% 14 Missing ⚠️
src/graph/storage.rs 98.61% 1 Missing ⚠️
src/storage/btree.rs 92.85% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

The chunk size of large transactions is an open option, default 65,536
for speed: at 1M facts with random entities 16,384 took up to twice as
long for 40-55 MiB less. Kept between 1 and 1,000,000, the most facts
WAL replay reads from one entry, since a small transaction is one entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019kY61Y11gcRD91Y6b7jbGT
@adityamukho
adityamukho merged commit 0535212 into v3 Oct 23, 2026
24 of 25 checks passed
@adityamukho
adityamukho deleted the feat/550-statement-bases branch October 23, 2026 02:53
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