Search in sources :

Example 1 with CacheEntry

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;
}
Also used : TransactionContext(org.apache.ratis.statemachine.TransactionContext) StateMachineException(org.apache.ratis.protocol.exceptions.StateMachineException) CacheEntry(org.apache.ratis.server.impl.RetryCacheImpl.CacheEntry) Timer(com.codahale.metrics.Timer) TransactionContext(org.apache.ratis.statemachine.TransactionContext)

Example 2 with CacheEntry

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());
        }
    }
}
Also used : CacheEntry(org.apache.ratis.server.impl.RetryCacheImpl.CacheEntry)

Example 3 with CacheEntry

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;
}
Also used : NotLeaderException(org.apache.ratis.protocol.exceptions.NotLeaderException) GroupMismatchException(org.apache.ratis.protocol.exceptions.GroupMismatchException) LeaderNotReadyException(org.apache.ratis.protocol.exceptions.LeaderNotReadyException) CacheEntry(org.apache.ratis.server.impl.RetryCacheImpl.CacheEntry) LeaderSteppingDownException(org.apache.ratis.protocol.exceptions.LeaderSteppingDownException)

Example 4 with CacheEntry

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);
    });
}
Also used : StateMachineException(org.apache.ratis.protocol.exceptions.StateMachineException) CacheEntry(org.apache.ratis.server.impl.RetryCacheImpl.CacheEntry)

Aggregations

CacheEntry (org.apache.ratis.server.impl.RetryCacheImpl.CacheEntry)4 StateMachineException (org.apache.ratis.protocol.exceptions.StateMachineException)2 Timer (com.codahale.metrics.Timer)1 GroupMismatchException (org.apache.ratis.protocol.exceptions.GroupMismatchException)1 LeaderNotReadyException (org.apache.ratis.protocol.exceptions.LeaderNotReadyException)1 LeaderSteppingDownException (org.apache.ratis.protocol.exceptions.LeaderSteppingDownException)1 NotLeaderException (org.apache.ratis.protocol.exceptions.NotLeaderException)1 TransactionContext (org.apache.ratis.statemachine.TransactionContext)1