github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/gossip/election/election.go (about)

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