8000 test: fault-injection matrix through save/WAL/recovery; never retry a failed checkpoint (#390) by adityamukho · Pull Request #515 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

Skip to content

test: fault-injection matrix through save/WAL/recovery; never retry a failed checkpoint (#390) - #515

Merged
adityamukho merged 2 commits into
v3from
test/390-fault-matrix
Oct 10, 2026
Merged

adityamukho merged 2 commits into
v3from
test/390-fault-matrix

Conversation

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

Refs #390 (durability label: stays open until released). Builds on #514 (merged).

What

Fault plan (src/storage/fault.rs, test builds only). A thread-local plan is consulted before every write and sync of FileBackend, the WAL (header, entries, torn-tail cut, removal) and sync_parent_dir. It fails the k-th write or sync with:

  • EIO
  • a short/torn write (1, 40, 4,095 bytes)
  • ENOSPC that keeps failing later writes
  • a failed fsync
  • a failed fsync that also loses the writes since the last good one (fsyncgate: FileBackend keeps pre-images while armed; the WAL is cut back to its last synced length)

Matrix (src/fault_matrix.rs). Each fault runs at every call index of: transact, a WAL-creating transact, auto-checkpoint, checkpoint(), the close-time checkpoint, and rebuild_indexes(). The database is committed over two checkpoints (multi-level trees, a free list, multi-valued/retracted/long values) with entries pending in the WAL. After each fault:

  • the handle holds the old facts, or those plus the whole transaction, never part of one
  • after a failed checkpoint, another is STG-046
  • a later write succeeds or is WAL-007/STG-046
  • kill and reopen: exactly the handle's facts, plus the failed transaction whole or not at all, and verify is clean
  • a commit after recovery survives another kill

Plus: the disk fills halfway through a checkpoint; and a retried checkpoint after a failed meta fsync, crashing at each of its writes. Runs in about 17 s (debug).

Decision: a failed checkpoint is never retried on the same handle

PersistentFactStorage sets failed before a commit's first write and clears it on success. While it is set:

  • save/rebuild_indexes return the new STG-046
  • transact/commit are refused before they take a tx_count
  • the close-time save is skipped
  • queries keep working

Reopen recovers everything: it is all in the WAL. A rebuild refused with STG-041 writes nothing and keeps the handle usable. Documented in ERROR_REFERENCE.md (STG-046), the Minigraf::checkpoint rustdoc and CHANGELOG.

Why it matters: with the refusal disabled, no_commit_after_a_failed_meta_fsync reopens to STG-029. The meta page of the "failed" checkpoint reached the file, and the retry overwrote pages it references.

Mutation checks

Docs: CHANGELOG, ERROR_REFERENCE, TEST_COVERAGE, CLAUDE.md module list, test counts (1,406). The docs-site note is in minigraf-docs#4.

🤖 Generated with Claude Code

https://claude.ai/code/session_012wXxZguv7PgKQjMMH4yJ5h

adityamukho and others added 2 commits October 10, 2026 15:24
…try a failed checkpoint (#390)

A test-only, thread-local fault plan (src/storage/fault.rs) is asked
before every write and sync of FileBackend, the WAL (header, entries,
torn-tail cut, removal) and sync_parent_dir. It fails the k-th write or
sync with EIO, a short or torn write, a full disk that keeps failing
later writes, a failed fsync, or a failed fsync that also loses the
writes since the last good one (FileBackend keeps pre-images while a
plan is armed; the WAL is cut back to its last synced length).

src/fault_matrix.rs runs each fault at every call index of a transact,
a WAL-creating transact, an automatic checkpoint, checkpoint(), the
close-time checkpoint and rebuild_indexes(), then kills the handle and
reopens: the facts are exactly the handle's, plus the failed transaction
whole or not at all; verify is clean; a commit after recovery survives
another kill. Two more tests fill the disk halfway through a checkpoint,
and retry a checkpoint whose meta fsync failed, crashing at each write.

Decision (written down in ERROR_REFERENCE and Minigraf::checkpoint): a
checkpoint that failed after it may have written is never retried on the
same handle. PersistentFactStorage sets `failed` before its first write
and clears it on success; while set, save and rebuild_indexes return the
new STG-046, writes are refused before they take a tx_count, and the
close-time save is skipped. Without this, the retry test reopens to
STG-029: the retried checkpoint overwrote pages the on-disk meta of the
"failed" one referenced. A rebuild refused with STG-041 writes nothing and
does not set it.

The #513 test hook (tear_next_append) is replaced by the fault plan.

Refs #390

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wXxZguv7PgKQjMMH4yJ5h
…n open file (#390)

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

Codecov Report

❌ Patch coverage is 96.29630% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/storage/backend/file.rs 95.45% 1 Missing ⚠️
src/storage/persistent_facts.rs 93.75% 1 Missing ⚠️
src/wal.rs 96.55% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@adityamukho
adityamukho merged commit 827f6fa into v3 Oct 10, 2026
24 checks passed
@adityamukho
adityamukho deleted the test/390-fault-matrix branch October 10, 2026 10:47
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