Skip to content

fix(wal): keep a committed log segment when the manifest CAS answers 412 - #11

Merged
yordis merged 2 commits into
mainfrom
yordis/fix-wal-keep-412-log-segment
Oct 23, 2026
Merged

yordis merged 2 commits into
mainfrom
yordis/fix-wal-keep-412-log-segment

Conversation

@yordis

@yordis yordis commented Oct 23, 2026

Copy link
Copy Markdown
Member

@cursor

cursor Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

PR Summary

High Risk
Changes core WAL commit semantics on manifest CAS 412 across push, compact, settings, and seal paths; incorrect classification could still delete committed segments or wedge writers.

Overview
Fixes a liveness/safety bug where a manifest CAS that had already committed could still surface as 412 (typical when an object-store SDK retries a conditional PUT after a lost response). The publisher used to treat every 412 as a lost race and CAS-delete its log segment, leaving a committed manifest that references a missing segment.

Publishing now re-reads the manifest via slot_evidence before dropping a slot: Committed (exact segment listed and bytes match) is treated as success; Unlisted (no descriptor and log not folded past the seq) allows safe cleanup and retry; Unknown (e.g. checkpoint folded descriptors away) returns CommitUnknown and keeps the segment. The same 412 handling is wired through push, compaction, settings publish, and seal pack replacement.

The fault-injection layer gains p_cas_fail_after (apply CAS, then answer 412) plus simulation/WAL tests and doc updates for the extra manifest GET on the 412 path. The TLA+ tools jar pin in scripts/ensure-tla-tools.sh is bumped.

Reviewed by Cursor Bugbot for commit 5afc103. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 58 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a27abb89-2e50-42d5-a149-73cf01618b5d
📥 Commits

Reviewing files that changed from the base of the PR and between bd761cb and 5afc103.

📒 Files selected for processing: 8
  • crates/walgit-server/tests/sim.rs
  • crates/walgit-store/src/fault.rs
  • crates/walgit-wal/src/classification.rs
  • crates/walgit-wal/src/publish.rs
  • crates/walgit-wal/tests/coverage.rs
  • crates/walgit-wal/tests/wal.rs
  • docs/ROUNDTRIPS.md
  • docs/spec/README.md
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

0bserver07 and others added 2 commits October 23, 2026 03:24
The S3 SDK retries failed calls three times by default and the GCS
client retries uploads that carry a precondition, the manifest CAS
included. When a CAS lands and its reply is lost, the retry gets a 412
from our own write. process_batch, publish_compact_classified and
publish_settings_impl all read any 412 as a lost race and delete their
log segment, which the committed manifest now lists: the writer reports
a failure for a durable change and every reader then fails on the
missing segment.

Run the existing cas_landed check on a 412 too before dropping the
segment, and keep an unknown outcome unknown when that check fails.
The fault store gains the missing fault kind (applied, then 412),
sharing the existing CAS roll so seeded simulations keep their
sequences, and a test drives a push, a compaction and a settings change
through it. One extra manifest GET on the 412 path only.

(cherry picked from commit 9fb0223)
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
A 412 on the manifest CAS can answer a transport retry of our own landed
write. The previous commit looks before dropping the segment, but treats
"not listed" as "never committed", and the pack seal still drops on any
412:

- seal_pack_replacement answered a landed 412 by deleting the segment the
  committed manifest lists: the same corruption, on the maintainer's
  retirement path.
- A checkpoint that folds the log at or past our seq drops our segment
  descriptor. If one lands between our CAS and the read-back, "not listed"
  proves nothing: the writer deleted its committed (folded) segment and
  re-verified its own push into a conflict against its own commit.

slot_evidence now classifies the fresh manifest as Committed (our exact
segment is listed), Unlisted (our key is not listed and nothing is folded
at or past our seq, so no committed manifest ever listed it; with a 412
no copy of our request can land later) or Unknown (folded, our key listed
with other contents, or no manifest). Every 412 path - push, compact,
settings and seal - drops the segment only on Unlisted; Unknown keeps it
and reports CommitUnknown. The non-412 paths are unchanged (cas_landed).

Round trips: unchanged. The 412 path still costs one manifest GET (plus
one log GET when our descriptor is listed); successful publishes do not
change.

Tests: a unit test for every evidence case; a_landed_seal_answered_412_
keeps_the_commit (fails before: "manifest lists log/...2.pb but it is
gone"); a_landed_cas_folded_before_the_412_is_resolved_stays_unknown
(fails before: the push is answered Conflict{expected: (new), actual:
<its own commit>}); and a sim liveness scenario,
liveness_after_a_landed_cas_answered_412.

(cherry picked from commit 69ba1b2)
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
@yordis
yordis force-pushed the yordis/fix-wal-keep-412-log-segment branch from fdc6d25 to 5afc103 Compare October 23, 2026 07:24
@yordis
yordis changed the base branch from main to yordis/fix-spec-tla-pin October 23, 2026 07:24
Base automatically changed from yordis/fix-spec-tla-pin to main October 23, 2026 08:18
@yordis
yordis marked this pull request as ready for review October 23, 2026 08:19
@yordis
yordis merged commit 7c0a6c9 into main Oct 23, 2026
7 checks passed
@yordis
yordis deleted the yordis/fix-wal-keep-412-log-segment branch October 23, 2026 08:19
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.

3 participants