use of io.atomix.protocols.raft.protocol.CommandResponse in project atomix by atomix.
the class LeaderRole method onCommand.
@Override
public CompletableFuture<CommandResponse> onCommand(final CommandRequest request) {
raft.checkThread();
logRequest(request);
if (transferring) {
return CompletableFuture.completedFuture(logResponse(CommandResponse.builder().withStatus(RaftResponse.Status.ERROR).withError(RaftError.Type.ILLEGAL_MEMBER_STATE).build()));
}
// Get the client's server session. If the session doesn't exist, return an unknown session error.
RaftSession session = raft.getSessions().getSession(request.session());
if (session == null) {
return CompletableFuture.completedFuture(logResponse(CommandResponse.builder().withStatus(RaftResponse.Status.ERROR).withError(RaftError.Type.UNKNOWN_SESSION).build()));
}
final long sequenceNumber = request.sequenceNumber();
// If a command with the given sequence number is already pending, return the existing future to ensure
// duplicate requests aren't committed as duplicate entries in the log.
PendingCommand existingCommand = session.getCommand(sequenceNumber);
if (existingCommand != null) {
if (sequenceNumber == session.nextRequestSequence()) {
session.removeCommand(sequenceNumber);
commitCommand(existingCommand.request(), existingCommand.future());
session.setRequestSequence(sequenceNumber);
drainCommands(session);
}
log.trace("Returning pending result for command sequence {}", sequenceNumber);
return existingCommand.future();
}
final CompletableFuture<CommandResponse> future = new CompletableFuture<>();
// to force it to be resent by the client.
if (sequenceNumber > session.nextRequestSequence()) {
if (session.getCommands().size() < MAX_PENDING_COMMANDS) {
log.trace("Registered sequence command {} > {}", sequenceNumber, session.nextRequestSequence());
session.registerCommand(request.sequenceNumber(), new PendingCommand(request, future));
return future;
} else {
return CompletableFuture.completedFuture(logResponse(CommandResponse.builder().withStatus(RaftResponse.Status.ERROR).withError(RaftError.Type.COMMAND_FAILURE).withLastSequence(session.getRequestSequence()).build()));
}
}
// return null.
if (sequenceNumber <= session.getCommandSequence()) {
OperationResult result = session.getResult(sequenceNumber);
if (result != null) {
completeOperation(result, CommandResponse.builder(), null, future);
} else {
future.complete(CommandResponse.builder().withStatus(RaftResponse.Status.ERROR).withError(RaftError.Type.PROTOCOL_ERROR).build());
}
} else // Otherwise, commit the command and update the request sequence number.
{
commitCommand(request, future);
session.setRequestSequence(sequenceNumber);
}
return future.thenApply(this::logResponse);
}
use of io.atomix.protocols.raft.protocol.CommandResponse in project atomix by atomix.
the class RaftProxyInvokerTest method testExpireSessionOnCommandFailure.
/**
* Tests that the client's session is expired when an UnknownSessionException is received from the cluster.
*/
@Test
public void testExpireSessionOnCommandFailure() throws Throwable {
CompletableFuture<CommandResponse> future = new CompletableFuture<>();
RaftProxyConnection connection = mock(RaftProxyConnection.class);
Mockito.when(connection.command(any(CommandRequest.class))).thenReturn(future);
RaftProxyState state = new RaftProxyState("test", SessionId.from(1), UUID.randomUUID().toString(), new TestPrimitiveType(), 1000);
RaftProxyManager manager = mock(RaftProxyManager.class);
ThreadContext threadContext = new TestContext();
RaftProxyInvoker submitter = new RaftProxyInvoker(connection, mock(RaftProxyConnection.class), state, new RaftProxySequencer(state), manager, threadContext);
CompletableFuture<byte[]> result = submitter.invoke(new PrimitiveOperation(COMMAND, HeapBytes.EMPTY));
assertEquals(state.getResponseIndex(), 1);
assertFalse(result.isDone());
future.completeExceptionally(new RaftException.UnknownSession("unknown session"));
assertTrue(result.isCompletedExceptionally());
}
use of io.atomix.protocols.raft.protocol.CommandResponse in project atomix by atomix.
the class RaftProxySequencerTest method testSequenceEventBeforeCommand.
/**
* Tests sequencing an event that arrives before a command response.
*/
@Test
public void testSequenceEventBeforeCommand() throws Throwable {
RaftProxySequencer sequencer = new RaftProxySequencer(new RaftProxyState("test", SessionId.from(1), UUID.randomUUID().toString(), new TestPrimitiveType(), 1000));
long sequence = sequencer.nextRequest();
PublishRequest request = PublishRequest.builder().withSession(1).withEventIndex(1).withPreviousIndex(0).withEvents(Collections.emptyList()).build();
CommandResponse response = CommandResponse.builder().withStatus(RaftResponse.Status.OK).withIndex(2).withEventIndex(1).build();
AtomicInteger run = new AtomicInteger();
sequencer.sequenceEvent(request, () -> assertEquals(run.getAndIncrement(), 0));
sequencer.sequenceResponse(sequence, response, () -> assertEquals(run.getAndIncrement(), 1));
assertEquals(run.get(), 2);
}
use of io.atomix.protocols.raft.protocol.CommandResponse in project atomix by atomix.
the class RaftProxySequencerTest method testSequenceEventAfterAllCommands.
/**
* Tests sequencing an event that arrives before a command response.
*/
@Test
public void testSequenceEventAfterAllCommands() throws Throwable {
RaftProxySequencer sequencer = new RaftProxySequencer(new RaftProxyState("test", SessionId.from(1), UUID.randomUUID().toString(), new TestPrimitiveType(), 1000));
long sequence = sequencer.nextRequest();
PublishRequest request1 = PublishRequest.builder().withSession(1).withEventIndex(2).withPreviousIndex(0).withEvents(Collections.emptyList()).build();
PublishRequest request2 = PublishRequest.builder().withSession(1).withEventIndex(3).withPreviousIndex(2).withEvents(Collections.emptyList()).build();
CommandResponse response = CommandResponse.builder().withStatus(RaftResponse.Status.OK).withIndex(2).withEventIndex(2).build();
AtomicInteger run = new AtomicInteger();
sequencer.sequenceEvent(request1, () -> assertEquals(run.getAndIncrement(), 0));
sequencer.sequenceEvent(request2, () -> assertEquals(run.getAndIncrement(), 2));
sequencer.sequenceResponse(sequence, response, () -> assertEquals(run.getAndIncrement(), 1));
assertEquals(run.get(), 3);
}
use of io.atomix.protocols.raft.protocol.CommandResponse in project atomix by atomix.
the class RaftProxySequencerTest method testSequenceEventAfterCommand.
/**
* Tests sequencing an event that arrives before a command response.
*/
@Test
public void testSequenceEventAfterCommand() throws Throwable {
RaftProxySequencer sequencer = new RaftProxySequencer(new RaftProxyState("test", SessionId.from(1), UUID.randomUUID().toString(), new TestPrimitiveType(), 1000));
long sequence = sequencer.nextRequest();
PublishRequest request = PublishRequest.builder().withSession(1).withEventIndex(1).withPreviousIndex(0).withEvents(Collections.emptyList()).build();
CommandResponse response = CommandResponse.builder().withStatus(RaftResponse.Status.OK).withIndex(2).withEventIndex(1).build();
AtomicInteger run = new AtomicInteger();
sequencer.sequenceResponse(sequence, response, () -> assertEquals(run.getAndIncrement(), 0));
sequencer.sequenceEvent(request, () -> assertEquals(run.getAndIncrement(), 1));
assertEquals(run.get(), 2);
}
Aggregations