use of org.apache.ratis.server.impl.RetryCacheImpl.CacheEntry in project incubator-ratis by apache.
the class RaftServerImpl method submitClientRequestAsync.
@Override
public CompletableFuture<RaftClientReply> submitClientRequestAsync(RaftClientRequest request) throws IOException {
assertLifeCycleState(LifeCycle.States.RUNNING);
LOG.debug("{}: receive client request({})", getMemberId(), request);
final Optional<Timer> timer = Optional.ofNullable(raftServerMetrics.getClientRequestTimer(request.getType()));
final CompletableFuture<RaftClientReply> replyFuture;
if (request.is(TypeCase.STALEREAD)) {
replyFuture = staleReadAsync(request);
} else {
// first check the server's leader state
CompletableFuture<RaftClientReply> reply = checkLeaderState(request, null, !request.is(TypeCase.READ) && !request.is(TypeCase.WATCH));
if (reply != null) {
return reply;
}
// let the state machine handle read-only request from client
RaftClientRequest.Type type = request.getType();
if (type.is(TypeCase.MESSAGESTREAM)) {
if (type.getMessageStream().getEndOfRequest()) {
final CompletableFuture<RaftClientRequest> f = streamEndOfRequestAsync(request);
if (f.isCompletedExceptionally()) {
return f.thenApply(r -> null);
}
request = f.join();
type = request.getType();
}
}
if (type.is(TypeCase.READ)) {
// TODO: We might not be the leader anymore by the time this completes.
// See the RAFT paper section 8 (last part)
replyFuture = processQueryFuture(stateMachine.query(request.getMessage()), request);
} else if (type.is(TypeCase.WATCH)) {
replyFuture = watchAsync(request);
} else if (type.is(TypeCase.MESSAGESTREAM)) {
replyFuture = streamAsync(request);
} else {
// query the retry cache
final RetryCacheImpl.CacheQueryResult queryResult = retryCache.queryCache(ClientInvocationId.valueOf(request));
final CacheEntry cacheEntry = queryResult.getEntry();
if (queryResult.isRetry()) {
// if the previous attempt is still pending or it succeeded, return its
// future
replyFuture = cacheEntry.getReplyFuture();
} else {
// TODO: this client request will not be added to pending requests until
// later which means that any failure in between will leave partial state in
// the state machine. We should call cancelTransaction() for failed requests
TransactionContext context = stateMachine.startTransaction(filterDataStreamRaftClientRequest(request));
if (context.getException() != null) {
final StateMachineException e = new StateMachineException(getMemberId(), context.getException());
final RaftClientReply exceptionReply = newExceptionReply(request, e);
cacheEntry.failWithReply(exceptionReply);
replyFuture = CompletableFuture.completedFuture(exceptionReply);
} else {
replyFuture = appendTransaction(request, context, cacheEntry);
}
}
}
}
final RaftClientRequest.Type type = request.getType();
replyFuture.whenComplete((clientReply, exception) -> {
if (clientReply.isSuccess()) {
timer.map(Timer::time).ifPresent(Timer.Context::stop);
}
if (exception != null || clientReply.getException() != null) {
raftServerMetrics.incFailedRequestCount(type);
}
});
return replyFuture;
}
use of org.apache.ratis.server.impl.RetryCacheImpl.CacheEntry in project incubator-ratis by apache.
the class RaftServerImpl method notifyTruncatedLogEntry.
/**
* The given log entry is being truncated.
* Fail the corresponding client request, if there is any.
*
* @param logEntry the log entry being truncated
*/
void notifyTruncatedLogEntry(LogEntryProto logEntry) {
if (logEntry.hasStateMachineLogEntry()) {
final ClientInvocationId invocationId = ClientInvocationId.valueOf(logEntry.getStateMachineLogEntry());
final CacheEntry cacheEntry = getRetryCache().getIfPresent(invocationId);
if (cacheEntry != null) {
cacheEntry.failWithReply(newReplyBuilder(invocationId, logEntry.getIndex()).setException(generateNotLeaderException()).build());
}
}
}
use of org.apache.ratis.server.impl.RetryCacheImpl.CacheEntry in project incubator-ratis by apache.
the class RaftServerImpl method checkLeaderState.
/**
* @return null if the server is in leader state.
*/
private CompletableFuture<RaftClientReply> checkLeaderState(RaftClientRequest request, CacheEntry entry, boolean isWrite) {
try {
assertGroup(request.getRequestorId(), request.getRaftGroupId());
} catch (GroupMismatchException e) {
return RetryCacheImpl.failWithException(e, entry);
}
if (!getInfo().isLeader()) {
NotLeaderException exception = generateNotLeaderException();
final RaftClientReply reply = newExceptionReply(request, exception);
return RetryCacheImpl.failWithReply(reply, entry);
}
if (!getInfo().isLeaderReady()) {
final CacheEntry cacheEntry = retryCache.getIfPresent(ClientInvocationId.valueOf(request));
if (cacheEntry != null && cacheEntry.isCompletedNormally()) {
return cacheEntry.getReplyFuture();
}
final LeaderNotReadyException lnre = new LeaderNotReadyException(getMemberId());
final RaftClientReply reply = newExceptionReply(request, lnre);
return RetryCacheImpl.failWithReply(reply, entry);
}
if (isWrite && isSteppingDown()) {
final LeaderSteppingDownException lsde = new LeaderSteppingDownException(getMemberId() + " is stepping down");
final RaftClientReply reply = newExceptionReply(request, lsde);
return RetryCacheImpl.failWithReply(reply, entry);
}
return null;
}
use of org.apache.ratis.server.impl.RetryCacheImpl.CacheEntry in project incubator-ratis by apache.
the class RaftServerImpl method replyPendingRequest.
/**
* The log has been submitted to the state machine. Use the future to update
* the pending requests and retry cache.
* @param logEntry the log entry that has been submitted to the state machine
* @param stateMachineFuture the future returned by the state machine
* from which we will get transaction result later
*/
private CompletableFuture<Message> replyPendingRequest(LogEntryProto logEntry, CompletableFuture<Message> stateMachineFuture) {
Preconditions.assertTrue(logEntry.hasStateMachineLogEntry());
final ClientInvocationId invocationId = ClientInvocationId.valueOf(logEntry.getStateMachineLogEntry());
// update the retry cache
final CacheEntry cacheEntry = retryCache.getOrCreateEntry(invocationId);
if (getInfo().isLeader()) {
Preconditions.assertTrue(cacheEntry != null && !cacheEntry.isCompletedNormally(), "retry cache entry should be pending: %s", cacheEntry);
}
if (cacheEntry.isFailed()) {
retryCache.refreshEntry(new CacheEntry(cacheEntry.getKey()));
}
final long logIndex = logEntry.getIndex();
return stateMachineFuture.whenComplete((reply, exception) -> {
final RaftClientReply.Builder b = newReplyBuilder(invocationId, logIndex);
final RaftClientReply r;
if (exception == null) {
r = b.setSuccess().setMessage(reply).build();
} else {
// the exception is coming from the state machine. wrap it into the
// reply as a StateMachineException
final StateMachineException e = new StateMachineException(getMemberId(), exception);
r = b.setException(e).build();
}
// update pending request
role.getLeaderState().ifPresent(leader -> leader.replyPendingRequest(logIndex, r));
cacheEntry.updateResult(r);
});
}
Aggregations