// 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 }
// ElectRPC implements the Election.Elect rpc. func (this *Election) ElectRPC(reqHeader *msgpb.Header, request *thispb.ElectRequest) (status error) { if !this.InCommittee() { return errs.ErrInvalid } round := request.GetElectionRound() current := this.CurrentRound() if round != current+1 { this.Errorf("could not begin election round %d because current round "+ "is only at %d", round, current) return errs.ErrStale } uid := this.PaxosUIDFromElectionRound(round) paxos, errGet := this.GetPaxosInstance(uid) if errGet != nil { this.Errorf("could not find paxos instance for uid %s: %v", uid, errGet) return errGet } this.Infof("starting new election for round %d using paxos instance %s", round, uid) clientID := reqHeader.GetMessengerId() proposedValue := []byte(clientID) chosenValue, errPropose := paxos.Propose(proposedValue, msg.RequestTimeout(reqHeader)) if errPropose != nil { this.Errorf("could not propose %s as the leader for election round %d: %v", proposedValue, round, errPropose) return errPropose } winner := string(chosenValue) // Send the election result. response := thispb.ElectResponse{} response.ElectionRound = proto.Int64(round) response.ElectionWinner = proto.String(winner) message := thispb.ElectionMessage{} message.ElectResponse = &response if err := msg.SendResponseProto(this.msn, reqHeader, &message); err != nil { this.Errorf("could not send election response to %s: %v", clientID, err) return err } return nil }