func (this *Paxos) doUpdateConfig(change *thispb.Configuration) error { if !this.wal.IsRecovering() { record := thispb.WALRecord{} record.ConfigChange = change _, errQueue := wal.QueueChangeProto(this.wal, this.uid, &record) if errQueue != nil { this.Errorf("could not append config change wal record: %v", errQueue) return errQueue } } this.doRestoreConfig(change) return nil }
func (this *Paxos) doUpdateLearner(change *thispb.LearnerChange) error { if this.chosenValue != nil { return nil } if !this.wal.IsRecovering() { walRecord := thispb.WALRecord{} walRecord.LearnerChange = change _, errQueue := wal.QueueChangeProto(this.wal, this.uid, &walRecord) if errQueue != nil { this.Errorf("could not write learner change record: %v", errQueue) return errQueue } } if change.ChosenValue != nil { this.chosenValue = change.GetChosenValue() this.Infof("consensus result learned from proposer is %s", this.chosenValue) if this.watch != nil { this.watch.ConsensusUpdate(this.uid, -1, this.chosenValue) } return nil } acceptor := change.GetVotedAcceptor() for index := range change.VotedBallotList { ballot := change.VotedBallotList[index] value := change.VotedValueList[index] this.ballotValueMap[ballot] = value acceptorSet, found := this.ballotAcceptorsMap[ballot] if !found { acceptorSet = make(map[string]struct{}) this.ballotAcceptorsMap[ballot] = acceptorSet } acceptorSet[acceptor] = struct{}{} if len(acceptorSet) >= this.MajoritySize() { this.chosenValue = value this.Infof("consensus result learned through votes is %s", value) if this.watch != nil { this.watch.ConsensusUpdate(this.uid, -1, this.chosenValue) } } } return nil }
// RecoverCheckpoint recovers state from a checkpoint record. func (this *Paxos) RecoverCheckpoint(uid string, data []byte) error { if uid != this.uid { this.Errorf("checkpoint record doesn't belong to this instance") return errs.ErrInvalid } walRecord := thispb.WALRecord{} if err := proto.Unmarshal(data, &walRecord); err != nil { this.Errorf("could not parse checkpoint wal record: %v", err) return err } if walRecord.Checkpoint == nil { this.Errorf("checkpoint record has no data") return errs.ErrInvalid } checkpoint := walRecord.GetCheckpoint() config := checkpoint.GetConfiguration() this.doRestoreConfig(config) if this.IsProposer() { if checkpoint.ProposerState == nil { this.Errorf("checkpoint record has no proposer state") return errs.ErrInvalid } this.doRestoreProposer(checkpoint.GetProposerState()) } if this.IsAcceptor() { if checkpoint.AcceptorState == nil { this.Errorf("checkpoint record has no acceptor state") return errs.ErrInvalid } this.doRestoreAcceptor(checkpoint.GetAcceptorState()) } if this.IsLearner() { if checkpoint.LearnerState == nil { this.Errorf("checkpoint record has no learner state") } this.doRestoreLearner(checkpoint.GetLearnerState()) } return nil }
func (this *Paxos) doUpdateProposer(change *thispb.ProposerChange) error { if !this.wal.IsRecovering() { walRecord := thispb.WALRecord{} walRecord.ProposerChange = change _, errSync := wal.SyncChangeProto(this.wal, this.uid, &walRecord) if errSync != nil { this.Errorf("could not write proposer change record: %v", errSync) return errSync } } if change.ProposalBallot != nil { ballot := change.GetProposalBallot() if this.proposalBallot < ballot { this.proposalBallot = ballot } } return nil }
// TakeCheckpoint saves current state into the wal as a checkpoint record. func (this *Paxos) TakeCheckpoint() error { lock := this.ctlr.ReadLockAll() defer lock.Unlock() if !this.IsConfigured() { this.Errorf("classic paxos instance is not yet configured") return errs.ErrInvalid } checkpoint := thispb.Checkpoint{} config := thispb.Configuration{} this.doSaveConfiguration(&config) checkpoint.Configuration = &config if this.IsProposer() { state := thispb.ProposerState{} this.doSaveProposer(&state) checkpoint.ProposerState = &state } if this.IsAcceptor() { state := thispb.AcceptorState{} this.doSaveAcceptor(&state) checkpoint.AcceptorState = &state } if this.IsLearner() { state := thispb.LearnerState{} this.doSaveLearner(&state) checkpoint.LearnerState = &state } walRecord := thispb.WALRecord{} walRecord.Checkpoint = &checkpoint errAppend := wal.AppendCheckpointProto(this.wal, this.uid, &walRecord) if errAppend != nil { this.Errorf("could not append checkpoint record: %v", errAppend) return errAppend } return nil }
func (this *Paxos) doUpdateAcceptor(change *thispb.AcceptorChange) error { if !this.wal.IsRecovering() { walRecord := thispb.WALRecord{} walRecord.AcceptorChange = change _, errSync := wal.SyncChangeProto(this.wal, this.uid, &walRecord) if errSync != nil { this.Errorf("could not write acceptor change record: %v", errSync) return errSync } } if change.PromisedBallot != nil { this.promisedBallot = change.GetPromisedBallot() } if change.VotedBallot != nil { ballot := change.GetVotedBallot() value := change.GetVotedValue() this.votedBallot = ballot this.votedValue = value this.votedValueMap[ballot] = value } if change.AckedLearner != nil { learner := change.GetAckedLearner() if change.GetAckedChosenValue() { this.doneLearnerSet[learner] = struct{}{} } else { for _, ballot := range change.GetAckedBallotList() { learnerSet, found := this.learnerAckMap[ballot] if !found { learnerSet = make(map[string]struct{}) this.learnerAckMap[ballot] = learnerSet } learnerSet[learner] = struct{}{} } } } return nil }
// RecoverChange recovers an update from a change record. func (this *Paxos) RecoverChange(lsn wal.LSN, uid string, data []byte) error { if lsn == nil { // We reached end of wal recovery, figure out the current state and resume // any inflight operations. _ = this.Refresh() return nil } if uid != this.uid { this.Errorf("change record doesn't belong to this instance") return errs.ErrInvalid } walRecord := thispb.WALRecord{} if err := proto.Unmarshal(data, &walRecord); err != nil { this.Errorf("could not parse change record: %v", err) return err } switch { case walRecord.ConfigChange != nil: return this.doUpdateConfig(walRecord.GetConfigChange()) case walRecord.ProposerChange != nil: return this.doUpdateProposer(walRecord.GetProposerChange()) case walRecord.AcceptorChange != nil: return this.doUpdateAcceptor(walRecord.GetAcceptorChange()) case walRecord.LearnerChange != nil: return this.doUpdateLearner(walRecord.GetLearnerChange()) default: this.Errorf("invalid/corrupt wal change record: %s", walRecord) return errs.ErrCorrupt } return nil }