use of org.neo4j.causalclustering.core.consensus.outcome.TruncateLogCommand in project neo4j by neo4j.
the class RaftMembershipManagerTest method membershipManagerShouldRevertToOldMembershipSetAfterTruncationCausesLossOfAllAppendedMembershipSets.
@Test
public void membershipManagerShouldRevertToOldMembershipSetAfterTruncationCausesLossOfAllAppendedMembershipSets() throws Exception {
// given
final InMemoryRaftLog raftLog = new InMemoryRaftLog();
RaftMembershipManager membershipManager = lifeRule.add(raftMembershipManager(raftLog));
// when
List<RaftLogCommand> logCommands = asList(new AppendLogEntry(0, new RaftLogEntry(0, new RaftTestGroup(1, 2, 3, 4))), new AppendLogEntry(1, new RaftLogEntry(0, new RaftTestGroup(1, 2, 3, 5))), new TruncateLogCommand(1));
for (RaftLogCommand logCommand : logCommands) {
logCommand.applyTo(raftLog, log);
}
membershipManager.processLog(0, logCommands);
// then
assertEquals(new RaftTestGroup(1, 2, 3, 4).getMembers(), membershipManager.votingMembers());
assertFalse(membershipManager.uncommittedMemberChangeInLog());
}
use of org.neo4j.causalclustering.core.consensus.outcome.TruncateLogCommand in project neo4j by neo4j.
the class RaftMembershipManagerTest method membershipManagerShouldRevertToEarlierAppendedMembershipSetAfterTruncationCausesLossOfLastAppened.
@Test
public void membershipManagerShouldRevertToEarlierAppendedMembershipSetAfterTruncationCausesLossOfLastAppened() throws Exception {
// given
final InMemoryRaftLog raftLog = new InMemoryRaftLog();
RaftMembershipManager membershipManager = lifeRule.add(raftMembershipManager(raftLog));
// when
List<RaftLogCommand> logCommands = asList(new AppendLogEntry(0, new RaftLogEntry(0, new RaftTestGroup(1, 2, 3, 4))), new AppendLogEntry(1, new RaftLogEntry(0, new RaftTestGroup(1, 2, 3, 5))), new AppendLogEntry(2, new RaftLogEntry(0, new RaftTestGroup(1, 2, 3, 6))), new TruncateLogCommand(2));
for (RaftLogCommand logCommand : logCommands) {
logCommand.applyTo(raftLog, log);
}
membershipManager.processLog(0, logCommands);
// then
assertEquals(new RaftTestGroup(1, 2, 3, 5).getMembers(), membershipManager.votingMembers());
}
use of org.neo4j.causalclustering.core.consensus.outcome.TruncateLogCommand in project neo4j by neo4j.
the class AppendEntriesRequestTest method shouldTruncateOnReceiptOfConflictingEntry.
@Test
public void shouldTruncateOnReceiptOfConflictingEntry() throws Exception {
// given
InMemoryRaftLog raftLog = new InMemoryRaftLog();
RaftState state = raftState().myself(myself).term(5).entryLog(raftLog).build();
long leaderTerm = state.term() + leaderTermDifference;
raftLog.append(new RaftLogEntry(state.term() - 1, content()));
raftLog.append(new RaftLogEntry(state.term() - 1, content()));
// when
long previousIndex = raftLog.appendIndex() - 1;
Outcome outcome = role.handler.handle(appendEntriesRequest().from(leader).leaderTerm(leaderTerm).prevLogIndex(previousIndex).prevLogTerm(raftLog.readEntryTerm(previousIndex)).logEntry(new RaftLogEntry(leaderTerm, content())).build(), state, log());
// then
assertTrue(((Response) messageFor(outcome, leader)).success());
assertThat(outcome.getLogCommands(), hasItem(new TruncateLogCommand(1)));
}
use of org.neo4j.causalclustering.core.consensus.outcome.TruncateLogCommand in project neo4j by neo4j.
the class RaftStateTest method shouldUpdateCacheState.
@Test
public void shouldUpdateCacheState() throws Exception {
//Test that updates applied to the raft state will be refelcted in the entry cache.
//given
InFlightMap<RaftLogEntry> cache = new InFlightMap<>();
RaftState raftState = new RaftState(member(0), new InMemoryStateStorage<>(new TermState()), new FakeMembership(), new InMemoryRaftLog(), new InMemoryStateStorage<>(new VoteState()), cache, NullLogProvider.getInstance());
List<RaftLogCommand> logCommands = new LinkedList<RaftLogCommand>() {
{
add(new AppendLogEntry(1, new RaftLogEntry(0L, valueOf(0))));
add(new AppendLogEntry(2, new RaftLogEntry(0L, valueOf(1))));
add(new AppendLogEntry(3, new RaftLogEntry(0L, valueOf(2))));
add(new AppendLogEntry(4, new RaftLogEntry(0L, valueOf(4))));
add(new TruncateLogCommand(3));
add(new AppendLogEntry(3, new RaftLogEntry(0L, valueOf(5))));
}
};
Outcome raftTestMemberOutcome = new Outcome(CANDIDATE, 0, null, -1, null, emptySet(), -1, initialFollowerStates(), true, logCommands, emptyOutgoingMessages(), emptySet(), -1, emptySet());
//when
raftState.update(raftTestMemberOutcome);
//then
assertNotNull(cache.get(1L));
assertNotNull(cache.get(2L));
assertNotNull(cache.get(3L));
assertEquals(valueOf(5), cache.get(3L).content());
assertNull(cache.get(4L));
}
use of org.neo4j.causalclustering.core.consensus.outcome.TruncateLogCommand in project neo4j by neo4j.
the class Appending method handleAppendEntriesRequest.
static void handleAppendEntriesRequest(ReadableRaftState state, Outcome outcome, RaftMessages.AppendEntries.Request request, Log log) throws IOException {
if (request.leaderTerm() < state.term()) {
RaftMessages.AppendEntries.Response appendResponse = new RaftMessages.AppendEntries.Response(state.myself(), state.term(), false, -1, state.entryLog().appendIndex());
outcome.addOutgoingMessage(new RaftMessages.Directed(request.from(), appendResponse));
return;
}
outcome.renewElectionTimeout();
outcome.setNextTerm(request.leaderTerm());
outcome.setLeader(request.from());
outcome.setLeaderCommit(request.leaderCommit());
if (!Follower.logHistoryMatches(state, request.prevLogIndex(), request.prevLogTerm(), log)) {
assert request.prevLogIndex() > -1 && request.prevLogTerm() > -1;
RaftMessages.AppendEntries.Response appendResponse = new RaftMessages.AppendEntries.Response(state.myself(), request.leaderTerm(), false, -1, state.entryLog().appendIndex());
outcome.addOutgoingMessage(new RaftMessages.Directed(request.from(), appendResponse));
return;
}
long baseIndex = request.prevLogIndex() + 1;
int offset;
/* Find possible truncation point. */
for (offset = 0; offset < request.entries().length; offset++) {
long logIndex = baseIndex + offset;
long logTerm = state.entryLog().readEntryTerm(logIndex);
if (logIndex > state.entryLog().appendIndex()) {
// entry doesn't exist because it's beyond the current log end, so we can go ahead and append
break;
} else if (logIndex < state.entryLog().prevIndex()) {
// entry doesn't exist because it's before the earliest known entry, so continue with the next one
continue;
} else if (logTerm != request.entries()[offset].term()) {
/*
* the entry's index falls within our current range and the term doesn't match what we know. We must
* truncate.
*/
if (// first, assert that we haven't committed what we are about to truncate
logIndex <= state.commitIndex()) {
throw new IllegalStateException(format("Cannot truncate entry at index %d with term %d when commit index is at %d", logIndex, logTerm, state.commitIndex()));
}
outcome.addLogCommand(new TruncateLogCommand(logIndex));
break;
}
}
if (offset < request.entries().length) {
outcome.addLogCommand(new BatchAppendLogEntries(baseIndex, offset, request.entries()));
}
Follower.commitToLogOnUpdate(state, request.prevLogIndex() + request.entries().length, request.leaderCommit(), outcome);
// this is the index of the last incoming entry
long endMatchIndex = request.prevLogIndex() + request.entries().length;
RaftMessages.AppendEntries.Response appendResponse = new RaftMessages.AppendEntries.Response(state.myself(), request.leaderTerm(), true, endMatchIndex, endMatchIndex);
outcome.addOutgoingMessage(new RaftMessages.Directed(request.from(), appendResponse));
}
Aggregations