8000 bug: a query during a checkpoint can miss committed facts (v2.x) · Issue #558 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

Skip to content

bug: a query during a checkpoint can miss committed facts (v2.x) #558

Closed

Description

@adityamukho

Affected versions: v0.9.0 (ee21bfb, on-disk B+tree indexes) through v2.0.5. v3 (format v8, copy-on-write checkpoints) is not affected: the reproducer below passes there.

Bug

On a file-backed database, a query that runs on another thread while a checkpoint is in progress can return wrong results. It can miss facts that were committed before it started, and it can see one fact of a transaction without the others.

PersistentFactStorage::save (src/storage/persistent_facts.rs on main) writes the new fact pages at 1 + old_fact_page_count, the same pages the previous checkpoint's B+tree index occupies. Queries still read through the old index reader during that time: they hold FactStorage's read lock, and save does not take it. The new state is then published in three separately locked steps (set_committed_reader, set_committed_index_reader, post_checkpoint_clear), so a query can also combine a new fact reader with the old index, or the new committed facts with the pending facts that are about to be cleared.

Stored data is not affected: the file written by the checkpoint is correct (single-threaded queries after the checkpoint are right). Only queries that run during a checkpoint are affected. Every checkpoint counts, including the automatic one at wal_checkpoint_threshold (default 1,000 WAL entries) and the one at close.

Reproducer

A writer thread commits [:pN :pair/x N] [:pN :pair/y N] with execute, then calls checkpoint(). A reader runs [:find ?e :where [?e :pair/y ?v] (not [?e :pair/x ?v])], which must be empty, and [:find ?e :where [?e :pair/x ?v]], which must not return fewer entities than were committed before the query began. On main, with the #554 and #555 fixes applied (PR #559), it fails within 30 ms:

43 entities with y and no x
1 x facts, 43 committed before the query

It passes on v3 for the whole run. Without checkpoints (wal_checkpoint_threshold(usize::MAX)) it passes on main.

Fix (v2.x)

save takes FactStorage's write lock before it overwrites any page that the published readers use, and holds it until the new readers are published and the pending facts cleared. Queries wait for a checkpoint to finish instead of reading one that is half-written. Lock order must stay data lock → backend mutex, as on the query path.

Workaround

Do not query from another thread while a checkpoint can run: call checkpoint() yourself while no query runs, and set wal_checkpoint_threshold(usize::MAX) to prevent automatic checkpoints. Note that usize::MAX also skips the checkpoint at close (#476), so call checkpoint() before closing.

Activity

  1. added
    bugSomething isn't working
    known-issueBug present in a published release and not yet fixed in one; listed in the pinned known-issues issue
    on Oct 11, 2026
  2. added 2 commits that reference this issue on Oct 11, 2026
  3. adityamukho commented on Oct 14, 2026

    @adityamukho
    CollaboratorAuthor

    Fixed in v2.0.6 (released 2026-10-14, on crates.io). Moved to "Fixed in v2.0.6" in #421.

  4. added
    known-issueBug present in a published release and not yet fixed in one; listed in the pinned known-issues issue
    and removed
    known-issueBug present in a published release and not yet fixed in one; listed in the pinned known-issues issue
    on Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdata-integrityData integrity issueknown-issueBug present in a published release and not yet fixed in one; listed in the pinned known-issues issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0