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