io.atomix.protocols.raft.protocol.AppendRequest Java Examples

The following examples show how to use io.atomix.protocols.raft.protocol.AppendRequest. 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: PassiveRole.java    From atomix with Apache License 2.0 6 votes vote down vote up
/**
 * Handles an AppendRequest.
 */
protected CompletableFuture<AppendResponse> handleAppend(final AppendRequest request) {
  CompletableFuture<AppendResponse> future = new CompletableFuture<>();

  // Check that the term of the given request matches the local term or update the term.
  if (!checkTerm(request, future)) {
    return future;
  }

  // Check that the previous index/term matches the local log's last entry.
  if (!checkPreviousEntry(request, future)) {
    return future;
  }

  // Append the entries to the log.
  appendEntries(request, future);
  return future;
}
 
Example #2
Source File: ActiveRole.java    From atomix with Apache License 2.0 6 votes vote down vote up
@Override
public CompletableFuture<AppendResponse> onAppend(final AppendRequest request) {
  raft.checkThread();
  logRequest(request);

  // If the request indicates a term that is greater than the current term then
  // assign that term and leader to the current context and transition to follower.
  boolean transition = updateTermAndLeader(request.term(), request.leader());

  // Handle the append request.
  CompletableFuture<AppendResponse> future = handleAppend(request);

  // If a transition is required then transition back to the follower state.
  // If the node is already a follower then the transition will be ignored.
  if (transition) {
    raft.transition(RaftServer.Role.FOLLOWER);
  }
  return future;
}
 
Example #3
Source File: LeaderRole.java    From atomix with Apache License 2.0 6 votes vote down vote up
@Override
public CompletableFuture<AppendResponse> onAppend(final AppendRequest request) {
  raft.checkThread();
  if (updateTermAndLeader(request.term(), request.leader())) {
    CompletableFuture<AppendResponse> future = super.onAppend(request);
    raft.transition(RaftServer.Role.FOLLOWER);
    return future;
  } else if (request.term() < raft.getTerm()) {
    logRequest(request);
    return CompletableFuture.completedFuture(logResponse(AppendResponse.builder()
        .withStatus(RaftResponse.Status.OK)
        .withTerm(raft.getTerm())
        .withSucceeded(false)
        .withLastLogIndex(raft.getLogWriter().getLastIndex())
        .withLastSnapshotIndex(raft.getSnapshotStore().getCurrentSnapshotIndex())
        .build()));
  } else {
    raft.setLeader(request.leader());
    raft.transition(RaftServer.Role.FOLLOWER);
    return super.onAppend(request);
  }
}
 
Example #4
Source File: AbstractAppender.java    From atomix with Apache License 2.0 6 votes vote down vote up
/**
 * Builds an append request.
 *
 * @param member The member to which to send the request.
 * @return The append request.
 */
protected AppendRequest buildAppendRequest(RaftMemberContext member, long lastIndex) {
  final RaftLogReader reader = member.getLogReader();

  // If the log is empty then send an empty commit.
  // If the next index hasn't yet been set then we send an empty commit first.
  // If the next index is greater than the last index then send an empty commit.
  // If the member failed to respond to recent communication send an empty commit. This
  // helps avoid doing expensive work until we can ascertain the member is back up.
  if (!reader.hasNext()) {
    return buildAppendEmptyRequest(member);
  } else if (member.getFailureCount() > 0) {
    return buildAppendEmptyRequest(member);
  } else {
    return buildAppendEntriesRequest(member, lastIndex);
  }
}
 
Example #5
Source File: AbstractAppender.java    From atomix with Apache License 2.0 6 votes vote down vote up
/**
 * Builds an empty AppendEntries request.
 * <p>
 * Empty append requests are used as heartbeats to followers.
 */
