예제 #1
0
파일: messenger.go 프로젝트: bvk/ascent
// 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
}
예제 #2
0
파일: election.go 프로젝트: bvk/ascent
// 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
	}
}
예제 #3
0
파일: messenger.go 프로젝트: bvk/ascent
// DispatchResponse enqueues an incoming response into the response channel for
// the corresponding request.
//
// header: Message header for the response.
//
// data: User data in the response message.
//
// Returns nil on success.
func (this *Messenger) DispatchResponse(header *msgpb.Header,
	data []byte) error {

	lock, errLock := this.ctlr.Lock("this.requestMap")
	if errLock != nil {
		return errLock
	}
	defer lock.Unlock()

	response := header.GetResponse()
	requestID := response.GetRequestId()
	responseCh, found := this.requestMap[requestID]
	if !found {
		this.Errorf("could not find request %d to dispatch response %s",
			requestID, header)
		return errs.ErrNotExist
	}

	// Close the lock early to release the resources.
	lock.Unlock()

	entry := &Entry{header: header, data: data}

	select {
	case responseCh <- entry:
		return nil
	default:
		this.Errorf("could not queue response %s to request %d", header,
			requestID)
		return errs.ErrOverflow
	}
}
예제 #4
0
파일: election.go 프로젝트: bvk/ascent
// 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
}
예제 #5
0
파일: paxos.go 프로젝트: bvk/ascent
// 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()
}
예제 #6
0
파일: util.go 프로젝트: bvk/ascent
// 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)
}
예제 #7
0
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
}
예제 #8
0
파일: election.go 프로젝트: bvk/ascent
// 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)
}
예제 #9
0
파일: paxos.go 프로젝트: bvk/ascent
// 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
	}
}
예제 #10
0
파일: election.go 프로젝트: bvk/ascent
// 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
}
예제 #11
0
파일: messenger.go 프로젝트: bvk/ascent
// Receive waits for a response to a live request.
//
// request: Message header of a live request.
//
// Returns response message header and response data on success.
func (this *Messenger) Receive(request *msgpb.Header) (*msgpb.Header,
	[]byte, error) {

	timeout := msg.RequestTimeout(request)
	lock, errLock := this.ctlr.TimedLock(timeout, "this.requestMap")
	if errLock != nil {
		return nil, nil, errLock
	}
	defer lock.Unlock()

	requestID := request.GetMessageId()
	responseCh, found := this.requestMap[requestID]
	if !found {
		this.Errorf("no live request [%s] exist", request)
		return nil, nil, errs.ErrNotExist
	}

	// Close the lock early to unblock others.
	lock.Unlock()

	// Perform a non-blocking receive if timeout is zero.
	if timeout == 0 {
		select {
		case entry := <-responseCh:
			return entry.header, entry.data, nil

		default:
			return nil, nil, errs.ErrRetry
		}
	}

	// Perform a blocking receive with a timeout.
	select {
	case <-time.After(timeout):
		this.Warningf("timedout waiting for response to %s", request)
		return nil, nil, errs.ErrTimeout

	case entry := <-responseCh:
		if entry == nil {
			return nil, nil, errs.ErrRetry
		}
		return entry.header, entry.data, nil
	}
}
예제 #12
0
파일: paxos.go 프로젝트: bvk/ascent
// 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
}
예제 #13
0
파일: util.go 프로젝트: bvk/ascent
// 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)
}
예제 #14
0
파일: messenger.go 프로젝트: bvk/ascent
// NewResponse creates a message header for a response message.
//
// request: Message header for the received request.
func (this *Messenger) NewResponse(request *msgpb.Header) *msgpb.Header {
	header := this.NewPost()
	header.Response = &msgpb.Header_Response{}
	header.Response.RequestId = proto.Int64(request.GetMessageId())
	return header
}
예제 #15
0
파일: messenger.go 프로젝트: bvk/ascent
// 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
}
예제 #16
0
파일: paxos.go 프로젝트: bvk/ascent
// 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
}