// respondToClient replies to the client when // an entry is replicated on majority of servers func (s *raftServer) updateLeaderCommitIndex(followers []int, matchIndex *utils.SyncIntIntMap) { for s.State() == LEADER { N := s.commitIndex.Get() + 1 upto := N + 1 for N <= upto { if !s.localLog.Exists(N) { break } i := 1 for _, f := range followers { if j, _ := matchIndex.Get(f); j >= N { i++ upto = max(upto, j) } } // followers do not include Leader if entry := s.localLog.Get(N); i > (len(followers)+1)/2 && entry.Term == s.Term() { s.writeToLog("Updating commitIndex to " + strconv.FormatInt(N, 10)) s.commitIndex.Set(N) } N++ } time.Sleep(NICE * time.Millisecond) } }
// handleFollowers ensures that followers are informed // about new messages and lagging followers catch up // AppendEntry and AppendEntryResponses occur in // lockstep. aeToken is used to implement mutex like // behaviour to ensure that a new AppendEntry message // is sent only when response to previous AppendEntry // message is received func (s *raftServer) handleFollowers(followers []int, nextIndex *utils.SyncIntIntMap, matchIndex *utils.SyncIntIntMap, aeToken *utils.SyncIntIntMap) { for s.State() == LEADER { for _, f := range followers { lastIndex := s.localLog.TailIndex() n, ok := nextIndex.Get(f) if !ok { panic("nextIndex not found for follower " + strconv.Itoa(f)) } unlocked, ok := aeToken.Get(f) if !ok { panic("aeToken not found for follower " + strconv.Itoa(f)) } if lastIndex != 0 && lastIndex >= n && unlocked == 1 { aeToken.Set(f, 0) // send a new AppendEntry prevIndex := n - 1 var prevTerm int64 = 0 // n = 0 when we add first entry to the log if prevIndex > 0 { prevTerm = s.localLog.Get(prevIndex).Term } ae := &AppendEntry{Term: s.Term(), LeaderId: s.server.Pid(), PrevLogIndex: prevIndex, PrevLogTerm: prevTerm} ae.LeaderCommit = s.commitIndex.Get() ae.Entry = *s.localLog.Get(n) s.writeToLog("Replicating entry " + strconv.FormatInt(n, 10)) s.server.Outbox() <- &cluster.Envelope{Pid: f, Msg: ae} } } time.Sleep(NICE * time.Millisecond) } }