8000 fix(storage): add a committed transaction's facts under one lock (#554) by adityamukho · Pull Request #556 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

Skip to content

fix(storage): add a committed transaction's facts under one lock (#554) - #556

Merged
adityamukho merged 3 commits into
v3from
fix/554-atomic-commit
Oct 11, 2026
Merged

adityamukho merged 3 commits into
v3from
fix/554-atomic-commit

Conversation

@adityamukho
Copy link
Copy Markdown
Collaborator

Refs #554 (data-integrity: stays open until a release ships the fix).

Bug

A commit wrote its WAL entry, then added the transaction's facts to FactStorage one load_fact call at a time, each under its own write lock. A query on another thread could run between two calls and see part of the transaction.

The issue said execute("(transact ...)") was atomic. It is not: Minigraf::execute's transact/retract path has added facts one at a time since v0.6.0 (21b555a, WAL-first ordering); WriteTransaction::commit since v0.5.0. transact_batch is only used below the facade.

Fix

FactStorage::load_facts appends a batch under one write lock, with the same duplicate skip as load_fact. Used by:

  • Minigraf::execute transact/retract
  • WriteTransaction::commit
  • WAL replay at open
  • BrowserDb commit
  • the v7 in-memory load

LogWriter still loads one fact at a time into its own private store, which no query reads.

Tests

  • tests/snapshot_atomicity_test.rs (reproducers from test/snapshot-atomicity-repro): the two WriteTransaction reproducers are un-ignored, and execute variants (in-memory, file) are added. All four failed within milliseconds before the fix and pass after. RUN_FOR is now 3 s.
  • The bug: patterns of one query can read different database states under concurrent writes #555 reproducers and the checkpoint double-read test stay ignored.
  • Unit test load_facts_appends_a_batch_and_skips_duplicates.
  • 1427 passing, 13 ignored. fmt and clippy clean; the wasm32 browser build checks.

Codex review: no findings.

A v2.x backport to main follows.

🤖 Generated with Claude Code

https://claude.ai/code/session_012wXxZguv7PgKQjMMH4yJ5h

adityamukho and others added 3 commits October 11, 2026 22:49
Ignored: they fail on v3 and main today. A concurrent query sees part of a
WriteTransaction commit, and two patterns of one query can read different
states. The checkpoint double-read case did not reproduce.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wXxZguv7PgKQjMMH4yJ5h
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wXxZguv7PgKQjMMH4yJ5h
A commit wrote its WAL entry, then added the facts to FactStorage one
load_fact call at a time, each under its own write lock, so a query on
another thread could see part of the transaction. execute's transact and
retract path (since v0.6.0, 21b555a) did this as well as
WriteTransaction::commit (since v0.5.0).

FactStorage::load_facts appends a batch under one write lock. Commits,
WAL replay, BrowserDb's commit and the v7 in-memory load use it.

The two #554 reproducers are un-ignored, with execute variants added.

Refs #554

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

Codecov Report

❌ Patch coverage is 88.23529% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/graph/storage.rs 84.61% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@adityamukho
adityamukho merged commit 2d799e3 into v3 Oct 11, 2026
24 checks passed
@adityamukho
adityamukho deleted the fix/554-atomic-commit branch October 11, 2026 18:22
q-qp-p pushed a commit to q-qp-p/minigraf that referenced this pull request Oct 19, 2026
…minigraf#554, project-minigraf#555)

Backport of the v3 fixes (PRs project-minigraf#556, project-minigraf#557) to v2.x.

project-minigraf#554: commits added a transaction's facts to FactStorage one load_fact
call at a time, each under its own write lock, so a query on another
thread could see part of the transaction. FactStorage::load_facts
appends a batch under one lock; execute, WriteTransaction::commit, WAL
replay and BrowserDb's commit use it.

project-minigraf#555: selective_fact_fetch read each entity/attribute lookup under its
own read lock, so a commit between two lookups was seen by some
patterns and not others. The three lookup bodies become functions over
&FactData and FactStorage::get_facts_for_lookups runs them all under one
read guard. The old methods remain for tests only.

The file-backed project-minigraf#555 reproducer disables checkpoints: on v2.x a query
that runs during a checkpoint can lose committed facts, a separate bug.

Refs project-minigraf#554, project-minigraf#555

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wXxZguv7PgKQjMMH4yJ5h
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