use of io.atomix.protocols.raft.impl.PendingCommand 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.PendingCommand in project atomix by atomix.
the class LeaderRole method drainCommands.
/**
* Sequentially drains pending commands from the session's command request queue.
*
* @param session the session for which to drain commands
*/
private void drainCommands(RaftSession session) {
long nextSequence = session.nextRequestSequence();
PendingCommand nextCommand = session.removeCommand(nextSequence);
while (nextCommand != null) {
commitCommand(nextCommand.request(), nextCommand.future());
session.setRequestSequence(nextSequence);
nextSequence = session.nextRequestSequence();
nextCommand = session.removeCommand(nextSequence);
}
}
Aggregations