8000 feat: raw log writer — LogWriter (#431) by adityamukho · Pull Request #466 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

Skip to content

feat: raw log writer — LogWriter (#431) - #466

Merged
adityamukho merged 2 commits into
v3from
feat/431-log-writer
Oct 7, 2026
Merged

adityamukho merged 2 commits into
v3from
feat/431-log-writer

Conversation

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

Part of #431 (Rust API; exposing LogWriter and FactRecord in the bindings is #467).

What

LogWriter builds a new database from explicit FactRecords, keeping each record's tx_count, tx_id, valid-time window and assertion flag. It is the load step after fact_log() on a read-only source (#430, #429), so extract, transform, load is complete; a purge is "do not append".

let mut out = LogWriter::create("new.graph", OpenOptions::new())?;
for rec in src.fact_log(&FactFilter::new())? { let rec = rec?; if keep(&rec) { out.append(&rec)?; } }
out.advance_tx_count(src.current_tx_count())?;
out.finish()?;
  • Transaction order, holes allowed. A record opens a later transaction or joins the open one with the same tx_id. Leaving a transaction out leaves a hole; :as-of on it shows the state before. advance_tx_count closes the open transaction and keeps the source's counter after trailing purges, so the next write takes the source's next number.
  • Structural checks only. Order is the new API-015, a second tx_id in one transaction the new API-016, two windows for one fact in one transaction the existing API-011 (bug: a later assertion does not replace an earlier valid-time window of the same [e a v]; :as-of N should see exactly one window per triple #435), oversize values or idents WAL-003. A rejected record changes nothing and the writer stays usable. Exact repeats are written once, as in a normal write.
  • No WAL, batched checkpoints. Records go to the pending list and are committed with the normal copy-on-write checkpoint once 65,536 facts are pending and a new transaction opens, never splitting a transaction, and once more in finish. Memory is O(batch + largest transaction).
  • Built beside the target. The file is built at <path>.partial under the exclusive lock and renamed into place by finish (then the directory is synced). A crash or an abandoned build (dropping the writer deletes the .partial) never leaves part of a log at path. An existing target or <path>.wal is the new STG-043, at create and again before the rename. A stale .partial from a crashed build is truncated and reused; a live one is STG-025/026. read_only options are API-014.
  • No copy_pages. v8 trees share one dictionary and copy-on-write pages, so a page range is not self-contained (same conclusion as enhancement: streaming fact-log iterator with full history (tx, asserted, valid bounds) and coarse filters #430 decision 5).

Design: docs/superpowers/specs/2026-10-07-log-writer-design.md

Tests

1345 total (1336 passing, 9 ignored), up 16.

  • tests/log_writer_test.rs (13 tests):
    • A copy of a source's fact log (assertions, retractions, an explicit window, long strings, refs, keywords; checkpointed and pending) has no .partial or WAL, verifies clean, and matches the source's tx_count, fact log and queries (current, :valid-at, every :as-of, entity/attribute/value-bound lookups) read-write, read-only and after reopen (bug: after a kill during an active run the EAVT index silently loses most entities' entries while AEVT stays intact — entity-bound lookups return [] and no rebuild path exists #370). It takes new writes at the source's next number.
    • 90,000 facts over two batch commits verify clean and count correctly at :as-of.
    • A purged middle transaction is a hole equal to :as-of the one before; trailing holes keep the counter; an empty log gives an empty database with the counter.
    • API-015, API-016, API-011 and WAL-003 rejections each leave the writer usable and the output exact.
    • STG-043 for an existing target or WAL, STG-025 for a second writer, API-014 for read_only; a stale .partial is reused; dropping leaves nothing; finish refuses a target created mid-build and leaves it untouched; LogWriter: Send.
  • Unit tests: a batch commits only when a transaction opens past the batch size (one transaction is never split), and FileBackend::truncate empties a file.

Docs updated: CHANGELOG, ROADMAP, README, CLAUDE.md, TEST_COVERAGE, ERROR_REFERENCE (STG-043, API-015, API-016, plus notes on API-011 and WAL-003). The wiki is left for the v3.0.0 doc sync, as with the earlier v3 PRs.

🤖 Generated with Claude Code

https://claude.ai/code/session_015GgLEs8YozJjYhwPEYgTUn

adityamukho and others added 2 commits October 7, 2026 10:13
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015GgLEs8YozJjYhwPEYgTUn
Builds a new database from explicit FactRecords, keeping each record's
tx_count, tx_id, valid-time window and assertion flag: the load step of
an offline migration after fact_log() on a read-only source.

Records come in transaction order with gaps allowed, so a purged
transaction stays a hole and :as-of on it shows the state before.
advance_tx_count keeps the source's counter after trailing purges.
Checks are structural: order (new API-015), one tx_id per transaction
(new API-016), one valid-time window per fact per transaction (API-011)
and size (WAL-003). A rejected record changes nothing.

No WAL: records are committed in batches (65,536 facts) with the normal
copy-on-write checkpoint, at transaction boundaries only. The file is
built at <path>.partial and renamed into place by finish(), so a crash
or an abandoned build never leaves part of a log at the target; an
existing target or WAL is the new STG-043. No page-range copy: v8 pages
share one dictionary and are not self-contained.

Bindings follow in the binding repositories.

Design: docs/superpowers/specs/2026-10-07-log-writer-design.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015GgLEs8YozJjYhwPEYgTUn
@adityamukho
adityamukho merged commit 97c22cb into v3 Oct 7, 2026
18 checks passed
@adityamukho
adityamukho deleted the feat/431-log-writer branch October 7, 2026 05:06
@codecov
codecov Bot commented Oct 7, 2026
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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