Preserve votes when term does not change#8094
Open
cjen1-msft wants to merge 4 commits into
Open
Conversation
Reproduce the same-term election-safety violation where a node retains a peer vote while becoming a pre-vote candidate, then clears and reuses that vote after rejecting an unmatched AppendEntries. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use a three-node network where the single follower is the overlapping voter for both term-3 leaders. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
cjen1-msft
force-pushed
the
cjen1-msft/raft-prevote-double-leader
branch
from
July 23, 2026 11:45
2a9ce1a to
18cb608
Compare
cjen1-msft
changed the base branch from
achamayou-test-former-candidate-vote
to
main
July 23, 2026 11:47
This was referenced Jul 23, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a Raft election-safety bug in CCF’s AFT consensus implementation by ensuring voted_for is only cleared when the observed term actually changes (preventing double-voting within a single term). It also adds a deterministic Raft scenario to reproduce the prior double-leader path under pre-vote and assert the corrected behavior.
Changes:
- Preserve
voted_foracross same-term step-downs by conditionally clearing it only whencurrent_viewchanges. - Add a new 3-node deterministic Raft scenario that exercises the pre-vote double-leader path and asserts the delayed second election is rejected.
- Ensure the scenario converges back to liveness after the safety assertion by dropping pending messages and syncing.
Custom instructions used:
.github/copilot-instructions.md.github/instructions/reviewing.instructions.md
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/consensus/aft/raft.h |
Prevents clearing voted_for on same-term transitions by resetting it only when the term actually changes. |
tests/raft_scenarios/pre_vote_double_leader |
Adds a deterministic regression scenario to cover the pre-vote/delayed election path and assert single-leader safety in-term. |
cjen1-msft
force-pushed
the
cjen1-msft/raft-prevote-double-leader
branch
from
July 23, 2026 13:11
5cfd8e2 to
81909b7
Compare
Only clear voted_for when become_aware_of_new_term observes an actual term change, and update the pre-vote double-leader scenario to assert the second election is rejected. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
cjen1-msft
force-pushed
the
cjen1-msft/raft-prevote-double-leader
branch
from
July 23, 2026 13:16
81909b7 to
353fdeb
Compare
achamayou
approved these changes
Jul 23, 2026
cjen1-msft
enabled auto-merge (squash)
July 23, 2026 17:15
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.
Summary
voted_foris only cleared when the observed term actually changesWhy this fixes the violation
Pre-vote does not write
voted_for, but it preserves any regular vote already cast in the current term. Previously, a same-term unmatched AppendEntries calledbecome_aware_of_new_term()and erased that regular vote even though the term had not changed. This let the same node vote for two different candidates in term 3. Same term is not a new term, so the vote is now preserved; higher-term handling still clears it.Validation
Candidate != Leaderandalready voted for n[0]scripts/ci-checks.shpassed