Repository navigation
fix(raft): queue each committed index once per Ready window - #348
Merged
Merged
Conversation
farhan-syah
requested changes
Sep 20, 2026
farhan-syah
left a comment
Member
There was a problem hiding this comment.
Root cause verified independently: collect_committed_entries is the only producer of ready.committed_entries, and the loop calls advance_applied only after apply_group_commits, so two commit advances between drains re-queue the range. Fix at the producer, no new state. Correct layer.
Claims verified: red proof reproduces on the base producer ([2, 2, 3]), nodedb-raft node:: 86 pass, nodedb-cluster apply_committed 1 pass, clippy --all-targets -D warnings clean on both crates, fmt clean.
Two inline notes, neither blocks. After the change, amend the commit or squash: one commit on this branch.
collect_committed_entries resumed from last_applied alone. It runs on every commit-index advance — a follower's AppendEntries, a single voter's propose — while the loop drains Ready and advances last_applied later, so two advances inside one window queued the same committed range twice and the applier received one committed index twice in a single batch. An append-shaped write gains a row from that repeat; the applier's delivery guard is the boundary, not the reason it was invisible. The collect now resumes past the furthest index already queued into Ready. A tick apply path also records the first committed index that is not greater than its predecessor, so a producer-side regression is observable instead of silent.
EnRaiha
force-pushed
the
fix/p2-duplicate-index
branch
from
September 21, 2026 15:26
c1ac606 to
218b1bb
Compare
farhan-syah
approved these changes
Sep 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The P2 review left one unexplained shape: a Raft batch carrying the same committed index twice. The applier's delivery guard (each committed index handed off at most once, watermark claimed in one critical section) is the boundary — but the producer-side cause was unknown, and an append-shaped write gains a row if that guard ever slips.
Root cause found:
collect_committed_entriesresumed fromlast_appliedalone. It runs on every commit-index advance — a follower's AppendEntries, a single voter's propose — while the loop drainsReadyand advanceslast_appliedlater. Two advances inside one window queued the same committed range twice, putting one index twice into a single batch.Part of #165 (P2 — cluster consensus safety).
What
collect_committed_entriesresumes past the furthest index already queued intoReady(max(last_applied, queued_through)), so a second advance in the window queues only what is new. No new state, O(1).apply_group_commitsrecords the first committed index that is not greater than its predecessor (first_non_increasing_committed_index, zero-allocationwindows(2)) with a warn — a producer-side regression is observable, and the applier guard stays the boundary.Validation
cargo test -p nodedb-raft --lib node::— 86 passed.cargo test -p nodedb-cluster --lib apply_committed— 1 passed (detector).cargo check -p nodedb-cluster --all-targets— clean; repository preflight passes.Notes
Readystarts empty). That is a repeated delivery across batches, covered by the applier's watermark guard, not a duplicate inside one batch.