io.atomix.protocols.raft.storage.log.entry.CommandEntry Java Examples

The following examples show how to use io.atomix.protocols.raft.storage.log.entry.CommandEntry. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: RaftServiceManager.java    From atomix with Apache License 2.0 5 votes vote down vote up
/**
 * Applies a command entry to the state machine.
 * <p>
 * Command entries result in commands being executed on the user provided {@link PrimitiveService} and a response
 * being sent back to the client by completing the returned future. All command responses are cached in the command's
 * {@link RaftSession} for fault tolerance. In the event that the same command is applied to the state machine more
 * than once, the original response will be returned.
 * <p>
 * Command entries are written with a sequence number. The sequence number is used to ensure that commands are applied
 * to the state machine in sequential order. If a command entry has a sequence number that is less than the next
 * sequence number for the session, that indicates that it is a duplicate of a command that was already applied.
 * Otherwise, commands are assumed to have been received in sequential order. The reason for this assumption is
 * because leaders always sequence commands as they're written to the log, so no sequence number will be skipped.
 */
private OperationResult applyCommand(Indexed<CommandEntry> entry) {
  // First check to ensure that the session exists.
  RaftSession session = raft.getSessions().getSession(entry.entry().session());

  // If the session is null, return an UnknownSessionException. Commands applied to the state machine must
  // have a session. We ensure that session register/unregister entries are not compacted from the log
  // until all associated commands have been cleaned.
  // Note that it's possible for a session to be unknown if a later snapshot has been taken, so we don't want
  // to log warnings here.
  if (session == null) {
    logger.debug("Unknown session: " + entry.entry().session());
    throw new RaftException.UnknownSession("unknown session: " + entry.entry().session());
  }

  // Increment the load counter to avoid snapshotting under high load.
  raft.getLoadMonitor().recordEvent();

  // Execute the command using the state machine associated with the session.
  return session.getService()
      .executeCommand(
          entry.index(),
          entry.entry().sequenceNumber(),
          entry.entry().timestamp(),
          session,
          entry.entry().operation());
}
 
Example #2
Source File: RaftServiceManagerTest.java    From atomix with Apache License 2.0 5 votes vote down vote up
@Test
public void testInstallSnapshotOnApply() throws Exception {
  RaftLogWriter writer = raft.getLogWriter();
  writer.append(new InitializeEntry(1, System.currentTimeMillis()));
  writer.append(new OpenSessionEntry(
      1,
      System.currentTimeMillis(),
      "test-1",
      "test",
      "test",
      null,
      ReadConsistency.LINEARIZABLE,
      100,
      1000));
  writer.commit(2);

  RaftServiceManager manager = raft.getServiceManager();

  manager.apply(2).join();

  Snapshot snapshot = manager.snapshot();
  assertEquals(2, snapshot.index());
  assertTrue(snapshotTaken.get());

  snapshot.complete();

  assertEquals(2, raft.getSnapshotStore().getCurrentSnapshot().index());

  writer.append(new CommandEntry(1, System.currentTimeMillis(), 2, 1, new PrimitiveOperation(RUN, new byte[0])));
  writer.commit(3);

  manager.apply(3).join();
  assertTrue(snapshotInstalled.get());
}
 
Example #3
Source File: LeaderRole.java    From atomix with Apache License 2.0 4 votes vote down vote up
/**
 * Commits a command.
 *
 * @param request the command request
 * @param future  the command response future
 */
private void commitCommand(CommandRequest request, CompletableFuture<CommandResponse> future) {
  final long term = raft.getTerm();
  final long timestamp = System.currentTimeMillis();

  CommandEntry command = new CommandEntry(term, timestamp, request.session(), request.sequenceNumber(), request.operation());
  appendAndCompact(command)
      .whenCompleteAsync((entry, error) -> {
        if (error != null) {
          Throwable cause = Throwables.getRootCause(error);
          if (Throwables.getRootCause(error) instanceof StorageException.TooLarge) {
            log.warn("Failed to append command {}", command, cause);
            future.complete(CommandResponse.builder()
                .withStatus(RaftResponse.Status.ERROR)
                .withError(RaftError.Type.PROTOCOL_ERROR)
                .build());
          } else {
            future.complete(CommandResponse.builder()
                .withStatus(RaftResponse.Status.ERROR)
                .withError(RaftError.Type.COMMAND_FAILURE)
                .build());
          }
          return;
        }

        // Replicate the command to followers.
        appender.appendEntries(entry.index()).whenComplete((commitIndex, commitError) -> {
          raft.checkThread();
          if (isRunning()) {
            // If the command was successfully committed, apply it to the state machine.
            if (commitError == null) {
              raft.getServiceManager().<OperationResult>apply(entry.index()).whenComplete((r, e) -> {
                completeOperation(r, CommandResponse.builder(), e, future);
              });
            } else {
              future.complete(CommandResponse.builder()
                  .withStatus(RaftResponse.Status.ERROR)
                  .withError(RaftError.Type.COMMAND_FAILURE)
                  .build());
            }
          } else {
            future.complete(CommandResponse.builder()
                .withStatus(RaftResponse.Status.ERROR)
                .withError(RaftError.Type.COMMAND_FAILURE)
                .build());
          }
        });
      }, raft.getThreadContext());
}