Speed up raft leader election

Skip wait if node was previous leader
Shorten the response time to minElectionTimeout

Signed-off-by: Matthias Hanel <mh@synadia.com>
This commit is contained in:
Matthias Hanel
2021-03-12 20:32:09 -05:00
parent a4e84ad781
commit 42e8387a4d

View File

@@ -147,6 +147,9 @@ type raft struct {
c *client c *client
dflag bool dflag bool
// last term this node was leader
llterm uint64
// Subjects for votes, updates, replays. // Subjects for votes, updates, replays.
psubj string psubj string
rpsubj string rpsubj string
@@ -2048,6 +2051,7 @@ func (n *raft) runAsCandidate() {
for len(n.votes) > 0 { for len(n.votes) > 0 {
<-n.votes <-n.votes
} }
lastTermAsLeader := n.llterm
n.Unlock() n.Unlock()
// Send out our request for votes. // Send out our request for votes.
@@ -2057,6 +2061,12 @@ func (n *raft) runAsCandidate() {
votes := 1 votes := 1
won := false won := false
// Used to nil out and thus cancel once timeout is over.
voteChan := n.votes
// No matter what, every server needs to be able to respond within minElectionTimeout
// minElectionTimeout is a value that the elect timer can have too
tikChan := time.After(minElectionTimeout)
for { for {
elect := n.electTimer() elect := n.electTimer()
select { select {
@@ -2069,23 +2079,28 @@ func (n *raft) runAsCandidate() {
case <-n.quit: case <-n.quit:
return return
case <-elect.C: case <-elect.C:
n.switchToCandidate()
return
case <-tikChan:
// disable timeout and receipt of more votes
voteChan = nil
tikChan = nil
if won { if won {
// we are here if we won the election but some server did not respond // we are here if we won the election but some server did not respond
n.switchToLeader() n.switchToLeader()
} else { return
n.switchToCandidate()
} }
return // else wait for the election timer to kick in and start all over again
case vresp := <-n.votes: case vresp := <-voteChan:
if vresp.granted && n.term >= vresp.term { if vresp.granted && n.term >= vresp.term {
// only track peers that would be our followers // only track peers that would be our followers
n.trackPeer(vresp.peer) n.trackPeer(vresp.peer)
votes++ votes++
if n.wonElection(votes) { if n.wonElection(votes) {
// TODO If this server was also leader in n.term-1, then we could skip the timer as well.
// This would be ok as we'd be guaranteed to have the latest history. // This would be ok as we'd be guaranteed to have the latest history.
if len(n.peers) == votes { if len(n.peers) == votes || lastTermAsLeader+1 == n.term {
// Become LEADER if we have won and gotten a quorum with everyone // Become LEADER if we have won and gotten a quorum with everyone or if we have been leader
// in the previous round and thus already waited
n.switchToLeader() n.switchToLeader()
return return
} else { } else {
@@ -3057,5 +3072,6 @@ func (n *raft) switchToLeader() {
} }
n.debug("Switching to leader") n.debug("Switching to leader")
n.leader = n.id n.leader = n.id
n.llterm = n.term
n.switchState(Leader) n.switchState(Leader)
} }