Search in sources :

Example 1 with OperationResult

use of io.atomix.protocols.raft.impl.OperationResult 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 OperationResult

use of io.atomix.protocols.raft.impl.OperationResult in project atomix by atomix.

the class RaftServiceContext method applyCommand.

/**
 * Applies the given commit to the state machine.
 */
private OperationResult applyCommand(long index, long sequence, long timestamp, PrimitiveOperation operation, RaftSession session) {
    Commit<byte[]> commit = new DefaultCommit<>(index, operation.id(), operation.value(), session, timestamp);
    long eventIndex = session.getEventIndex();
    OperationResult result;
    try {
        currentSession = session;
        // Execute the state machine operation and get the result.
        byte[] output = service.apply(commit);
        // Store the result for linearizability and complete the command.
        result = OperationResult.succeeded(index, eventIndex, output);
    } catch (Exception e) {
        // If an exception occurs during execution of the command, store the exception.
        result = OperationResult.failed(index, eventIndex, e);
    } finally {
        currentSession = null;
    }
    // Once the operation has been applied to the state machine, commit events published by the command.
    // The state machine context will build a composite future for events published to all sessions.
    commit();
    // Register the result in the session to ensure retries receive the same output for the command.
    session.registerResult(sequence, result);
    // Update the session timestamp and command sequence number.
    session.setCommandSequence(sequence);
    // Complete the command.
    return result;
}
Also used : OperationResult(io.atomix.protocols.raft.impl.OperationResult) DefaultCommit(io.atomix.primitive.service.impl.DefaultCommit) RaftException(io.atomix.protocols.raft.RaftException)

Example 3 with OperationResult

use of io.atomix.protocols.raft.impl.OperationResult in project atomix by atomix.

the class RaftServiceContext method applyQuery.

/**
 * Applies a query to the state machine.
 */
private void applyQuery(long timestamp, RaftSession session, PrimitiveOperation operation, CompletableFuture<OperationResult> future) {
    // If the session is not open, fail the request.
    if (!session.getState().active()) {
        log.warn("Inactive session: " + session.sessionId());
        future.completeExceptionally(new RaftException.UnknownSession("Unknown session: " + session.sessionId()));
        return;
    }
    // Set the current operation type to QUERY to prevent events from being sent to clients.
    setOperation(OperationType.QUERY);
    Commit<byte[]> commit = new DefaultCommit<>(currentIndex, operation.id(), operation.value(), session, timestamp);
    long eventIndex = session.getEventIndex();
    OperationResult result;
    try {
        currentSession = session;
        result = OperationResult.succeeded(currentIndex, eventIndex, service.apply(commit));
    } catch (Exception e) {
        result = OperationResult.failed(currentIndex, eventIndex, e);
    } finally {
        currentSession = null;
    }
    future.complete(result);
}
Also used : RaftException(io.atomix.protocols.raft.RaftException) OperationResult(io.atomix.protocols.raft.impl.OperationResult) DefaultCommit(io.atomix.primitive.service.impl.DefaultCommit) RaftException(io.atomix.protocols.raft.RaftException)

Example 4 with OperationResult

use of io.atomix.protocols.raft.impl.OperationResult in project atomix by atomix.

the class LeaderRole method configure.

/**
 * Commits the given configuration.
 */
protected CompletableFuture<Long> configure(Collection<RaftMember> members) {
    raft.checkThread();
    final long term = raft.getTerm();
    return appendAndCompact(new ConfigurationEntry(term, System.currentTimeMillis(), members)).thenComposeAsync(entry -> {
        // Store the index of the configuration entry in order to prevent other configurations from
        // being logged and committed concurrently. This is an important safety property of Raft.
        configuring = entry.index();
        raft.getCluster().configure(new Configuration(entry.index(), entry.entry().term(), entry.entry().timestamp(), entry.entry().members()));
        return appender.appendEntries(entry.index()).whenComplete((commitIndex, commitError) -> {
            raft.checkThread();
            if (isRunning() && commitError == null) {
                raft.getServiceManager().<OperationResult>apply(entry.index());
            }
            configuring = 0;
        });
    }, raft.getThreadContext());
}
Also used : Configuration(io.atomix.protocols.raft.storage.system.Configuration) OperationResult(io.atomix.protocols.raft.impl.OperationResult) ConfigurationEntry(io.atomix.protocols.raft.storage.log.entry.ConfigurationEntry)

Aggregations

OperationResult (io.atomix.protocols.raft.impl.OperationResult)4 DefaultCommit (io.atomix.primitive.service.impl.DefaultCommit)2 RaftException (io.atomix.protocols.raft.RaftException)2 PendingCommand (io.atomix.protocols.raft.impl.PendingCommand)1 CommandResponse (io.atomix.protocols.raft.protocol.CommandResponse)1 RaftSession (io.atomix.protocols.raft.session.RaftSession)1 ConfigurationEntry (io.atomix.protocols.raft.storage.log.entry.ConfigurationEntry)1 Configuration (io.atomix.protocols.raft.storage.system.Configuration)1 CompletableFuture (java.util.concurrent.CompletableFuture)1