func TestFindNewerCommitIndex_Figure8_CaseEextended(t *testing.T) { ci, err := config.NewClusterInfo([]ServerId{"s1", "s2", "s3", "s4", "s5"}, "s1") if err != nil { t.Fatal(err) } // Figure 8, case (e) extended with extra term 4 entry at index 4 terms := []TermNo{1, 2, 4, 4} // leader line for the case imle := log.TestUtil_NewInMemoryLog_WithTerms(terms) lvs, err := consensus_state.NewLeaderVolatileState(ci, LogIndex(len(terms))) if err != nil { t.Fatal(err) } _findNewerCommitIndex := func(currentTerm TermNo, commitIndex LogIndex) LogIndex { nci, err := consensus_state.FindNewerCommitIndex(ci, lvs, imle, currentTerm, commitIndex) if err != nil { t.Fatal(err) } return nci } // match peers err = lvs.SetMatchIndexAndNextIndex("s2", 4) if err != nil { t.Fatal(err) } err = lvs.SetMatchIndexAndNextIndex("s3", 4) if err != nil { t.Fatal(err) } err = lvs.SetMatchIndexAndNextIndex("s4", 1) if err != nil { t.Fatal(err) } err = lvs.SetMatchIndexAndNextIndex("s5", 1) if err != nil { t.Fatal(err) } // currentTerm = 4 has a solution // the highest match is returned if _findNewerCommitIndex(4, 0) != 4 { t.Fatal() } if _findNewerCommitIndex(4, 1) != 4 { t.Fatal() } if _findNewerCommitIndex(4, 2) != 4 { t.Fatal() } if _findNewerCommitIndex(4, 3) != 4 { t.Fatal() } // returns 0 if current commitIndex is the highest match if _findNewerCommitIndex(4, 4) != 0 { t.Fatal() } }
func TestFindNewerCommitIndex_SOLO(t *testing.T) { ci, err := config.NewClusterInfo([]ServerId{"s1"}, "s1") if err != nil { t.Fatal(err) } terms := []TermNo{1, 2, 2, 2, 3, 3} imle := log.TestUtil_NewInMemoryLog_WithTerms(terms) lvs, err := consensus_state.NewLeaderVolatileState(ci, LogIndex(len(terms))) if err != nil { t.Fatal(err) } _findNewerCommitIndex := func(currentTerm TermNo, commitIndex LogIndex) LogIndex { nci, err := consensus_state.FindNewerCommitIndex(ci, lvs, imle, currentTerm, commitIndex) if err != nil { t.Fatal(err) } return nci } if nci := _findNewerCommitIndex(1, 0); nci != 1 { t.Fatal(nci) } if nci := _findNewerCommitIndex(1, 1); nci != 0 { t.Fatal(nci) } // the highest match is returned if nci := _findNewerCommitIndex(2, 0); nci != 4 { t.Fatal(nci) } if nci := _findNewerCommitIndex(2, 3); nci != 4 { t.Fatal(nci) } if nci := _findNewerCommitIndex(3, 1); nci != 6 { t.Fatal(nci) } // returns 0 if current commitIndex is the highest match if nci := _findNewerCommitIndex(2, 4); nci != 0 { t.Fatal(nci) } }
// #RFS-L4: If there exists an N such that N > commitIndex, a majority // of matchIndex[i] >= N, and log[N].term == currentTerm: // set commitIndex = N (#5.3, #5.4) func (cm *PassiveConsensusModule) advanceCommitIndexIfPossible() error { newerCommitIndex, err := consensus_state.FindNewerCommitIndex( cm.ClusterInfo, cm.LeaderVolatileState, cm.LogRO, cm.RaftPersistentState.GetCurrentTerm(), cm.GetCommitIndex(), ) if err != nil { return err } if newerCommitIndex != 0 && newerCommitIndex > cm.GetCommitIndex() { err = cm.setCommitIndex(newerCommitIndex) if err != nil { return err } } return nil }
func TestFindNewerCommitIndex_Figure8_CaseCAndE(t *testing.T) { ci, err := config.NewClusterInfo([]ServerId{"s1", "s2", "s3", "s4", "s5"}, "s1") if err != nil { t.Fatal(err) } // Figure 8, case (c) terms := []TermNo{1, 2, 4} // leader line for the case imle := log.TestUtil_NewInMemoryLog_WithTerms(terms) lvs, err := consensus_state.NewLeaderVolatileState(ci, LogIndex(len(terms))) if err != nil { t.Fatal(err) } _findNewerCommitIndex := func(currentTerm TermNo, commitIndex LogIndex) LogIndex { nci, err := consensus_state.FindNewerCommitIndex(ci, lvs, imle, currentTerm, commitIndex) if err != nil { t.Fatal(err) } return nci } // With matchIndex stuck at 0, there is no solution for any currentTerm if _findNewerCommitIndex(1, 0) != 0 { t.Fatal() } if _findNewerCommitIndex(2, 0) != 0 { t.Fatal() } if _findNewerCommitIndex(3, 0) != 0 { t.Fatal() } // match peers for Figure 8, case (c) err = lvs.SetMatchIndexAndNextIndex("s2", 2) if err != nil { t.Fatal(err) } err = lvs.SetMatchIndexAndNextIndex("s3", 2) if err != nil { t.Fatal(err) } err = lvs.SetMatchIndexAndNextIndex("s4", 1) if err != nil { t.Fatal(err) } err = lvs.SetMatchIndexAndNextIndex("s5", 1) if err != nil { t.Fatal(err) } // While we cannot be at currentTerm=1, it has solutions if _findNewerCommitIndex(1, 0) != 1 { t.Fatal() } if _findNewerCommitIndex(1, 1) != 0 { t.Fatal() } // While we cannot be at currentTerm=2, it has solutions if _findNewerCommitIndex(2, 0) != 2 { t.Fatal() } if _findNewerCommitIndex(2, 1) != 2 { t.Fatal() } // While we cannot be at currentTerm=3, it has no solution if _findNewerCommitIndex(3, 0) != 0 { t.Fatal() } if _findNewerCommitIndex(3, 1) != 0 { t.Fatal() } if _findNewerCommitIndex(3, 2) != 0 { t.Fatal() } // No solution for currentTerm >= 4 if _findNewerCommitIndex(4, 0) != 0 { t.Fatal() } if _findNewerCommitIndex(4, 1) != 0 { t.Fatal() } if _findNewerCommitIndex(4, 2) != 0 { t.Fatal() } // match peers for Figure 8, case (e) err = lvs.SetMatchIndexAndNextIndex("s2", 3) if err != nil { t.Fatal(err) } err = lvs.SetMatchIndexAndNextIndex("s3", 3) if err != nil { t.Fatal(err) } err = lvs.SetMatchIndexAndNextIndex("s4", 1) if err != nil { t.Fatal(err) } err = lvs.SetMatchIndexAndNextIndex("s5", 1) if err != nil { t.Fatal(err) } // Now currentTerm = 4 has a solution if _findNewerCommitIndex(4, 0) != 3 { t.Fatal() } if _findNewerCommitIndex(4, 1) != 3 { t.Fatal() } if _findNewerCommitIndex(4, 2) != 3 { t.Fatal() } if _findNewerCommitIndex(4, 3) != 0 { t.Fatal() } }