Example #1
0
func (s *ConsentState) voteAcceptedEpoch(voter string, newEpoch uint32, voting bool) (uint32, bool) {
	s.acceptedEpochCond.L.Lock()
	defer s.acceptedEpochCond.L.Unlock()

	// Reach quorum. Just Return
	if len(s.acceptedEpochSet) > int(s.ensembleSize/2) {
		return s.acceptedEpoch, true
	}

	if voting {
		s.acceptedEpochSet[voter] = newEpoch

		// This function can panic if we exceed epoch limit
		s.acceptedEpoch = common.CompareAndIncrementEpoch(newEpoch, s.acceptedEpoch)

		if len(s.acceptedEpochSet) > int(s.ensembleSize/2) {
			// reach quorum. Notify
			s.acceptedEpochCond.Broadcast()
			return s.acceptedEpoch, true
		}
	}

	// wait for quorum to be reached.  It is possible
	// that the go-routine is woken up before quorum is
	// reached (if Terminate() is called).   It is
	// also possible that a concurrent go-routine has
	// remove the voter after reaching quorum.  In these
	// cases, return false.
	s.acceptedEpochCond.Wait()
	return s.acceptedEpoch, len(s.acceptedEpochSet) > int(s.ensembleSize/2)
}
Example #2
0
//
// Increment the epoch.  Only call this method if the ensemble size is 1 (single server).
// This function can panic if the epoch reaches its limit.
//
func (l *LeaderServer) incrementEpoch() error {

	epoch, err := l.handler.GetCurrentEpoch()
	if err != nil {
		return err
	}

	epoch = common.CompareAndIncrementEpoch(epoch, epoch)

	log.Current.Debugf("LeaderServer.incrementEpoch(): new epoch %d", epoch)

	if err := l.handler.NotifyNewAcceptedEpoch(epoch); err != nil {
		return err
	}

	if err := l.handler.NotifyNewCurrentEpoch(epoch); err != nil {
		return err
	}

	return nil
}
Example #3
0
//
// Create a new ConsentState for synchronization.   The leader must proceed in
// 4 stages:
// 1) Reach quourm of followers for sending its persisted acceptedEpoch to the leader.
//    The leader uses followers acceptedEpoch to determine the next epoch.
// 2) Reach quorum of followers to accept the new epoch.
// 3) Synchronizes the commit log between leader and each follower
// 4) Reach quorum of followers to accepts this leader (NewLeaderAck)
//
// The ConsentState is used for keep that state for stages (1), (2) and (4) where
// quorum is required to proceed to next stage.
//
// The ConsentState is using the physical host (actual port) which is different for each TCP connection.  This requires
// the ConsentState to be cleaned up if synchronization with a particular follower aborts.   After synchronization
// with a follower succeeds, the follower's vote will stay in the ConsentState, since the main purpose of the
// ConsentState is for voting on a new epoch, as well as establishing that a majority of followers are going
// to follower the leader.   A node can only establish leadership until stage 4 passes.  Once leadership is
// established, if the node looses majority of followers, the server should abort and go through re-election again
// with a new ConsentState.
//
func NewConsentState(sid string, epoch uint32, ensemble uint64) *ConsentState {

	epoch = common.CompareAndIncrementEpoch(epoch, 0) // increment epoch to next value

	state := &ConsentState{
		acceptedEpoch:    epoch,
		acceptedEpochSet: make(map[string]uint32),
		ackEpochSet:      make(map[string]string),
		newLeaderAckSet:  make(map[string]string),
		ensembleSize:     ensemble}

	state.acceptedEpochCond = sync.NewCond(&state.acceptedEpochMutex)
	state.ackEpochCond = sync.NewCond(&state.ackEpochMutex)
	state.newLeaderAckCond = sync.NewCond(&state.newLeaderAckMutex)

	// add the leader to both sets, since enemble size can count the leader as well.
	state.acceptedEpochSet[sid] = epoch
	state.ackEpochSet[sid] = sid
	state.newLeaderAckSet[sid] = sid

	return state
}