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) }
// // 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 }
// // 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 }