Search in sources :

Example 1 with CommandResponse

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);
}
Also used : CompletableFuture(java.util.concurrent.CompletableFuture) RaftSession(io.atomix.protocols.raft.session.RaftSession) OperationResult(io.atomix.protocols.raft.impl.OperationResult) CommandResponse(io.atomix.protocols.raft.protocol.CommandResponse) PendingCommand(io.atomix.protocols.raft.impl.PendingCommand)

Example 2 with CommandResponse

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());
}
Also used : PrimitiveOperation(io.atomix.primitive.operation.PrimitiveOperation) RaftException(io.atomix.protocols.raft.RaftException) ThreadContext(io.atomix.utils.concurrent.ThreadContext) CommandResponse(io.atomix.protocols.raft.protocol.CommandResponse) CompletableFuture(java.util.concurrent.CompletableFuture) CommandRequest(io.atomix.protocols.raft.protocol.CommandRequest) Test(org.junit.Test)

Example 3 with CommandResponse

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);
}
Also used : AtomicInteger(java.util.concurrent.atomic.AtomicInteger) CommandResponse(io.atomix.protocols.raft.protocol.CommandResponse) PublishRequest(io.atomix.protocols.raft.protocol.PublishRequest) Test(org.junit.Test)

Example 4 with CommandResponse

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);
}
Also used : AtomicInteger(java.util.concurrent.atomic.AtomicInteger) CommandResponse(io.atomix.protocols.raft.protocol.CommandResponse) PublishRequest(io.atomix.protocols.raft.protocol.PublishRequest) Test(org.junit.Test)

Example 5 with CommandResponse

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);
}
Also used : AtomicInteger(java.util.concurrent.atomic.AtomicInteger) CommandResponse(io.atomix.protocols.raft.protocol.CommandResponse) PublishRequest(io.atomix.protocols.raft.protocol.PublishRequest) Test(org.junit.Test)

Aggregations

CommandResponse (io.atomix.protocols.raft.protocol.CommandResponse)8 Test (org.junit.Test)7 PublishRequest (io.atomix.protocols.raft.protocol.PublishRequest)4 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)4 CompletableFuture (java.util.concurrent.CompletableFuture)3 PrimitiveOperation (io.atomix.primitive.operation.PrimitiveOperation)2 ThreadContext (io.atomix.utils.concurrent.ThreadContext)2 RaftException (io.atomix.protocols.raft.RaftException)1 OperationResult (io.atomix.protocols.raft.impl.OperationResult)1 PendingCommand (io.atomix.protocols.raft.impl.PendingCommand)1 CommandRequest (io.atomix.protocols.raft.protocol.CommandRequest)1 QueryResponse (io.atomix.protocols.raft.protocol.QueryResponse)1 RaftSession (io.atomix.protocols.raft.session.RaftSession)1 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)1