Skip to content

Commit

Permalink
raft: set lead to none when recv PreVoteResp(reject)
Browse files Browse the repository at this point in the history
  • Loading branch information
lishuai87 committed Jul 30, 2017
1 parent 22fb6a3 commit bf883bc
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 0 deletions.
3 changes: 3 additions & 0 deletions raft/raft.go
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,9 @@ func (r *raft) Step(m pb.Message) error {
// rejected our vote so we should become a follower at the new
// term.
default:
if m.Type == pb.MsgPreVoteResp {
lead = None
}
r.logger.Infof("%x [term: %d] received a %s message with higher term from %x [term: %d]",
r.id, r.Term, m.Type, m.From, m.Term)
r.becomeFollower(m.Term, lead)
Expand Down
94 changes: 94 additions & 0 deletions raft/raft_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3209,6 +3209,100 @@ func TestNodeWithSmallerTermCanCompleteElection(t *testing.T) {
}
}

// TestNodeWithSmallerTermCanCompleteElectionWithCheckQuorum ensures that when
// a pre-candidate recv PreVoteResp with reject, will checkQuorum correctly.
func TestNodeWithSmallerTermCanCompleteElectionWithCheckQuorum(t *testing.T) {
n1 := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
n2 := newTestRaft(2, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
n3 := newTestRaft(3, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())

n1.becomeFollower(1, None)
n2.becomeFollower(1, None)
n3.becomeFollower(1, None)

n1.preVote = true
n2.preVote = true
n3.preVote = true

n1.checkQuorum = true
n2.checkQuorum = true
n3.checkQuorum = true

// cause a network partition to isolate node 3
nt := newNetwork(n1, n2, n3)
nt.cut(1, 3)
nt.cut(2, 3)

nt.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})

sm := nt.peers[1].(*raft)
if sm.state != StateLeader {
t.Errorf("peer 1 state: %s, want %s", sm.state, StateLeader)
}
sm = nt.peers[2].(*raft)
if sm.state != StateFollower {
t.Errorf("peer 2 state: %s, want %s", sm.state, StateFollower)
}

nt.send(pb.Message{From: 3, To: 3, Type: pb.MsgHup})
sm = nt.peers[3].(*raft)
if sm.state != StatePreCandidate {
t.Errorf("peer 3 state: %s, want %s", sm.state, StatePreCandidate)
}

// check whether the term values are expected
// n1.Term == 2
// n2.Term == 2
// n3.Term == 1
sm = nt.peers[1].(*raft)
if sm.Term != 2 {
t.Errorf("peer 1 term: %d, want %d", sm.Term, 2)
}
sm = nt.peers[2].(*raft)
if sm.Term != 2 {
t.Errorf("peer 2 term: %d, want %d", sm.Term, 2)
}
sm = nt.peers[3].(*raft)
if sm.Term != 1 {
t.Errorf("peer 3 term: %d, want %d", sm.Term, 1)
}

// check state
// n1 == leader
// n2 == follower
// n3 == pre-candidate
sm = nt.peers[1].(*raft)
if sm.state != StateLeader {
t.Errorf("peer 1 state: %s, want %s", sm.state, StateLeader)
}
sm = nt.peers[2].(*raft)
if sm.state != StateFollower {
t.Errorf("peer 2 state: %s, want %s", sm.state, StateFollower)
}
sm = nt.peers[3].(*raft)
if sm.state != StatePreCandidate {
t.Errorf("peer 3 state: %s, want %s", sm.state, StatePreCandidate)
}

sm.logger.Infof("going to bring back peer 3 and kill peer 1")
// recover the network then immediately isolate node 1 which is currently
// the leader, this is to emulate the crash of node 1.
nt.recover()
nt.cut(1, 2)
nt.cut(1, 3)

// call for election. node 3 should handle PreVoteResp (reject) correctly.
nt.send(pb.Message{From: 3, To: 3, Type: pb.MsgHup})
nt.send(pb.Message{From: 2, To: 2, Type: pb.MsgHup})

// do we have a leader?
sm2 := nt.peers[2].(*raft)
sm3 := nt.peers[3].(*raft)
if sm2.state != StateLeader && sm3.state != StateLeader {
t.Errorf("no leader")
}
}

// TestPreVoteWithCheckQuorum ensures that after a node become pre-candidate,
// it will checkQuorum correctly.
func TestPreVoteWithCheckQuorum(t *testing.T) {
Expand Down

0 comments on commit bf883bc

Please sign in to comment.