Beispiel #1
0
// NotifyAllLearners sends the current vote to all learners.
func (this *Paxos) NotifyAllLearners() (status error) {
	if !this.IsConfigured() || !this.IsAcceptor() {
		return nil
	}

	defer func() {
		if status != nil && !errs.IsClosed(status) {
			now := time.Now()
			next := now.Add(this.opts.LearnRetryInterval)
			_ = this.alarm.ScheduleAt(this.uid, next, this.NotifyAllLearners)
		}
	}()

	rlock := this.ctlr.ReadLock("acceptor", "config")
	// Stop notifications when all learners know the consensus value.
	if len(this.doneLearnerSet) == len(this.learnerList) {
		rlock.Unlock()
		return nil
	}
	// Make a copy of what we need: learners and the vote map.
	numLearners := len(this.learnerList)
	learnerList := append([]string{}, this.learnerList...)
	votedValueMap := make(map[int64][]byte)
	for ballot, value := range this.votedValueMap {
		if ackMap := this.learnerAckMap[ballot]; len(ackMap) < numLearners {
			votedValueMap[ballot] = value
		}
	}
	rlock.Unlock()

	request := thispb.LearnRequest{}
	for ballot, value := range votedValueMap {
		request.VotedBallotList = append(request.VotedBallotList, ballot)
		request.VotedValueList = append(request.VotedValueList, value)
	}
	message := thispb.PaxosMessage{}
	message.LearnRequest = &request

	// Send notification to all learners.
	reqHeader := this.msn.NewRequest(this.namespace, this.uid,
		"ClassicPaxos.Learn", this.opts.LearnTimeout)
	defer this.msn.CloseMessage(reqHeader)

	count, errSend := msg.SendAllProto(this.msn, learnerList, reqHeader,
		&message)
	if errSend != nil {
		this.Errorf("could not send learn request to all learners: %v", errSend)
		return errSend
	}

	// Wait for responses from all learners.
	for ii := 0; ii < count; ii++ {
		message := thispb.PaxosMessage{}
		resHeader, errRecv := msg.ReceiveProto(this.msn, reqHeader, &message)
		if errRecv != nil {
			this.Warningf("could not receive learner responses: %v", errRecv)
			break
		}

		learner := resHeader.GetMessengerId()
		if message.LearnResponse == nil {
			continue
		}
		response := message.GetLearnResponse()

		// Save the learner acknowledgment to the wal.
		change := thispb.AcceptorChange{}
		change.AckedLearner = proto.String(learner)
		if response.GetKnowsChosenValue() {
			change.AckedChosenValue = proto.Bool(true)
		} else {
			for ballot := range votedValueMap {
				change.AckedBallotList = append(change.AckedBallotList, ballot)
			}
		}
		if err := this.UpdateAcceptor(&change); err != nil {
			this.Errorf("could not update acceptor state: %v", err)
			return err
		}
	}
	return nil
}