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 }