// TODO: This method is inefficient.. we should not have to update this state by a complete // TODO: iteration each time. Instead it should be updated as a direct response to each // TODO: append response. public static long QuorumAppendIndex <MEMBER>(ISet <MEMBER> votingMembers, FollowerStates <MEMBER> states) { /* * Build up a map of tx id -> number of instances that have appended, * sorted by tx id. * * This allows us to then iterate backwards over the values in the map, * adding up a total count of how many have appended, until we reach a majority. * Once we do, the tx id at the current entry in the map will be the highest one * with a majority appended. */ SortedDictionary <long, int> appendedCounts = new SortedDictionary <long, int>(); foreach (MEMBER member in votingMembers) { long txId = states.Get(member).MatchIndex; appendedCounts.merge(txId, 1, (a, b) => a + b); } // Iterate over it until we find a majority int total = 0; foreach (KeyValuePair <long, int> entry in appendedCounts.descendingMap().entrySet()) { total += entry.Value; if (MajorityIncludingSelfQuorum.IsQuorum(votingMembers.Count, total)) { return(entry.Key); } } // No majority for any appended entry return(-1); }
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes: //ORIGINAL LINE: @Test public void leaderShouldIgnoreSuccessResponseThatIndicatesLaggingWhileLocalStateIndicatesFollowerIsCaughtUp() throws Exception //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#: public virtual void LeaderShouldIgnoreSuccessResponseThatIndicatesLaggingWhileLocalStateIndicatesFollowerIsCaughtUp() { // given /* * A leader who * - has an append index of 100 * - knows about instance 2 * - assumes that instance 2 is fully caught up */ Leader leader = new Leader(); MemberId instance2 = member(2); int j = 100; FollowerState instance2State = CreateArtificialFollowerState(j); ReadableRaftState state = mock(typeof(ReadableRaftState)); FollowerStates <MemberId> followerState = new FollowerStates <MemberId>(); followerState = new FollowerStates <MemberId>(followerState, instance2, instance2State); ReadableRaftLog logMock = mock(typeof(ReadableRaftLog)); when(logMock.AppendIndex()).thenReturn(100L); // with commit requests in this test when(state.CommitIndex()).thenReturn(-1L); when(state.EntryLog()).thenReturn(logMock); when(state.FollowerStates()).thenReturn(followerState); when(state.Term()).thenReturn(4L); // both leader and follower are in the same term // when that leader is asked to handle a response from that follower that says that the follower is still // missing things Org.Neo4j.causalclustering.core.consensus.RaftMessages_AppendEntries_Response response = appendEntriesResponse().success().matchIndex(80).term(4).from(instance2).build(); Outcome outcome = leader.Handle(response, state, mock(typeof(Log))); // then the leader should not send anything, since this is a delayed, out of order response to a previous append // request assertTrue(outcome.OutgoingMessages.Count == 0); // The follower state should not be touched FollowerStates <MemberId> updatedFollowerStates = outcome.FollowerStates; assertEquals(100, updatedFollowerStates.Get(instance2).MatchIndex); }
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes: //ORIGINAL LINE: @Test public void leaderShouldNotRespondToSuccessResponseThatIndicatesUpToDateFollower() throws Exception //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#: public virtual void LeaderShouldNotRespondToSuccessResponseThatIndicatesUpToDateFollower() { // given /* * A leader who * - has an append index of 100 * - knows about instance 2 * - assumes that instance 2 is at an index less than 100 -say 84 */ Leader leader = new Leader(); MemberId instance2 = member(2); FollowerState instance2State = CreateArtificialFollowerState(84); ReadableRaftState state = mock(typeof(ReadableRaftState)); FollowerStates <MemberId> followerState = new FollowerStates <MemberId>(); followerState = new FollowerStates <MemberId>(followerState, instance2, instance2State); ReadableRaftLog logMock = mock(typeof(ReadableRaftLog)); when(logMock.AppendIndex()).thenReturn(100L); when(state.CommitIndex()).thenReturn(-1L); when(state.EntryLog()).thenReturn(logMock); when(state.FollowerStates()).thenReturn(followerState); when(state.Term()).thenReturn(4L); // both leader and follower are in the same term // when // that leader is asked to handle a response from that follower that says that the follower is up to date Org.Neo4j.causalclustering.core.consensus.RaftMessages_AppendEntries_Response response = appendEntriesResponse().success().matchIndex(100).term(4).from(instance2).build(); Outcome outcome = leader.Handle(response, state, mock(typeof(Log))); // then // The leader should not be trying to send any messages to that instance assertTrue(outcome.OutgoingMessages.Count == 0); // And the follower state should be updated FollowerStates <MemberId> updatedFollowerStates = outcome.FollowerStates; assertEquals(100, updatedFollowerStates.Get(instance2).MatchIndex); }
public override RaftMembershipStateMachineEventHandler OnFollowerStateChange(FollowerStates <MemberId> followerStates) { CatchupGoalTracker.updateProgress(followerStates.Get(outerInstance.catchingUpMember)); if (CatchupGoalTracker.Finished) { if (CatchupGoalTracker.GoalAchieved) { ISet <MemberId> updatedVotingMembers = new HashSet <MemberId>(outerInstance.membershipManager.VotingMembers()); updatedVotingMembers.Add(outerInstance.catchingUpMember); outerInstance.membershipManager.DoConsensus(updatedVotingMembers); MovingToConsensus = true; return(new ConsensusInProgress(_outerInstance)); } else { return(new Idle(_outerInstance)); } } return(this); }