protected AppendRequest buildAppendEmptyRequest(RaftMemberContext member) {
  final RaftLogReader reader = member.getLogReader();

  // Read the previous entry from the reader.
  // The reader can be null for RESERVE members.
  Indexed<RaftLogEntry> prevEntry = reader != null ? reader.getCurrentEntry() : null;

  DefaultRaftMember leader = raft.getLeader();
  return AppendRequest.builder()
      .withTerm(raft.getTerm())
      .withLeader(leader != null ? leader.memberId() : null)
      .withPrevLogIndex(prevEntry != null ? prevEntry.index() : reader != null ? reader.getFirstIndex() - 1 : 0)
      .withPrevLogTerm(prevEntry != null ? prevEntry.entry().term() : 0)
      .withEntries(Collections.emptyList())
      .withCommitIndex(raft.getCommitIndex())
      .build();
}
 
Example #6
Source File: LeaderAppender.java    From atomix with Apache License 2.0 5 votes vote down vote up
@Override
protected void handleAppendResponseOk(RaftMemberContext member, AppendRequest request, AppendResponse response) {
  // Reset the member failure count and update the member's availability status if necessary.
  succeedAttempt(member);

  // If replication succeeded then trigger commit futures.
  if (response.succeeded()) {
    member.appendSucceeded();
    updateMatchIndex(member, response);

    // If entries were committed to the replica then check commit indexes.
    if (!request.entries().isEmpty()) {
      commitEntries();
    }

    // If there are more entries to send then attempt to send another commit.
    if (hasMoreEntries(member)) {
      appendEntries(member);
    }
  }
  // If we've received a greater term, update the term and transition back to follower.
  else if (response.term() > raft.getTerm()) {
    raft.setTerm(response.term());
    raft.setLeader(null);
    raft.transition(RaftServer.Role.FOLLOWER);
  }
  // If the response failed, the follower should have provided the correct last index in their log. This helps
  // us converge on the matchIndex faster than by simply decrementing nextIndex one index at a time.
  else {
    member.appendFailed();
    resetMatchIndex(member, response);
    resetNextIndex(member, response);
    resetSnapshotIndex(member, response);

    // If there are more entries to send then attempt to send another commit.
    if (hasMoreEntries(member)) {
      appendEntries(member);
    }
  }
}
 
Example #7
Source File: LeaderAppender.java    From atomix with Apache License 2.0 5 votes vote down vote up
/**
 * Handles a {@link io.atomix.protocols.raft.protocol.RaftResponse.Status#ERROR} response.
 */
protected void handleAppendResponseError(RaftMemberContext member, AppendRequest request, AppendResponse response) {
  // If we've received a greater term, update the term and transition back to follower.
  if (response.term() > raft.getTerm()) {
    log.debug("Received higher term from {}", member.getMember().memberId());
    raft.setTerm(response.term());
    raft.setLeader(null);
    raft.transition(RaftServer.Role.FOLLOWER);
  } else {
    super.handleAppendResponseError(member, request, response);
  }
}
 
Example #8
Source File: PassiveRole.java    From atomix with Apache License 2.0 5 votes vote down vote up
@Override
public CompletableFuture<AppendResponse> onAppend(final AppendRequest request) {
  raft.checkThread();
  logRequest(request);
  updateTermAndLeader(request.term(), request.leader());
  return handleAppend(request);
}
 
Example #9
Source File: AbstractAppender.java    From atomix with Apache License 2.0 5 votes vote down vote up
/**
 * Handles a {@link RaftResponse.Status#ERROR} response.
 */
protected void handleAppendResponseError(RaftMemberContext member, AppendRequest request, AppendResponse response) {
  // If any other error occurred, increment the failure count for the member. Log the first three failures,
  // and thereafter log 1% of the failures. This keeps the log from filling up with annoying error messages
  // when attempting to send entries to down followers.
  int failures = member.incrementFailureCount();
  if (failures <= 3 || failures % 100 == 0) {
    log.debug("{} to {} failed: {}", request, member.getMember().memberId(), response.error() != null ? response.error() : "");
  }
}
 
Example #10
Source File: AbstractAppender.java    From atomix with Apache License 2.0 5 votes vote down vote up
/**
 * Handles a {@link RaftResponse.Status#OK} response.
 */
