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 }