// Dispatch handles all incoming rpc requests for this object. func (this *Election) Dispatch(header *msgpb.Header, data []byte) error { request := header.GetRequest() objectID := request.GetObjectId() // Dispatch classic paxos rpcs to the paxos instances. if objectID != this.uid { if this.IsPaxosUID(objectID) { return this.DispatchPaxos(header, data) } this.Errorf("rpc request %s doesn't belong to this instance", header) return errs.ErrInvalid } message := thispb.ElectionMessage{} if err := proto.Unmarshal(data, &message); err != nil { this.Errorf("could not parse incoming message %s", header) return err } switch { case message.ElectRequest != nil: return this.ElectRPC(header, message.GetElectRequest()) case message.StatusRequest != nil: return this.StatusRPC(header, message.GetStatusRequest()) default: this.Errorf("unknown/invalid rpc reqest %s", header) return errs.ErrInvalid } }
// DispatchPaxos handles incoming rpc requests for the paxos instances. func (this *Election) DispatchPaxos(header *msgpb.Header, data []byte) error { request := header.GetRequest() objectID := request.GetObjectId() instance, errGet := this.GetPaxosInstance(objectID) if errGet != nil { this.Errorf("could not get paxos instance with object id %s", objectID) return errGet } return instance.Dispatch(header, data) }
// RequestTimeout returns the pending time left to complete the operations // represented by the request header. Timeout is measured from the creation // timestamp if the request header is created locally; otherwise, timeout is // measured from the time this request is received from the socket (optionally, // removing any transfer latency if available.) func RequestTimeout(header *msgpb.Header) time.Duration { if header.Request == nil { // This is not a request. return 0 } start := header.GetCreateTimestampNsecs() if header.ReceiverTimestampNsecs != nil { start = header.GetReceiverTimestampNsecs() } request := header.GetRequest() timeout := time.Duration(request.GetTimeoutNsecs()) elapsed := time.Now().UnixNano() - start return timeout - time.Duration(elapsed) }
// Dispatch implements the msg.Handler interface for Paxos objects. // // header: Message header for an incoming request. // // data: User data in the message. // // Returns the result of perform the incoming request. func (this *Paxos) Dispatch(header *msgpb.Header, data []byte) error { request := header.GetRequest() if request == nil { this.Error("rpc message %s is not a request", header) return errs.ErrInvalid } if request.GetObjectId() != this.uid { this.Errorf("rpc request [%s] doesn't belong to this paxos instance %s", header, this.uid) return errs.ErrInvalid } message := &thispb.PaxosMessage{} if err := proto.Unmarshal(data, message); err != nil { this.Errorf("could not parse message [%s]: %v", header, err) return err } switch { case message.ProposeRequest != nil: if !this.IsProposer() { return errs.ErrInvalid } return this.ProposeRPC(header, message.ProposeRequest) case message.Phase1Request != nil: if !this.IsAcceptor() { return errs.ErrInvalid } return this.Phase1RPC(header, message.Phase1Request) case message.Phase2Request != nil: if !this.IsAcceptor() { return errs.ErrInvalid } return this.Phase2RPC(header, message.Phase2Request) case message.LearnRequest != nil: if !this.IsLearner() { return errs.ErrInvalid } return this.LearnRPC(header, message.LearnRequest) default: this.Errorf("rpc request [%s] has no request parameters", header) return errs.ErrInvalid } }
// 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 }