protected void handleAppendResponseOk(RaftMemberContext member, AppendRequest request, AppendResponse response) {
  // Reset the member failure count and update the member's availability status if necessary.
  succeedAttempt(member);

  // If replication succeeded then trigger commit futures.
  if (response.succeeded()) {
    updateMatchIndex(member, response);

    // If there are more entries to send then attempt to send another commit.
    if (request.prevLogIndex() != response.lastLogIndex() && hasMoreEntries(member)) {
      appendEntries(member);
    }
  }
  // If we've received a greater term, update the term and transition back to follower.
  else if (response.term() > raft.getTerm()) {
    raft.setTerm(response.term());
    raft.setLeader(null);
    raft.transition(RaftServer.Role.FOLLOWER);
  }
  // If the response failed, the follower should have provided the correct last index in their log. This helps
  // us converge on the matchIndex faster than by simply decrementing nextIndex one index at a time.
  else {
    resetMatchIndex(member, response);
    resetNextIndex(member, response);
    resetSnapshotIndex(member, response);

    // If there are more entries to send then attempt to send another commit.
    if (response.lastLogIndex() != request.prevLogIndex() && hasMoreEntries(member)) {
      appendEntries(member);
    }
  }
}
 
Example #11
Source File: AbstractAppender.java    From atomix with Apache License 2.0 5 votes vote down vote up
/**
 * Handles an append response.
 */
protected void handleAppendResponse(RaftMemberContext member, AppendRequest request, AppendResponse response, long timestamp) {
  if (response.status() == RaftResponse.Status.OK) {
    handleAppendResponseOk(member, request, response);
  } else {
    handleAppendResponseError(member, request, response);
  }
}
 
Example #12
Source File: PassiveRole.java    From atomix with Apache License 2.0 5 votes vote down vote up
/**
 * Checks the leader's term of the given AppendRequest, returning a boolean indicating whether to continue
 * handling the request.
 */
protected boolean checkTerm(AppendRequest request, CompletableFuture<AppendResponse> future) {
  RaftLogWriter writer = raft.getLogWriter();
  if (request.term() < raft.getTerm()) {
    log.debug("Rejected {}: request term is less than the current term ({})", request, raft.getTerm());
    return failAppend(writer.getLastIndex(), future);
  }
  return true;
}
 
Example #13
Source File: AbstractAppender.java    From atomix with Apache License 2.0 5 votes vote down vote up
/**
 * Connects to the member and sends a commit message.
 */
protected void sendAppendRequest(RaftMemberContext member, AppendRequest request) {
  // If this is a heartbeat message and a heartbeat is already in progress, skip the request.
  if (request.entries().isEmpty() && !member.canHeartbeat()) {
    return;
  }

  // Start the append to the member.
  member.startAppend();

  long timestamp = System.currentTimeMillis();

  log.trace("Sending {} to {}", request, member.getMember().memberId());
  raft.getProtocol().append(member.getMember().memberId(), request).whenCompleteAsync((response, error) -> {
    // Complete the append to the member.
    if (!request.entries().isEmpty()) {
      member.completeAppend(System.currentTimeMillis() - timestamp);
    } else {
      member.completeAppend();
    }

    if (open) {
      if (error == null) {
        log.trace("Received {} from {}", response, member.getMember().memberId());
        handleAppendResponse(member, request, response, timestamp);
      } else {
        handleAppendResponseFailure(member, request, error);
      }
    }
  }, raft.getThreadContext());

  if (!request.entries().isEmpty() && hasMoreEntries(member)) {
    appendEntries(member);
  }
}
 
Example #14
Source File: CandidateRole.java    From atomix with Apache License 2.0 5 votes vote down vote up
@Override
public CompletableFuture<AppendResponse> onAppend(AppendRequest request) {
  raft.checkThread();

  // If the request indicates a term that is greater than the current term then
  // assign that term and leader to the current context and step down as a candidate.
  if (request.term() >= raft.getTerm()) {
    raft.setTerm(request.term());
    raft.transition(RaftServer.Role.FOLLOWER);
  }
  return super.onAppend(request);
}
 
