Merge pull request #2911 from hashicorp/serial-leader-loop

Switches to reliable Raft leader notifications.
This commit is contained in:
James Phillips 2017-04-13 14:43:32 -07:00 committed by GitHub
commit c59f8fba5e
2 changed files with 23 additions and 2 deletions

View File

@ -5,6 +5,7 @@ import (
"net" "net"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"github.com/armon/go-metrics" "github.com/armon/go-metrics"
@ -29,18 +30,29 @@ const (
// as the leader in the Raft cluster. There is some work the leader is // as the leader in the Raft cluster. There is some work the leader is
// expected to do, so we must react to changes // expected to do, so we must react to changes
func (s *Server) monitorLeadership() { func (s *Server) monitorLeadership() {
leaderCh := s.raft.LeaderCh() // We use the notify channel we configured Raft with, NOT Raft's
// leaderCh, which is only notified best-effort. Doing this ensures
// that we get all notifications in order, which is required for
// cleanup and to ensure we never run multiple leader loops.
leaderCh := s.leaderCh
var wg sync.WaitGroup
var stopCh chan struct{} var stopCh chan struct{}
for { for {
select { select {
case isLeader := <-leaderCh: case isLeader := <-leaderCh:
if isLeader { if isLeader {
stopCh = make(chan struct{}) stopCh = make(chan struct{})
go s.leaderLoop(stopCh) wg.Add(1)
go func() {
s.leaderLoop(stopCh)
wg.Done()
}()
s.logger.Printf("[INFO] consul: cluster leadership acquired") s.logger.Printf("[INFO] consul: cluster leadership acquired")
} else if stopCh != nil { } else if stopCh != nil {
close(stopCh) close(stopCh)
stopCh = nil stopCh = nil
wg.Wait()
s.logger.Printf("[INFO] consul: cluster leadership lost") s.logger.Printf("[INFO] consul: cluster leadership lost")
} }
case <-s.shutdownCh: case <-s.shutdownCh:

View File

@ -132,6 +132,10 @@ type Server struct {
raftTransport *raft.NetworkTransport raftTransport *raft.NetworkTransport
raftInmem *raft.InmemStore raftInmem *raft.InmemStore
// leaderCh set up by setupRaft() and ensures that we get reliable leader
// transition notifications from the Raft layer.
leaderCh <-chan bool
// reconcileCh is used to pass events from the serf handler // reconcileCh is used to pass events from the serf handler
// into the leader manager, so that the strong state can be // into the leader manager, so that the strong state can be
// updated // updated
@ -554,6 +558,11 @@ func (s *Server) setupRaft() error {
} }
} }
// Set up a channel for reliable leader notifications.
leaderCh := make(chan bool, 1)
s.config.RaftConfig.NotifyCh = leaderCh
s.leaderCh = leaderCh
// Setup the Raft store. // Setup the Raft store.
s.raft, err = raft.NewRaft(s.config.RaftConfig, s.fsm, log, stable, snap, trans) s.raft, err = raft.NewRaft(s.config.RaftConfig, s.fsm, log, stable, snap, trans)
if err != nil { if err != nil {