Skip to content

SnapshotWriter::open: cut a torn tail off before appending - #33

Merged
jaredLunde merged 1 commit into
mainfrom
jared/writer-open-truncates-torn-tail
Oct 21, 2026
Merged

jaredLunde merged 1 commit into
mainfrom
jared/writer-open-truncates-torn-tail

Conversation

@jaredLunde

Copy link
Copy Markdown
Contributor

Why

A short write (the disk ran out at a block boundary, then a drop whose flush failed) leaves part of a record at the end of the log. SnapshotWriter::open appended behind it whenever no load ran first. From then on the log was corrupt mid-file for good: every load and every compact failed with Corrupted.

This happened in production-shaped use. On the beyond homelab, instd's VXLAN network-sync.snap has a 6-byte and a 5-byte torn cursor record just before a 4 KiB block boundary (offset 139253, boundary 0x22000), followed by complete records. instd has logged "failed to compact snapshot: snapshot corrupted (CRC mismatch)" every 30 s ever since, and the file never compacts.

What

SnapshotWriter::open now validates the log before appending:

  • it truncates a torn tail, using the same tail rules as replay_log; a truncate needs no free space, unlike load's rewrite;
  • it replaces a torn header instead of appending after it;
  • it refuses mid-file corruption with Corrupted, as load does.

The ARCHITECTURE format section is updated, and the version is bumped to 0.8.6.

Proof

  • New tests: open_after_a_torn_tail_never_appends_behind_it, open_after_a_torn_header_starts_a_fresh_log, open_refuses_mid_file_corruption.
  • Mutation check: with the truncate removed, all 3 fail.
  • mise exec -- cargo test: 0 failures. clippy is clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XoaQgogNq1z3dNoM9feGoH

A short write (the disk ran out at a block boundary, then a drop whose
flush failed) leaves part of a record at the end of the log. A writer
opened without a `load` first appended behind it, and the log was then
corrupt mid-file for good: every `load` and every `compact` failed with
a CRC mismatch. instd's VXLAN network sync hit this on the homelab
(network-sync.snap, a 6-byte and a 5-byte torn cursor record at a 4 KiB
boundary, then complete records) and logged "failed to compact
snapshot" every 30 s from then on.

open now validates the log and truncates a torn tail (a truncate needs
no free space, unlike load's rewrite), replaces a torn header instead
of appending after it, and refuses mid-file corruption, as load does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XoaQgogNq1z3dNoM9feGoH
@jaredLunde
jaredLunde merged commit 77bb35b into main Oct 21, 2026
1 check passed
@jaredLunde
jaredLunde deleted the jared/writer-open-truncates-torn-tail branch October 21, 2026 02:07
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