Example #15
Source File: InactiveRole.java    From atomix with Apache License 2.0 5 votes vote down vote up
@Override
public CompletableFuture<AppendResponse> onAppend(AppendRequest request) {
  logRequest(request);
  return Futures.completedFuture(logResponse(AppendResponse.builder()
      .withStatus(Status.ERROR)
      .withError(RaftError.Type.UNAVAILABLE)
      .build()));
}
 
Example #16
Source File: FollowerRole.java    From atomix with Apache License 2.0 5 votes vote down vote up
@Override
public CompletableFuture<AppendResponse> onAppend(AppendRequest request) {
  CompletableFuture<AppendResponse> future = super.onAppend(request);

  // Reset the heartbeat timeout.
  resetHeartbeatTimeout();
  return future;
}
 
Example #17
Source File: PassiveRole.java    From atomix with Apache License 2.0 4 votes vote down vote up
/**
 * Checks the previous index of the given AppendRequest, returning a boolean indicating whether to continue
 * handling the request.
 */
protected boolean checkPreviousEntry(AppendRequest request, CompletableFuture<AppendResponse> future) {
  RaftLogWriter writer = raft.getLogWriter();
  RaftLogReader reader = raft.getLogReader();

  // If the previous term is set, validate that it matches the local log.
  // We check the previous log term since that indicates whether any entry is present in the leader's
  // log at the previous log index. It's possible that the leader can send a non-zero previous log index
  // with a zero term in the event the leader has compacted its logs and is sending the first entry.
  if (request.prevLogTerm() != 0) {
    // Get the last entry written to the log.
    Indexed<RaftLogEntry> lastEntry = writer.getLastEntry();

    // If the local log is non-empty...
    if (lastEntry != null) {
      // If the previous log index is greater than the last entry index, fail the attempt.
      if (request.prevLogIndex() > lastEntry.index()) {
        log.debug("Rejected {}: Previous index ({}) is greater than the local log's last index ({})", request, request.prevLogIndex(), lastEntry.index());
        return failAppend(lastEntry.index(), future);
      }

      // If the previous log index is less than the last written entry index, look up the entry.
      if (request.prevLogIndex() < lastEntry.index()) {
        // Reset the reader to the previous log index.
        if (reader.getNextIndex() != request.prevLogIndex()) {
          reader.reset(request.prevLogIndex());
        }

        // The previous entry should exist in the log if we've gotten this far.
        if (!reader.hasNext()) {
          log.debug("Rejected {}: Previous entry does not exist in the local log", request);
          return failAppend(lastEntry.index(), future);
        }

        // Read the previous entry and validate that the term matches the request previous log term.
        Indexed<RaftLogEntry> previousEntry = reader.next();
        if (request.prevLogTerm() != previousEntry.entry().term()) {
          log.debug("Rejected {}: Previous entry term ({}) does not match local log's term for the same entry ({})", request, request.prevLogTerm(), previousEntry.entry().term());
          return failAppend(request.prevLogIndex() - 1, future);
        }
      }
      // If the previous log term doesn't equal the last entry term, fail the append, sending the prior entry.
      else if (request.prevLogTerm() != lastEntry.entry().term()) {
        log.debug("Rejected {}: Previous entry term ({}) does not equal the local log's last term ({})", request, request.prevLogTerm(), lastEntry.entry().term());
        return failAppend(request.prevLogIndex() - 1, future);
      }
    } else {
      // If the previous log index is set and the last entry is null, fail the append.
      if (request.prevLogIndex() > 0) {
        log.debug("Rejected {}: Previous index ({}) is greater than the local log's last index (0)", request, request.prevLogIndex());
        return failAppend(0, future);
      }
    }
  }
  return true;
}
 
