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);
}
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;
}
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);
}
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());
}
Aggregations