diff --git a/raft.go b/raft.go index 5b3c7fac..f5ee849e 100644 --- a/raft.go +++ b/raft.go @@ -1082,7 +1082,6 @@ func (r *raft) Step(m pb.Message) error { // m.Type == pb.MsgVote r.logger.Infof("%x [term: %d] received a pb.MsgVote message with higher term from %x [term: %d]", r.id, r.Term, m.From, m.Term) - r.becomeFollower(m.Term, None) } } else if m.Type == pb.MsgPreVoteResp && !m.Reject { // We send pre-vote requests with a term in our future. If the @@ -1190,6 +1189,9 @@ func (r *raft) Step(m pb.Message) error { lastID := r.raftLog.lastEntryID() candLastID := entryID{term: m.LogTerm, index: m.Index} if canVote && r.raftLog.isUpToDate(candLastID) { + // The local node should turn into a follower as long as it + // grants the vote (or pre-vote). + r.becomeFollower(m.Term, None) // Note: it turns out that that learners must be allowed to cast votes. // This seems counter- intuitive but is necessary in the situation in which // a learner has been promoted (i.e. is now a voter) but has not learned @@ -1226,6 +1228,20 @@ func (r *raft) Step(m pb.Message) error { r.Vote = m.From } } else { + if m.Term > r.Term { + // If the local node receives a message with higher term, + // but it doesn't grant the vote (or pre-vote); it turns + // into a follower, but it shouldn't reset the electionElapsed, + // to ensure it has higher priority to start a campaign + // in the next round of election. If we reject a node, it's + // highly likely we will reject it again if it immediately + // campaigns again. + bakElectionElapsed, bakRandomizedElectionTimeout := r.electionElapsed, r.randomizedElectionTimeout + r.becomeFollower(m.Term, None) + r.electionElapsed = bakElectionElapsed + r.randomizedElectionTimeout = bakRandomizedElectionTimeout + } + r.logger.Infof("%x [logterm: %d, index: %d, vote: %x] rejected %s from %x [logterm: %d, index: %d] at term %d", r.id, lastID.term, lastID.index, r.Vote, m.Type, m.From, candLastID.term, candLastID.index, r.Term) r.send(pb.Message{To: m.From, Term: r.Term, Type: voteRespMsgType(m.Type), Reject: true})