Skip to content

Commit 9bfd517

Browse files
committed
Improve: tolerate leader state reversion upon restart
When a leader restarted and its log reverted, and tried to re-elect itself as leader: And when vote request is rejected and see a greater vote, it should only update to the non-committed version of the responded vote to its local state: This prevents a dangerous scenario when state reversion is allowed: 1. A node was a leader but its state reverted to a previous version; 2. The node restarts and begins election; 3. It receives a vote response containing its own previous leader vote; 4. Without this protection, it would update to that committed vote and become leader again; 5. However, it lacks the necessary logs, causing committed entries to be lost or inconsistent; By using the non-committed version, we prevent this reverted node from becoming leader while still allowing proper vote updates for legitimate cases.
1 parent 49a7df1 commit 9bfd517

File tree

2 files changed

+15
-18
lines changed

2 files changed

+15
-18
lines changed

openraft/src/core/raft_core.rs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -661,23 +661,6 @@ where
661661
self.engine.snapshot_handler().trigger_snapshot();
662662
}
663663

664-
/// Reject a request due to the Raft node being in a state which prohibits the request.
665-
#[tracing::instrument(level = "trace", skip(self, tx))]
666-
pub(crate) fn reject_with_forward_to_leader<T: OptionalSend, E>(&self, tx: ResultSender<C, T, E>)
667-
where E: From<ForwardToLeader<C::NodeId, C::Node>> + OptionalSend {
668-
let mut leader_id = self.current_leader();
669-
let leader_node = self.get_leader_node(leader_id.clone());
670-
671-
// Leader is no longer a node in the membership config.
672-
if leader_node.is_none() {
673-
leader_id = None;
674-
}
675-
676-
let err = ForwardToLeader { leader_id, leader_node };
677-
678-
let _ = tx.send(Err(err.into()));
679-
}
680-
681664
#[tracing::instrument(level = "debug", skip(self))]
682665
pub(crate) fn current_leader(&self) -> Option<C::NodeId> {
683666
tracing::debug!(

openraft/src/engine/engine_impl.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,8 +386,22 @@ where C: RaftTypeConfig
386386
self.set_greater_log();
387387
}
388388

389+
// When vote request is rejected, only update to the non-committed version of the vote.
390+
//
391+
// This prevents a dangerous scenario when state reversion is allowed:
392+
// 1. A node was a leader but its state reverted to a previous version
393+
// 2. The node restarts and begins election
394+
// 3. It receives a vote response containing its own previous leader vote
395+
// 4. Without this protection, it would update to that committed vote and become leader again
396+
// 5. However, it lacks the necessary logs, causing committed entries to be lost or inconsistent
397+
//
398+
// By using the non-committed version, we prevent this reverted node from becoming leader
399+
// while still allowing proper vote updates for legitimate cases.
400+
let mut vote = resp.vote.clone();
401+
vote.committed = false;
402+
389403
// Update if resp.vote is greater.
390-
let _ = self.vote_handler().update_vote(&resp.vote);
404+
let _ = self.vote_handler().update_vote(&vote);
391405
}
392406

393407
/// Append entries to follower/learner.

0 commit comments

Comments
 (0)