// committed instance will // - reject all request messages (pre-accept, accept), // - handle prepare to help it find committed, // - ignore others func (i *Instance) committedProcess(m message.Message) (action uint8, msg message.Message) { defer i.checkStatus(committed) if !i.isAtStatus(committed) { panic("") } switch content := m.Content().(type) { case *message.PreAccept: return noAction, nil case *message.Accept: return noAction, nil case *message.Timeout: // here we ignore the timeout event instead of panic, // because sometimes timeout event // comes right after the instance becomes committed return noAction, nil case *message.Prepare: return i.handlePrepare(content) case *message.PreAcceptReply, *message.PreAcceptOk, *message.AcceptReply, *message.PrepareReply, *message.Commit: return noAction, nil // ignore stale replies default: panic("") } }
// preparing instance could only acts as a sender. // It handles most kinds of messages (in some conditions with larger ballot) and // ignores all replies except prepare reply. func (i *Instance) preparingProcess(m message.Message) (action uint8, msg message.Message) { defer i.checkStatus(preparing, preAccepted, accepted, committed, nilStatus) if !i.isAtStatus(preparing) || i.recoveryInfo == nil { panic("") } switch content := m.Content().(type) { case *message.PreAccept: if content.Ballot.Compare(i.ballot) < 0 { return noAction, nil } return i.handlePreAccept(content) case *message.Accept: if content.Ballot.Compare(i.ballot) < 0 { return noAction, nil } return i.handleAccept(content) case *message.Commit: return i.handleCommit(content) case *message.Timeout: return i.handleTimeout(content) case *message.Prepare: // the instance itself is the first one to have ballot of this // magnitude. It can't receive others having the same if content.Ballot.Compare(i.ballot) == 0 { panic("") } if content.Ballot.Compare(i.ballot) < 0 { return noAction, nil } return i.revertAndHandlePrepare(content) case *message.PrepareReply: if content.Ballot.Compare(i.ballot) < 0 { return noAction, nil } return i.handlePrepareReply(content) case *message.PreAcceptReply: if i.recoveryInfo.formerStatus < preAccepted { panic("") } return noAction, nil case *message.PreAcceptOk: if i.recoveryInfo.formerStatus < preAccepted { panic("") } return noAction, nil case *message.AcceptReply: if i.recoveryInfo.formerStatus < accepted { panic("") } // ignore delayed replies return noAction, nil default: panic("") } }
// preaccepted instance // - handles preaccept-ok/-reply, preaccept, accept, commit, and prepare. func (i *Instance) preAcceptedProcess(m message.Message) (action uint8, msg message.Message) { defer i.checkStatus(preAccepted, accepted, committed, preparing) if !i.isAtStatus(preAccepted) { panic("") } switch content := m.Content().(type) { case *message.PreAccept: if content.Ballot.Compare(i.ballot) < 0 { return noAction, nil } return i.handlePreAccept(content) case *message.Accept: if content.Ballot.Compare(i.ballot) < 0 { return noAction, nil } return i.handleAccept(content) case *message.Commit: return i.handleCommit(content) case *message.Timeout: return i.handleTimeout(content) case *message.Prepare: if content.Ballot.Compare(i.ballot) < 0 { return noAction, nil } return i.handlePrepare(content) case *message.PreAcceptReply: if content.Ballot.Compare(i.ballot) < 0 { // ignore stale PreAcceptReply return noAction, nil } return i.handlePreAcceptReply(content) case *message.PreAcceptOk: if !i.ballot.IsInitialBallot() { return noAction, nil // ignore stale reply } return i.handlePreAcceptOk(content) case *message.AcceptReply: panic("") case *message.PrepareReply: if i.ballot.IsInitialBallot() { panic("") } return noAction, nil default: panic("") } }
// NilStatus exists for: // - the instance is newly created when // - - received a proposal first time and only once. (sender) // - - received pre-accept, accept, commit, prepare the first time. (receiver) // - - required by commit dependencies and transitioning to preparing. (sender) // - the instance is not newly created when // - - after reverted back from `preparing`(sender -> receiver) // - - received prepare and waiting for further message. (receiver) func (i *Instance) nilStatusProcess(m message.Message) (action uint8, msg message.Message) { defer i.checkStatus(nilStatus, preAccepted, accepted, committed, preparing) if !i.isAtStatus(nilStatus) { panic("") } switch content := m.Content().(type) { case *message.Propose: return i.handlePropose(content) case *message.PreAccept: if content.Ballot.Compare(i.ballot) < 0 { return noAction, nil } return i.handlePreAccept(content) case *message.Accept: if content.Ballot.Compare(i.ballot) < 0 { // [*] this could happens when the instance revert from preparing return noAction, nil } return i.handleAccept(content) case *message.Commit: return i.handleCommit(content) case *message.Timeout: return i.handleTimeout(content) case *message.Prepare: if content.Ballot.Compare(i.ballot) < 0 { return noAction, nil } return i.handlePrepare(content) case *message.PrepareReply: if i.isNewBorn() || i.ballot.GetNumber() == 0 { panic("Never send prepare before but receive prepare reply") } return noAction, nil case *message.PreAcceptReply, *message.AcceptReply, *message.PreAcceptOk: panic("") default: panic("") } }
// This function is responsible for communicating with instance processing. func (r *Replica) dispatch(msg message.Message) { replicaId := msg.Replica() instanceId := msg.Instance() r.updateMaxInstanceNum(replicaId, instanceId) v1Log.Infof("Replica[%v]: recv message[%s], from Replica[%v]\n", r.Id, msg.String(), msg.Sender()) if glog.V(0) { printDependencies(msg) } if instanceId <= conflictNotFound { panic("") } if r.InstanceMatrix[replicaId][instanceId] == nil { r.InstanceMatrix[replicaId][instanceId] = NewInstance(r, replicaId, instanceId) if p, ok := msg.(*message.Propose); ok { // send back a signal for this successfully creation close(p.Created) } } i := r.InstanceMatrix[replicaId][instanceId] i.touch() // update last touched timestamp v1Log.Infof("Replica[%v]: instance[%v][%v] status before = %v, ballot = [%v]\n", r.Id, replicaId, instanceId, i.StatusString(), i.ballot.String()) v2Log.Infof("dependencies before: %v\n", i.Dependencies()) var action uint8 var rep message.Message switch i.status { case nilStatus: action, rep = i.nilStatusProcess(msg) case preAccepted: action, rep = i.preAcceptedProcess(msg) case accepted: action, rep = i.acceptedProcess(msg) case committed: action, rep = i.committedProcess(msg) case preparing: action, rep = i.preparingProcess(msg) default: panic("") } v2Log.Infof("dependencies after: %v\n", i.Dependencies()) if action == noAction { v1Log.Infof("Replica[%v]: instance[%v][%v] status after = %v, ballot = [%v]\n\n\n", r.Id, replicaId, instanceId, i.StatusString(), i.ballot.String()) } else { v1Log.Infof("Replica[%v]: instance[%v][%v] status after = %v, ballot = [%v]\n", r.Id, replicaId, instanceId, i.StatusString(), i.ballot.String()) } if r.enablePersistent { r.StoreSingleInstance(i) } switch action { case noAction: return case replyAction: v1Log.Infof("Replica[%v]: send message[%s], to Replica[%v]\n\n\n", r.Id, rep.String(), msg.Sender()) r.Transporter.Send(msg.Sender(), rep) // send back to the sender of the message case fastQuorumAction: v1Log.Infof("Replica[%v]: send message[%s], to FastQuorum\n\n\n", r.Id, rep.String()) r.Transporter.MulticastFastquorum(rep) case broadcastAction: v1Log.Infof("Replica[%v]: send message[%s], to Everyone\n\n\n", r.Id, rep.String()) r.Transporter.Broadcast(rep) default: panic("") } }