// ConsensusUpdate implements paxos.Watcher interface. func (this *Election) ConsensusUpdate(uid string, index int64, value []byte) { round := this.ElectionRoundFromPaxosUID(uid) if round <= this.CurrentRound() { return } winner := string(value) lock, errLock := this.ctlr.LockAll() if errLock != nil { return } defer lock.Unlock() change := thispb.ElectionChange{} change.ElectionRound = proto.Int64(round) change.ElectionWinner = proto.String(winner) if err := this.doUpdateElection(&change); err != nil { this.Fatalf("could not update new election round status: %v", err) } if this.InCommittee() { change := thispb.CommitteeChange{} change.NewElectionRound = proto.Int64(round) change.NewElectionWinner = proto.String(winner) if err := this.doUpdateCommittee(&change); err != nil { this.Fatalf("could not update committee with new election status: %v", err) } } }
// ElectLeader performs a leader election for the next round. func (this *Election) ElectLeader(timeout time.Duration) ( string, int64, error) { lock := this.ctlr.ReadLockAll() target := this.msn.UID() if !this.InCommittee() { target = this.committee[rand.Intn(len(this.committee))] } lock.Unlock() request := thispb.ElectRequest{} request.ElectionRound = proto.Int64(this.CurrentRound() + 1) message := thispb.ElectionMessage{} message.ElectRequest = &request reqHeader := this.msn.NewRequest(this.namespace, this.uid, "Election.Elect", timeout) defer this.msn.CloseMessage(reqHeader) if err := msg.SendProto(this.msn, target, reqHeader, &message); err != nil { this.Errorf("could not send elect request to %s: %v", target, err) return "", -1, err } message.Reset() _, errRecv := msg.ReceiveProto(this.msn, reqHeader, &message) if errRecv != nil { this.Errorf("could not receive elect response from %s: %v", target, errRecv) return "", -1, errRecv } if message.ElectResponse == nil { this.Errorf("elect response from %s is empty", target) return "", -1, errs.ErrCorrupt } response := message.GetElectResponse() change := thispb.ElectionChange{} change.ElectionRound = response.ElectionRound change.ElectionWinner = response.ElectionWinner if err := this.UpdateElection(&change); err != nil { this.Errorf("could not update to new election round: %v", err) return "", -1, err } newRound := response.GetElectionRound() newLeader := response.GetElectionWinner() return newLeader, newRound, nil }
// RefreshLeader queries committee members for the latest election information. func (this *Election) RefreshLeader(timeout time.Duration) ( string, int64, error) { lock := this.ctlr.ReadLock("config") committee := append([]string{}, this.committee...) lock.Unlock() request := thispb.StatusRequest{} message := thispb.ElectionMessage{} message.StatusRequest = &request reqHeader := this.msn.NewRequest(this.namespace, this.uid, "Election.Status", timeout) defer this.msn.CloseMessage(reqHeader) count, errSend := msg.SendAllProto(this.msn, committee, reqHeader, &message) if errSend != nil && count < this.MajoritySize() { this.Errorf("could not send status request to committee: %v", errSend) return "", -1, errSend } // Wait for majority number of status responses and pick the winner for the // largest election round. It may also happen that no election round exists // because this instance is just created. maxElectionRound := int64(-1) maxElectionWinner := "" remoteSet := make(map[string]struct{}) for ii := 0; ii < count && len(remoteSet) < this.MajoritySize(); ii++ { message := thispb.ElectionMessage{} resHeader, errRecv := msg.ReceiveProto(this.msn, reqHeader, &message) if errRecv != nil { this.Warningf("could not receive any more status responses for %s: %v", reqHeader, errRecv) break } memberID := resHeader.GetMessengerId() if _, ok := remoteSet[memberID]; ok { this.Warningf("duplicate status response from %s (ignored)", memberID) continue } // TODO Check the response header for errors. if message.StatusResponse == nil { this.Warningf("status response data is empty from %s (ignored)", memberID) continue } response := message.GetStatusResponse() if response.ElectionRound != nil { round := response.GetElectionRound() if round > maxElectionRound { maxElectionRound = round maxElectionWinner = response.GetElectionWinner() } } remoteSet[memberID] = struct{}{} } if len(remoteSet) < this.MajoritySize() { this.Errorf("could not get majority responses %v in finding leader", remoteSet) return "", -1, errs.ErrRetry } if maxElectionRound > this.CurrentRound() { change := thispb.ElectionChange{} change.ElectionRound = proto.Int64(maxElectionRound) change.ElectionWinner = proto.String(maxElectionWinner) if err := this.UpdateElection(&change); err != nil { this.Errorf("could not update election status: %v", err) return maxElectionWinner, maxElectionRound, err } } this.Infof("found %s as the last known leader in round %d", maxElectionWinner, maxElectionRound) return maxElectionWinner, maxElectionRound, nil }