Example #18
Source File: RaftServerMessagingProtocol.java    From atomix with Apache License 2.0 4 votes vote down vote up
@Override
public CompletableFuture<AppendResponse> append(MemberId memberId, AppendRequest request) {
  return sendAndReceive(memberId, "append", request);
}
 
Example #19
Source File: RaftServerMessagingProtocol.java    From atomix with Apache License 2.0 4 votes vote down vote up
@Override
public void registerAppendHandler(Function<AppendRequest, CompletableFuture<AppendResponse>> handler) {
  registerHandler("append", handler);
}
 
Example #20
Source File: LocalRaftServerProtocol.java    From atomix with Apache License 2.0 4 votes vote down vote up
@Override
public CompletableFuture<AppendResponse> append(MemberId memberId, AppendRequest request) {
  return getServer(memberId).thenCompose(listener -> listener.append(encode(request))).thenApply(this::decode);
}
 
Example #21
Source File: RaftServerMessagingProtocol.java    From submarine with Apache License 2.0 4 votes vote down vote up
@Override
public CompletableFuture<AppendResponse> append(MemberId memberId, AppendRequest request) {
  return sendAndReceive(memberId, "append", request);
}
 
Example #22
Source File: LocalRaftServerProtocol.java    From atomix with Apache License 2.0 4 votes vote down vote up
@Override
public void registerAppendHandler(Function<AppendRequest, CompletableFuture<AppendResponse>> handler) {
  this.appendHandler = handler;
}
 
Example #23
Source File: LeaderAppender.java    From atomix with Apache License 2.0 4 votes vote down vote up
@Override
protected void handleAppendResponse(RaftMemberContext member, AppendRequest request, AppendResponse response, long timestamp) {
  super.handleAppendResponse(member, request, response, timestamp);
  recordHeartbeat(member, timestamp);
}
 
Example #24
Source File: LeaderAppender.java    From atomix with Apache License 2.0 4 votes vote down vote up
@Override
protected void handleAppendResponseFailure(RaftMemberContext member, AppendRequest request, Throwable error) {
  failHeartbeat();
  super.handleAppendResponseFailure(member, request, error);
}
 
Example #25
Source File: AbstractAppender.java    From atomix with Apache License 2.0 4 votes vote down vote up
/**
 * Handles an append failure.
 */
protected void handleAppendResponseFailure(RaftMemberContext member, AppendRequest request, Throwable error) {
  // Log the failed attempt to contact the member.
  failAttempt(member, request, error);
}
 
Example #26
Source File: RaftServerCommunicator.java    From atomix with Apache License 2.0 4 votes vote down vote up
@Override
public void registerAppendHandler(Function<AppendRequest, CompletableFuture<AppendResponse>> handler) {
  clusterCommunicator.subscribe(context.appendSubject, serializer::decode, handler, serializer::encode);
}
 
Example #27
Source File: RaftServerCommunicator.java    From atomix with Apache License 2.0 4 votes vote down vote up
@Override
public CompletableFuture<AppendResponse> append(MemberId memberId, AppendRequest request) {
  return sendAndReceive(context.appendSubject, request, memberId);
}
 
Example #28
Source File: LocalRaftServerProtocol.java    From zeppelin with Apache License 2.0 4 votes vote down vote up
@Override
public void registerAppendHandler(Function<AppendRequest,
    CompletableFuture<AppendResponse>> handler) {
  this.appendHandler = handler;
}
 
Example #29
Source File: LocalRaftServerProtocol.java    From zeppelin with Apache License 2.0 4 votes vote down vote up
@Override
public CompletableFuture<AppendResponse> append(MemberId memberId, AppendRequest request) {
  return getServer(memberId).thenCompose(listener ->
      listener.append(encode(request))).thenApply(this::decode);
}
 
Example #30
Source File: RaftServerMessagingProtocol.java    From zeppelin with Apache License 2.0 4 votes vote down vote up
@Override
public void registerAppendHandler(Function<AppendRequest,
    CompletableFuture<AppendResponse>> handler) {
  registerHandler("append", handler);
}