func testCM_SOLO_Follower_ElectsSelfOnElectionTimeout( t *testing.T, mcm *managedConsensusModule, mrs *testhelpers.MockRpcSender, ) { if mcm.pcm.GetServerState() != FOLLOWER { t.Fatal() } if mcm.pcm.RaftPersistentState.GetVotedFor() != "" { t.Fatal() } // Test that a tick before election timeout causes no state change. err := mcm.Tick() if err != nil { t.Fatal(err) } if mcm.pcm.RaftPersistentState.GetCurrentTerm() != testdata.CurrentTerm { t.Fatal() } if mcm.pcm.GetServerState() != FOLLOWER { t.Fatal() } var expectedNewTerm TermNo = testdata.CurrentTerm + 1 timeout1 := mcm.pcm.ElectionTimeoutTracker.GetCurrentElectionTimeout() // Test that election timeout causes a new election mcm.tickTilElectionTimeout(t) if mcm.pcm.RaftPersistentState.GetCurrentTerm() != expectedNewTerm { t.Fatal(expectedNewTerm, mcm.pcm.RaftPersistentState.GetCurrentTerm()) } // Single node should immediately elect itself as leader if mcm.pcm.GetServerState() != LEADER { t.Fatal() } // candidate has voted for itself if mcm.pcm.RaftPersistentState.GetVotedFor() != testdata.ThisServerId { t.Fatal() } // a new election timeout was chosen if mcm.pcm.ElectionTimeoutTracker.GetCurrentElectionTimeout() == timeout1 { t.Fatal() } // candidate state is fresh expectedCvs, err := consensus_state.NewCandidateVolatileState(mcm.pcm.ClusterInfo) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(mcm.pcm.CandidateVolatileState, expectedCvs) { t.Fatal() } // no RPCs issued mrs.CheckSentRpcs(t, make(map[ServerId]interface{})) mrs.ClearSentRpcs() }
func testCM_FollowerOrCandidate_StartsElectionOnElectionTimeout_Part2( t *testing.T, mcm *managedConsensusModule, mrs *testhelpers.MockRpcSender, expectedNewTerm TermNo, ) { timeout1 := mcm.pcm.ElectionTimeoutTracker.GetCurrentElectionTimeout() // Test that election timeout causes a new election mcm.tickTilElectionTimeout(t) if mcm.pcm.RaftPersistentState.GetCurrentTerm() != expectedNewTerm { t.Fatal(expectedNewTerm, mcm.pcm.RaftPersistentState.GetCurrentTerm()) } if mcm.pcm.GetServerState() != CANDIDATE { t.Fatal() } // candidate has voted for itself if mcm.pcm.RaftPersistentState.GetVotedFor() != testdata.ThisServerId { t.Fatal() } // a new election timeout was chosen // Playing the odds here :P if mcm.pcm.ElectionTimeoutTracker.GetCurrentElectionTimeout() == timeout1 { t.Fatal() } // candidate state is fresh expectedCvs, err := consensus_state.NewCandidateVolatileState(mcm.pcm.ClusterInfo) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(mcm.pcm.CandidateVolatileState, expectedCvs) { t.Fatal() } // candidate has issued RequestVote RPCs to all other servers. lastLogIndex, lastLogTerm, err := GetIndexAndTermOfLastEntry(mcm.pcm.LogRO) if err != nil { t.Fatal(err) } expectedRpc := &RpcRequestVote{expectedNewTerm, lastLogIndex, lastLogTerm} expectedRpcs := map[ServerId]interface{}{} err = mcm.pcm.ClusterInfo.ForEachPeer(func(serverId ServerId) error { expectedRpcs[serverId] = expectedRpc return nil }) if err != nil { t.Fatal(err) } mrs.CheckSentRpcs(t, expectedRpcs) mrs.ClearSentRpcs() }
func testIsLeaderWithTermAndSentEmptyAppendEntries( t *testing.T, mcm *managedConsensusModule, mrs *testhelpers.MockRpcSender, serverTerm TermNo, ) { if mcm.pcm.GetServerState() != LEADER { t.Fatal() } if mcm.pcm.RaftPersistentState.GetCurrentTerm() != serverTerm { t.Fatal() } // leader state is fresh expectedNextIndex := map[ServerId]LogIndex{"s2": 11, "s3": 11, "s4": 11, "s5": 11} if !reflect.DeepEqual(mcm.pcm.LeaderVolatileState.NextIndex, expectedNextIndex) { t.Fatal() } expectedMatchIndex := map[ServerId]LogIndex{"s2": 0, "s3": 0, "s4": 0, "s5": 0} if !reflect.DeepEqual(mcm.pcm.LeaderVolatileState.MatchIndex, expectedMatchIndex) { t.Fatal() } // leader setup lastLogIndex, lastLogTerm, err := GetIndexAndTermOfLastEntry(mcm.pcm.LogRO) if err != nil { t.Fatal(err) } expectedRpc := &RpcAppendEntries{ serverTerm, lastLogIndex, lastLogTerm, []LogEntry{}, mcm.pcm.GetCommitIndex(), } expectedRpcs := map[ServerId]interface{}{ "s2": expectedRpc, "s3": expectedRpc, "s4": expectedRpc, "s5": expectedRpc, } mrs.CheckSentRpcs(t, expectedRpcs) }
func testConsensusModule_RpcReplyCallback_AndBecomeLeader( t *testing.T, cm *ConsensusModule, mrs *testhelpers.MockRpcSender, ) { // FIXME: multiple unsafe concurrent accesses // Sleep till election starts ett := cm.passiveConsensusModule.ElectionTimeoutTracker max_ticks := (ett.GetCurrentElectionTimeout().Nanoseconds() / testdata.TickerDuration.Nanoseconds()) + 2 for i := int64(0); i < max_ticks; i++ { time.Sleep(testdata.TickerDuration) if cm.GetServerState() != FOLLOWER { break } } if cm.GetServerState() != CANDIDATE { t.Fatal() } // candidate has issued RequestVote RPCs to all other servers. lastLogIndex, lastLogTerm, err := consensus.GetIndexAndTermOfLastEntry(cm.passiveConsensusModule.LogRO) if err != nil { t.Fatal(err) } expectedRpc := &RpcRequestVote{testdata.CurrentTerm + 1, lastLogIndex, lastLogTerm} expectedRpcs := map[ServerId]interface{}{ "s2": expectedRpc, "s3": expectedRpc, "s4": expectedRpc, "s5": expectedRpc, } mrs.CheckSentRpcs(t, expectedRpcs) // reply true for all votes serverTerm := cm.passiveConsensusModule.RaftPersistentState.GetCurrentTerm() if n := mrs.SendRVRepliesAndClearRpcs(&RpcRequestVoteReply{serverTerm, true}); n != 4 { t.Fatal(n) } time.Sleep(testdata.SleepToLetGoroutineRun) // server should now be a leader if cm.IsStopped() { t.Fatal() } if cm.GetServerState() != LEADER { t.Fatal() } // leader setup expectedRpc2 := &RpcAppendEntries{ serverTerm, lastLogIndex, lastLogTerm, []LogEntry{}, cm.passiveConsensusModule.GetCommitIndex(), } expectedRpcs2 := map[ServerId]interface{}{ "s2": expectedRpc2, "s3": expectedRpc2, "s4": expectedRpc2, "s5": expectedRpc2, } mrs.CheckSentRpcs(t, expectedRpcs2) // reply handling expectedMatchIndex := map[ServerId]LogIndex{"s2": 0, "s3": 0, "s4": 0, "s5": 0} if !reflect.DeepEqual(cm.passiveConsensusModule.LeaderVolatileState.MatchIndex, expectedMatchIndex) { t.Fatal() } if n := mrs.SendAERepliesAndClearRpcs(&RpcAppendEntriesReply{serverTerm, true}); n != 4 { t.Fatal(n) } time.Sleep(testdata.SleepToLetGoroutineRun) expectedMatchIndex = map[ServerId]LogIndex{ "s2": lastLogIndex, "s3": lastLogIndex, "s4": lastLogIndex, "s5": lastLogIndex, } if !reflect.DeepEqual(cm.passiveConsensusModule.LeaderVolatileState.MatchIndex, expectedMatchIndex) { t.Fatal(cm.passiveConsensusModule.LeaderVolatileState.MatchIndex) } }