// CloseMessage releases a message header. // // header: Message header. // // Returns nil on success. func (this *Messenger) CloseMessage(header *msgpb.Header) error { if header.GetMessengerId() != this.uid { this.Errorf("message header %s is not created by this messenger", header) return errs.ErrInvalid } if header.Request == nil { return nil } // // Remove the request and its response channel from live requests map. // lock, errLock := this.ctlr.Lock("this.requestMap") if errLock != nil { return errLock } defer lock.Unlock() requestID := header.GetMessageId() responseCh, found := this.requestMap[requestID] if !found { return nil } close(responseCh) delete(this.requestMap, requestID) return nil }
// StatusRPC implements the Election.Status rpc. func (this *Election) StatusRPC(reqHeader *msgpb.Header, request *thispb.StatusRequest) error { if !this.InCommittee() { this.Errorf("this election object is not a committee member") return errs.ErrNotExist } lock := this.ctlr.ReadLock("election") round := this.CurrentRound() winner := this.currentWinner lock.Unlock() // Committee members always have up to date election information. response := thispb.StatusResponse{} response.ElectionRound = proto.Int64(round) response.ElectionWinner = proto.String(winner) message := thispb.ElectionMessage{} message.StatusResponse = &response clientID := reqHeader.GetMessengerId() if err := msg.SendResponseProto(this.msn, reqHeader, &message); err != nil { this.Errorf("could not send status response to %s: %v", clientID, err) return err } return nil }
// Phase1RPC handles ClassicPaxos.Phase1 rpc. func (this *Paxos) Phase1RPC(header *msgpb.Header, request *thispb.Phase1Request) (status error) { if !this.IsAcceptor() { this.Errorf("this paxos instance is not an acceptor; rejecting %s", header) return errs.ErrInvalid } lock, errLock := this.ctlr.TimedLock(msg.RequestTimeout(header), "acceptor") if errLock != nil { return errLock } defer lock.Unlock() clientID := header.GetMessengerId() respond := func() error { response := thispb.Phase1Response{} response.PromisedBallot = proto.Int64(this.promisedBallot) if this.votedBallot >= 0 { response.VotedBallot = proto.Int64(this.votedBallot) response.VotedValue = this.votedValue } message := thispb.PaxosMessage{} message.Phase1Response = &response errSend := msg.SendResponseProto(this.msn, header, &message) if errSend != nil { this.Errorf("could not send phase1 response to %s: %v", clientID, errSend) return errSend } return nil } ballot := request.GetBallotNumber() if ballot < this.promisedBallot { this.Warningf("phase1 request from %s is ignored due to stale ballot %d", clientID, ballot) return respond() } if ballot == this.promisedBallot { this.Warningf("duplicate phase1 request from client %s with an already "+ "promised ballot number %d", clientID, ballot) return respond() } // Save the promise into the wal. change := thispb.AcceptorChange{} change.PromisedBallot = proto.Int64(ballot) if err := this.doUpdateAcceptor(&change); err != nil { this.Errorf("could not update acceptor state: %v", err) return err } this.Infof("this acceptor has now promised higher ballot %d from %s", ballot, clientID) return respond() }
// SendResponse sends a response message to the source of a request. // // msn: The Messenger. // // reqHeader: Message header for a request. // // data: User data to include in the response. // // Returns nil on success. func SendResponse(msn Messenger, reqHeader *msgpb.Header, data []byte) error { if reqHeader.Request == nil { msn.Errorf("message header %s is not a request", reqHeader) return errs.ErrInvalid } resHeader := msn.NewResponse(reqHeader) return msn.Send(reqHeader.GetMessengerId(), resHeader, data) }
func (this *RPCServer) Dispatch(header *msgpb.Header, data []byte) error { response := this.NewResponse(header) sourceID := header.GetMessengerId() if err := this.Send(sourceID, response, []byte("response-data")); err != nil { this.Errorf("could not send response for %s to messenger %s: %v", header, sourceID, err) return err } return 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 }
// LearnRPC handles ClassicPaxos.Learn rpc. func (this *Paxos) LearnRPC(header *msgpb.Header, request *thispb.LearnRequest) (status error) { if !this.IsLearner() { this.Errorf("this paxos instance is not a learner; rejecting %s", header) return errs.ErrInvalid } lock, errLock := this.ctlr.TimedLock(msg.RequestTimeout(header), "learner") if errLock != nil { return errLock } defer lock.Unlock() acceptor := header.GetMessengerId() if this.chosenValue == nil { change := thispb.LearnerChange{} change.VotedBallotList = request.VotedBallotList change.VotedValueList = request.VotedValueList change.VotedAcceptor = proto.String(acceptor) if err := this.doUpdateLearner(&change); err != nil { this.Errorf("could not update learner state: %v", err) return err } } response := thispb.LearnResponse{} if this.chosenValue != nil { response.KnowsChosenValue = proto.Bool(true) } message := thispb.PaxosMessage{} message.LearnResponse = &response errSend := msg.SendResponseProto(this.msn, header, &message) if errSend != nil { this.Errorf("could not respond to learn request from %s: %v", acceptor, errSend) return errSend } return nil }
// DispatchRequest invokes the target (exported) function identified in the // request message. // // header: Message header for the request. // // data: User data in the request. // // Returns dispatch operation status and the handler status. func (this *Messenger) DispatchRequest(header *msgpb.Header, data []byte) (msnStatus, appStatus error) { defer func() { if msnStatus != nil || appStatus != nil { failure := this.NewResponse(header) if msnStatus != nil { failure.Response.MessengerStatus = errs.MakeProtoFromError(msnStatus) } else { failure.Response.HandlerStatus = errs.MakeProtoFromError(appStatus) } sourceID := header.GetMessengerId() if err := this.Send(sourceID, failure, nil); err != nil { this.Errorf("could not reply failure %s for %s from %s (ignored)", failure, header, sourceID) } } }() lock, errLock := this.ctlr.Lock("this.exportMap") if errLock != nil { return errLock, nil } defer lock.Unlock() request := header.GetRequest() classID := request.GetClassId() methodName := request.GetMethodName() if len(classID) == 0 || len(methodName) == 0 { this.Errorf("header %s is invalid because class id and/or method name "+ "are empty", header) msnStatus = errs.NewErrInvalid("class id and/or method name cannot be " + "empty") return msnStatus, nil } methodMap, found := this.exportMap[classID] if !found { this.Errorf("no class with id %s was registered", classID) msnStatus = errs.NewErrNotExist("no class with id %s was registered", classID) return msnStatus, nil } handler, found := methodMap[methodName] if !found { this.Errorf("class %s has no method named %s", classID, methodName) msnStatus = errs.NewErrNotExist("class %s has no method named %s", classID, methodName) return msnStatus, nil } // Close the lock early to release the resources. lock.Unlock() appStatus = handler.Dispatch(header, data) if appStatus != nil { this.Warningf("request %s failed with status %v", header, appStatus) } return nil, appStatus }
// Phase2RPC handles ClassicPaxos.Phase2 rpc. func (this *Paxos) Phase2RPC(header *msgpb.Header, request *thispb.Phase2Request) (status error) { if !this.IsAcceptor() { this.Errorf("this paxos instance is not an acceptor; rejecting %s", header) return errs.ErrInvalid } lock, errLock := this.ctlr.TimedLock(msg.RequestTimeout(header), "acceptor") if errLock != nil { return errLock } defer lock.Unlock() clientID := header.GetMessengerId() respond := func() error { response := thispb.Phase2Response{} response.PromisedBallot = proto.Int64(this.promisedBallot) if this.votedBallot >= 0 { response.VotedBallot = proto.Int64(this.votedBallot) response.VotedValue = this.votedValue } message := thispb.PaxosMessage{} message.Phase2Response = &response errSend := msg.SendResponseProto(this.msn, header, &message) if errSend != nil { this.Errorf("could not send phase2 response to %s: %v", clientID, errSend) return errSend } return nil } ballot := request.GetBallotNumber() if ballot < this.promisedBallot { this.Warningf("phase2 request from %s is ignored due to stale ballot %d", clientID, ballot) return respond() } if ballot > this.promisedBallot { this.Errorf("phase2 request from client %s without acquiring a prior "+ "promise", clientID) return respond() } value := request.GetProposedValue() // Save the phase2 vote into the wal. change := thispb.AcceptorChange{} change.VotedBallot = proto.Int64(ballot) change.VotedValue = value if err := this.doUpdateAcceptor(&change); err != nil { this.Errorf("could not update acceptor state: %v", err) return err } this.Infof("this acceptor has voted for %d in ballot %s", ballot, value) if err := respond(); err != nil { return err } // Schedule a notification to all learners. _ = this.alarm.ScheduleAt(this.uid, time.Now(), this.NotifyAllLearners) return nil }