github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/gossip/election/election.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package election
     8  
     9  import (
    10  	"bytes"
    11  	"sync"
    12  	"sync/atomic"
    13  	"time"
    14  
    15  	"github.com/hyperledger/fabric/gossip/util"
    16  	"github.com/op/go-logging"
    17  	"github.com/spf13/viper"
    18  )
    19  
    20  // Gossip leader election module
    21  // Algorithm properties:
    22  // - Peers break symmetry by comparing IDs
    23  // - Each peer is either a leader or a follower,
    24  //   and the aim is to have exactly 1 leader if the membership view
    25  //   is the same for all peers
    26  // - If the network is partitioned into 2 or more sets, the number of leaders
    27  //   is the number of network partitions, but when the partition heals,
    28  //   only 1 leader should be left eventually
    29  // - Peers communicate by gossiping leadership proposal or declaration messages
    30  
    31  // The Algorithm, in pseudo code:
    32  //
    33  //
    34  // variables:
    35  // 	leaderKnown = false
    36  //
    37  // Invariant:
    38  //	Peer listens for messages from remote peers
    39  //	and whenever it receives a leadership declaration,
    40  //	leaderKnown is set to true
    41  //
    42  // Startup():
    43  // 	wait for membership view to stabilize, or for a leadership declaration is received
    44  //      or the startup timeout expires.
    45  //	goto SteadyState()
    46  //
    47  // SteadyState():
    48  // 	while true:
    49  //		If leaderKnown is false:
    50  // 			LeaderElection()
    51  //		If you are the leader:
    52  //			Broadcast leadership declaration
    53  //			If a leadership declaration was received from
    54  // 			a peer with a lower ID,
    55  //			become a follower
    56  //		Else, you're a follower:
    57  //			If haven't received a leadership declaration within
    58  // 			a time threshold:
    59  //				set leaderKnown to false
    60  //
    61  // LeaderElection():
    62  // 	Gossip leadership proposal message
    63  //	Collect messages from other peers sent within a time period
    64  //	If received a leadership declaration:
    65  //		return
    66  //	Iterate over all proposal messages collected.
    67  // 	If a proposal message from a peer with an ID lower
    68  // 	than yourself was received, return.
    69  //	Else, declare yourself a leader
    70  
    71  // LeaderElectionAdapter is used by the leader election module
    72  // to send and receive messages and to get membership information
    73  type LeaderElectionAdapter interface {
    74  	// Gossip gossips a message to other peers
    75  	Gossip(Msg)
    76  
    77  	// Accept returns a channel that emits messages
    78  	Accept() <-chan Msg
    79  
    80  	// CreateProposalMessage
    81  	CreateMessage(isDeclaration bool) Msg
    82  
    83  	// Peers returns a list of peers considered alive
    84  	Peers() []Peer
    85  }
    86  
    87  type leadershipCallback func(isLeader bool)
    88  
    89  // LeaderElectionService is the object that runs the leader election algorithm
    90  type LeaderElectionService interface {
    91  	// IsLeader returns whether this peer is a leader or not
    92  	IsLeader() bool
    93  
    94  	// Stop stops the LeaderElectionService
    95  	Stop()
    96  }
    97  
    98  type peerID []byte
    99  
   100  // Peer describes a remote peer
   101  type Peer interface {
   102  	// ID returns the ID of the peer
   103  	ID() peerID
   104  }
   105  
   106  // Msg describes a message sent from a remote peer
   107  type Msg interface {
   108  	// SenderID returns the ID of the peer sent the message
   109  	SenderID() peerID
   110  	// IsProposal returns whether this message is a leadership proposal
   111  	IsProposal() bool
   112  	// IsDeclaration returns whether this message is a leadership declaration
   113  	IsDeclaration() bool
   114  }
   115  
   116  func noopCallback(_ bool) {
   117  }
   118  
   119  // NewLeaderElectionService returns a new LeaderElectionService
   120  func NewLeaderElectionService(adapter LeaderElectionAdapter, id string, callback leadershipCallback) LeaderElectionService {
   121  	if len(id) == 0 {
   122  		panic("Empty id")
   123  	}
   124  	le := &leaderElectionSvcImpl{
   125  		id:            peerID(id),
   126  		proposals:     util.NewSet(),
   127  		adapter:       adapter,
   128  		stopChan:      make(chan struct{}, 1),
   129  		interruptChan: make(chan struct{}, 1),
   130  		logger:        util.GetLogger(util.LoggingElectionModule, ""),
   131  		callback:      noopCallback,
   132  	}
   133  
   134  	if callback != nil {
   135  		le.callback = callback
   136  	}
   137  
   138  	go le.start()
   139  	return le
   140  }
   141  
   142  // leaderElectionSvcImpl is an implementation of a LeaderElectionService
   143  type leaderElectionSvcImpl struct {
   144  	id        peerID
   145  	proposals *util.Set
   146  	sync.Mutex
   147  	stopChan      chan struct{}
   148  	interruptChan chan struct{}
   149  	stopWG        sync.WaitGroup
   150  	isLeader      int32
   151  	toDie         int32
   152  	leaderExists  int32
   153  	sleeping      bool
   154  	adapter       LeaderElectionAdapter
   155  	logger        *logging.Logger
   156  	callback      leadershipCallback
   157  }
   158  
   159  func (le *leaderElectionSvcImpl) start() {
   160  	le.stopWG.Add(2)
   161  	go le.handleMessages()
   162  	le.waitForMembershipStabilization(getStartupGracePeriod())
   163  	go le.run()
   164  }
   165  
   166  func (le *leaderElectionSvcImpl) handleMessages() {
   167  	le.logger.Debug(le.id, ": Entering")
   168  	defer le.logger.Debug(le.id, ": Exiting")
   169  	defer le.stopWG.Done()
   170  	msgChan := le.adapter.Accept()
   171  	for {
   172  		select {
   173  		case <-le.stopChan:
   174  			le.stopChan <- struct{}{}
   175  			return
   176  		case msg := <-msgChan:
   177  			if !le.isAlive(msg.SenderID()) {
   178  				le.logger.Debug(le.id, ": Got message from", msg.SenderID(), "but it is not in the view")
   179  				break
   180  			}
   181  			le.handleMessage(msg)
   182  		}
   183  	}
   184  }
   185  
   186  func (le *leaderElectionSvcImpl) handleMessage(msg Msg) {
   187  	msgType := "proposal"
   188  	if msg.IsDeclaration() {
   189  		msgType = "declaration"
   190  	}
   191  	le.logger.Debug(le.id, ":", msg.SenderID(), "sent us", msgType)
   192  	le.Lock()
   193  	defer le.Unlock()
   194  
   195  	if msg.IsProposal() {
   196  		le.proposals.Add(string(msg.SenderID()))
   197  	} else if msg.IsDeclaration() {
   198  		atomic.StoreInt32(&le.leaderExists, int32(1))
   199  		if le.sleeping && len(le.interruptChan) == 0 {
   200  			le.interruptChan <- struct{}{}
   201  		}
   202  		if bytes.Compare(msg.SenderID(), le.id) < 0 && le.IsLeader() {
   203  			le.stopBeingLeader()
   204  		}
   205  	} else {
   206  		// We shouldn't get here
   207  		le.logger.Error("Got a message that's not a proposal and not a declaration")
   208  	}
   209  }
   210  
   211  // waitForInterrupt sleeps until the interrupt channel is triggered
   212  // or given timeout expires
   213  func (le *leaderElectionSvcImpl) waitForInterrupt(timeout time.Duration) {
   214  	le.logger.Debug(le.id, ": Entering")
   215  	defer le.logger.Debug(le.id, ": Exiting")
   216  	le.Lock()
   217  	le.sleeping = true
   218  	le.Unlock()
   219  
   220  	select {
   221  	case <-le.interruptChan:
   222  	case <-le.stopChan:
   223  		le.stopChan <- struct{}{}
   224  	case <-time.After(timeout):
   225  	}
   226  
   227  	le.Lock()
   228  	le.sleeping = false
   229  	// We drain the interrupt channel
   230  	// because we might get 2 leadership declarations messages
   231  	// while sleeping, but we would only read 1 of them in the select block above
   232  	le.drainInterruptChannel()
   233  	le.Unlock()
   234  }
   235  
   236  func (le *leaderElectionSvcImpl) run() {
   237  	defer le.stopWG.Done()
   238  	for !le.shouldStop() {
   239  		if !le.isLeaderExists() {
   240  			le.leaderElection()
   241  		}
   242  		if le.shouldStop() {
   243  			return
   244  		}
   245  		if le.IsLeader() {
   246  			le.leader()
   247  		} else {
   248  			le.follower()
   249  		}
   250  	}
   251  }
   252  
   253  func (le *leaderElectionSvcImpl) leaderElection() {
   254  	le.logger.Debug(le.id, ": Entering")
   255  	defer le.logger.Debug(le.id, ": Exiting")
   256  	le.propose()
   257  	le.waitForInterrupt(getLeaderElectionDuration())
   258  	// If someone declared itself as a leader, give up
   259  	// on trying to become a leader too
   260  	if le.isLeaderExists() {
   261  		le.logger.Debug(le.id, ": Some peer is already a leader")
   262  		return
   263  	}
   264  	// Leader doesn't exist, let's see if there is a better candidate than us
   265  	// for being a leader
   266  	for _, o := range le.proposals.ToArray() {
   267  		id := o.(string)
   268  		if bytes.Compare(peerID(id), le.id) < 0 {
   269  			return
   270  		}
   271  	}
   272  	// If we got here, there is no one that proposed being a leader
   273  	// that's a better candidate than us.
   274  	le.beLeader()
   275  	atomic.StoreInt32(&le.leaderExists, int32(1))
   276  }
   277  
   278  // propose sends a leadership proposal message to remote peers
   279  func (le *leaderElectionSvcImpl) propose() {
   280  	le.logger.Debug(le.id, ": Entering")
   281  	le.logger.Debug(le.id, ": Exiting")
   282  	leadershipProposal := le.adapter.CreateMessage(false)
   283  	le.adapter.Gossip(leadershipProposal)
   284  }
   285  
   286  func (le *leaderElectionSvcImpl) follower() {
   287  	le.logger.Debug(le.id, ": Entering")
   288  	defer le.logger.Debug(le.id, ": Exiting")
   289  
   290  	le.proposals.Clear()
   291  	atomic.StoreInt32(&le.leaderExists, int32(0))
   292  	select {
   293  	case <-time.After(getLeaderAliveThreshold()):
   294  	case <-le.stopChan:
   295  		le.stopChan <- struct{}{}
   296  	}
   297  }
   298  
   299  func (le *leaderElectionSvcImpl) leader() {
   300  	leaderDeclaration := le.adapter.CreateMessage(true)
   301  	le.adapter.Gossip(leaderDeclaration)
   302  	le.waitForInterrupt(getLeadershipDeclarationInterval())
   303  }
   304  
   305  // waitForMembershipStabilization waits for membership view to stabilize
   306  // or until a time limit expires, or until a peer declares itself as a leader
   307  func (le *leaderElectionSvcImpl) waitForMembershipStabilization(timeLimit time.Duration) {
   308  	le.logger.Debug(le.id, ": Entering")
   309  	defer le.logger.Debug(le.id, ": Exiting, peers found", len(le.adapter.Peers()))
   310  	endTime := time.Now().Add(timeLimit)
   311  	viewSize := len(le.adapter.Peers())
   312  	for !le.shouldStop() {
   313  		time.Sleep(getMembershipSampleInterval())
   314  		newSize := len(le.adapter.Peers())
   315  		if newSize == viewSize || time.Now().After(endTime) || le.isLeaderExists() {
   316  			return
   317  		}
   318  		viewSize = newSize
   319  	}
   320  }
   321  
   322  // drainInterruptChannel clears the interruptChannel
   323  // if needed
   324  func (le *leaderElectionSvcImpl) drainInterruptChannel() {
   325  	if len(le.interruptChan) == 1 {
   326  		<-le.interruptChan
   327  	}
   328  }
   329  
   330  // isAlive returns whether peer of given id is considered alive
   331  func (le *leaderElectionSvcImpl) isAlive(id peerID) bool {
   332  	for _, p := range le.adapter.Peers() {
   333  		if bytes.Equal(p.ID(), id) {
   334  			return true
   335  		}
   336  	}
   337  	return false
   338  }
   339  
   340  func (le *leaderElectionSvcImpl) isLeaderExists() bool {
   341  	return atomic.LoadInt32(&le.leaderExists) == int32(1)
   342  }
   343  
   344  // IsLeader returns whether this peer is a leader
   345  func (le *leaderElectionSvcImpl) IsLeader() bool {
   346  	isLeader := atomic.LoadInt32(&le.isLeader) == int32(1)
   347  	le.logger.Debug(le.id, ": Returning", isLeader)
   348  	return isLeader
   349  }
   350  
   351  func (le *leaderElectionSvcImpl) beLeader() {
   352  	le.logger.Debug(le.id, ": Becoming a leader")
   353  	atomic.StoreInt32(&le.isLeader, int32(1))
   354  	le.callback(true)
   355  }
   356  
   357  func (le *leaderElectionSvcImpl) stopBeingLeader() {
   358  	le.logger.Debug(le.id, "Stopped being a leader")
   359  	atomic.StoreInt32(&le.isLeader, int32(0))
   360  	le.callback(false)
   361  }
   362  
   363  func (le *leaderElectionSvcImpl) shouldStop() bool {
   364  	return atomic.LoadInt32(&le.toDie) == int32(1)
   365  }
   366  
   367  // Stop stops the LeaderElectionService
   368  func (le *leaderElectionSvcImpl) Stop() {
   369  	le.logger.Debug(le.id, ": Entering")
   370  	defer le.logger.Debug(le.id, ": Exiting")
   371  	atomic.StoreInt32(&le.toDie, int32(1))
   372  	le.stopChan <- struct{}{}
   373  	le.stopWG.Wait()
   374  }
   375  
   376  // SetStartupGracePeriod configures startup grace period interval,
   377  // the period of time to wait until election algorithm will start
   378  func SetStartupGracePeriod(t time.Duration) {
   379  	viper.Set("peer.gossip.election.startupGracePeriod", t)
   380  }
   381  
   382  // SetMembershipSampleInterval setups/initializes the frequency the
   383  // membership view should be checked
   384  func SetMembershipSampleInterval(t time.Duration) {
   385  	viper.Set("peer.gossip.election.membershipSampleInterval", t)
   386  }
   387  
   388  // SetLeaderAliveThreshold configures leader election alive threshold
   389  func SetLeaderAliveThreshold(t time.Duration) {
   390  	viper.Set("peer.gossip.election.leaderAliveThreshold", t)
   391  }
   392  
   393  // SetLeaderElectionDuration configures expected leadership election duration,
   394  // interval to wait until leader election will be completed
   395  func SetLeaderElectionDuration(t time.Duration) {
   396  	viper.Set("peer.gossip.election.leaderElectionDuration", t)
   397  }
   398  
   399  func getStartupGracePeriod() time.Duration {
   400  	return util.GetDurationOrDefault("peer.gossip.election.startupGracePeriod", time.Second*15)
   401  }
   402  
   403  func getMembershipSampleInterval() time.Duration {
   404  	return util.GetDurationOrDefault("peer.gossip.election.membershipSampleInterval", time.Second)
   405  }
   406  
   407  func getLeaderAliveThreshold() time.Duration {
   408  	return util.GetDurationOrDefault("peer.gossip.election.leaderAliveThreshold", time.Second*10)
   409  }
   410  
   411  func getLeadershipDeclarationInterval() time.Duration {
   412  	return time.Duration(getLeaderAliveThreshold() / 2)
   413  }
   414  
   415  func getLeaderElectionDuration() time.Duration {
   416  	return util.GetDurationOrDefault("peer.gossip.election.leaderElectionDuration", time.Second*5)
   417  }
   418  
   419  // GetMsgExpirationTimeout return leadership message expiration timeout
   420  func GetMsgExpirationTimeout() time.Duration {
   421  	return getLeaderAliveThreshold() * 10
   422  }