Skip to content

Fix: align RequestVote up to date check with Raft 5.4.1 - #298

Merged
sakno merged 4 commits into
dotnet:developfrom
sukanya1426:fix/vote-uptodate-or
Sep 11, 2026
Merged

sakno merged 4 commits into
dotnet:developfrom
sukanya1426:fix/vote-uptodate-or

Conversation

@sukanya1426

@sukanya1426 sukanya1426 commented Sep 10, 2026

Copy link
Copy Markdown

Hi, thanks for maintaining this library!
I used Specula to model the system and run model checking, which is what brought this to my attention. After looking through the code, I think it may be a potential finding in IsUpToDateAsync, specifically, a small deviation from Raft §5.4.1. Right now, the check is:
index >= localIndex && term >= localTerm

This rejects a candidate whose last-log term is newer but whose log is shorter. However, the Raft paper compares the last-log term first, and only compares the log length when the terms are equal. Both VoteAsync and PreVoteAsync go through this helper. The change would be:

term > localTerm || (term == localTerm && index >= localIndex)

How can we reproduce it?
The helper check is covered in PersistentStateExtensionsTests:
https://github.com/sukanya1426/dotNext/blob/fix/vote-uptodate-or/src/DotNext.Tests/Net/Cluster/Consensus/Raft/ConsensusOnlyStateTests.cs

Local last entry:
index = 3
term = 2

HigherTermShorterLogIsUpToDate checks whether a candidate with last-log (index=2, term=3) is up-to-date. According to the paper, it should be accepted because its last-log term is newer. The current AND check returns false. StaleCandidateIsRejected still makes sure that a candidate with an older last-log term is rejected, even if its log is longer.

That split can happen if one node still has extra uncommitted entries from an older term, while another node received a later-term entry from a different leader and never got those extra older entries.

Happy to adjust the patch if you'd prefer a different approach. Thanks for taking a look!

@sukanya1426

Copy link
Copy Markdown
Author

@dotnet-policy-service agree company="Specula"

@sakno sakno added the Lib:Cluster DotNext.Net.Cluster library label Sep 11, 2026
@sakno sakno added this to Cluster Sep 11, 2026
@github-project-automation github-project-automation Bot moved this to Opened in Cluster Sep 11, 2026
@sakno sakno removed this from Cluster Sep 11, 2026
@sakno

sakno commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

@sukanya1426 , you're right. I need just some time to analyze it.

Comment thread src/DotNext.Tests/Net/Cluster/Consensus/Raft/PersistentStateExtensionsTests.cs Outdated
@sakno

sakno commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

I can confirm that the bug is real and the fix is reasonable. @sukanya1426 , good catch, thanks a lot! Since Specula uses LLM, I would add ai_assisted tag to this PR. Do you mind?

@sukanya1426

Copy link
Copy Markdown
Author

I can confirm that the bug is real and the fix is reasonable. @sukanya1426 , good catch, thanks a lot! Since Specula uses LLM, I would add ai_assisted tag to this PR. Do you mind?

Sure, no problem!

@sakno sakno added the ai_assisted Bug or PR is produced partially or fully by the AI model (Codex, Claude, etc.) label Sep 11, 2026
@sakno
sakno merged commit c3d0b11 into dotnet:develop Sep 11, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai_assisted Bug or PR is produced partially or fully by the AI model (Codex, Claude, etc.) Lib:Cluster DotNext.Net.Cluster library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants