Skip to content

fix(storage): write index nodes append-only so older snapshots survive compaction - #34

Merged
tiensonqin merged 1 commit into
mainfrom
devin/append-only-storage-nodes
Oct 4, 2026
Merged

tiensonqin merged 1 commit into
mainfrom
devin/append-only-storage-nodes

Conversation

@tiensonqin

Copy link
Copy Markdown
Contributor

Summary

Index node writes used to reuse each node's previous storage address (store_node ?address), so a stored snapshot that still held deferred refs to an address would silently resolve to post-flush content after the tail-compaction store inside transact_conn/apply_report. Concretely, report.db_before appeared to already contain the tx's own datoms — before = after for any consumer reading the pre-transaction snapshot (e.g. the Logseq db-worker's render_delta membership-at, which produced empty :children patches so inserted blocks never rendered).

buffered_node_storage and restoring_node_storage now ignore the inherited address and always allocate a fresh one — index writes are append-only. Write volume per flush is unchanged (same dirty nodes written, just at new addresses); superseded addresses become garbage and are reclaimed by collect_garbage, matching the cljs fork's stored_dbs/GC model rather than its in-place reuse. The reuse in cljs latentlly corrupts snapshots the same way; logseq's cljs fork has the same overwrite-then-notify ordering in StorageAdapter.store/conn._transact_BANG_.

Includes a regression test test_db_before_snapshot_survives_compaction_store: restore a conn from storage, transact past the branching-factor compaction threshold, and assert report.db_before and a captured snapshot still read exactly the pre-tx datoms (was expected 200, got 260 before the fix).

Link to Devin session: https://app.devin.ai/sessions/f1e2f055a0e14c7abf889688d09c8371
Open in Devin Desktop: https://app.devin.ai/desktop/session/f1e2f055a0e14c7abf889688d09c8371?variant=devin
Requested by: @tiensonqin

…e compaction

Index node writes reused previous storage addresses, so a stored snapshot
holding deferred refs to those addresses silently observed post-flush
content (e.g. report.db_before appeared to already contain the tx's own
datoms after the compaction flush inside transact_conn/apply_report,
making before = after for downstream delta computation).

store_node now always allocates a fresh address in both
buffered_node_storage and restoring_node_storage; obsolete addresses are
reclaimed by collect_garbage. Adds a regression test that restores a
conn and forces a compaction flush, then asserts the pre-transaction
snapshot still reads its original datoms.
@devin-ai-integration

Copy link
Copy Markdown

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@tiensonqin
tiensonqin merged commit 3d6989d into main Oct 4, 2026
2 checks passed
devin-ai-integration Bot pushed a commit to logseq/logseq that referenced this pull request Oct 4, 2026
datascript-ocaml 3d6989d (logseq/datascript-ocaml#34): store_node no
longer overwrites in-use addresses, so deferred refs in older snapshots
(db_before) survive compaction — fixes empty :children patches after
insertBatchBlock.
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