mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
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:
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user