Search in sources :

Example 1 with RaftSession

use of io.atomix.protocols.raft.session.RaftSession 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);
}
Also used : CompletableFuture(java.util.concurrent.CompletableFuture) RaftSession(io.atomix.protocols.raft.session.RaftSession) OperationResult(io.atomix.protocols.raft.impl.OperationResult) CommandResponse(io.atomix.protocols.raft.protocol.CommandResponse) PendingCommand(io.atomix.protocols.raft.impl.PendingCommand)

Example 2 with RaftSession

use of io.atomix.protocols.raft.session.RaftSession in project atomix by atomix.

the class RaftServiceContext method keepAliveSessions.

/**
 * Keeps all sessions alive using the given timestamp.
 *
 * @param index     the index of the timestamp
 * @param timestamp the timestamp with which to reset session timeouts
 */
public void keepAliveSessions(long index, long timestamp) {
    log.debug("Resetting session timeouts");
    this.currentIndex = index;
    this.currentTimestamp = Math.max(currentTimestamp, timestamp);
    for (RaftSession session : sessions.getSessions()) {
        session.setLastUpdated(timestamp);
    }
}
Also used : RaftSession(io.atomix.protocols.raft.session.RaftSession)

Example 3 with RaftSession

use of io.atomix.protocols.raft.session.RaftSession in project atomix by atomix.

the class RaftSessions method expireSession.

/**
 * Expires and removes a session from the sessions list.
 *
 * @param session The session to remove.
 */
void expireSession(RaftSession session) {
    final RaftSession singletonSession = sessionManager.removeSession(session.sessionId());
    if (singletonSession != null) {
        singletonSession.expire();
        listeners.forEach(l -> l.onExpire(singletonSession));
    }
}
Also used : RaftSession(io.atomix.protocols.raft.session.RaftSession)

Example 4 with RaftSession

use of io.atomix.protocols.raft.session.RaftSession in project atomix by atomix.

the class RaftServiceManager method applyKeepAlive.

/**
 * Applies a session keep alive entry to the state machine.
 * <p>
 * Keep alive entries are applied to the internal state machine to reset the timeout for a specific session.
 * If the session indicated by the KeepAliveEntry is still held in memory, we mark the session as trusted,
 * indicating that the client has committed a keep alive within the required timeout. Additionally, we check
 * all other sessions for expiration based on the timestamp provided by this KeepAliveEntry. Note that sessions
 * are never completely expired via this method. Leaders must explicitly commit an UnregisterEntry to expire
 * a session.
 * <p>
 * When a KeepAliveEntry is committed to the internal state machine, two specific fields provided in the entry
 * are used to update server-side session state. The {@code commandSequence} indicates the highest command for
 * which the session has received a successful response in the proper sequence. By applying the {@code commandSequence}
 * to the server session, we clear command output held in memory up to that point. The {@code eventVersion} indicates
 * the index up to which the client has received event messages in sequence for the session. Applying the
 * {@code eventVersion} to the server-side session results in events up to that index being removed from memory
 * as they were acknowledged by the client. It's essential that both of these fields be applied via entries committed
 * to the Raft log to ensure they're applied on all servers in sequential order.
 * <p>
 * Keep alive entries are retained in the log until the next time the client sends a keep alive entry or until the
 * client's session is expired. This ensures for sessions that have long timeouts, keep alive entries cannot be cleaned
 * from the log before they're replicated to some servers.
 */
private long[] applyKeepAlive(Indexed<KeepAliveEntry> entry) {
    // Store the session/command/event sequence and event index instead of acquiring a reference to the entry.
    long[] sessionIds = entry.entry().sessionIds();
    long[] commandSequences = entry.entry().commandSequenceNumbers();
    long[] eventIndexes = entry.entry().eventIndexes();
    // Iterate through session identifiers and keep sessions alive.
    List<Long> successfulSessionIds = new ArrayList<>(sessionIds.length);
    for (int i = 0; i < sessionIds.length; i++) {
        long sessionId = sessionIds[i];
        long commandSequence = commandSequences[i];
        long eventIndex = eventIndexes[i];
        RaftSession session = raft.getSessions().getSession(sessionId);
        if (session != null) {
            if (session.getService().keepAlive(entry.index(), entry.entry().timestamp(), session, commandSequence, eventIndex)) {
                successfulSessionIds.add(sessionId);
            }
        }
    }
    // Iterate through services and complete keep-alives, causing sessions to be expired if necessary.
    for (RaftServiceContext service : raft.getServices()) {
        service.completeKeepAlive(entry.index(), entry.entry().timestamp());
    }
    return Longs.toArray(successfulSessionIds);
}
Also used : RaftSession(io.atomix.protocols.raft.session.RaftSession) ArrayList(java.util.ArrayList) RaftServiceContext(io.atomix.protocols.raft.service.RaftServiceContext)

Example 5 with RaftSession

use of io.atomix.protocols.raft.session.RaftSession in project atomix by atomix.

the class RaftSessionsTest method createSession.

private RaftSession createSession(long sessionId) {
    RaftServiceContext context = mock(RaftServiceContext.class);
    when(context.serviceType()).thenReturn(new TestPrimitiveType());
    when(context.serviceName()).thenReturn("test");
    when(context.serviceId()).thenReturn(PrimitiveId.from(1));
    RaftContext server = mock(RaftContext.class);
    when(server.getProtocol()).thenReturn(mock(RaftServerProtocol.class));
    RaftServiceManager manager = mock(RaftServiceManager.class);
    when(manager.executor()).thenReturn(mock(ThreadContext.class));
    when(server.getServiceManager()).thenReturn(manager);
    return new RaftSession(SessionId.from(sessionId), NodeId.from("1"), "test", new TestPrimitiveType(), ReadConsistency.LINEARIZABLE, 100, 5000, System.currentTimeMillis(), context, server, mock(ThreadContextFactory.class));
}
Also used : RaftServerProtocol(io.atomix.protocols.raft.protocol.RaftServerProtocol) RaftContext(io.atomix.protocols.raft.impl.RaftContext) RaftSession(io.atomix.protocols.raft.session.RaftSession) RaftServiceManager(io.atomix.protocols.raft.impl.RaftServiceManager) ThreadContextFactory(io.atomix.utils.concurrent.ThreadContextFactory) ThreadContext(io.atomix.utils.concurrent.ThreadContext) TestPrimitiveType(io.atomix.protocols.raft.proxy.impl.TestPrimitiveType)

Aggregations

RaftSession (io.atomix.protocols.raft.session.RaftSession)17 RaftServiceContext (io.atomix.protocols.raft.service.RaftServiceContext)3 PrimitiveType (io.atomix.primitive.PrimitiveType)2 SessionId (io.atomix.primitive.session.SessionId)2 QueryEntry (io.atomix.protocols.raft.storage.log.entry.QueryEntry)2 Indexed (io.atomix.storage.journal.Indexed)2 NodeId (io.atomix.cluster.NodeId)1 SessionMetadata (io.atomix.primitive.session.SessionMetadata)1 ReadConsistency (io.atomix.protocols.raft.ReadConsistency)1 OperationResult (io.atomix.protocols.raft.impl.OperationResult)1 PendingCommand (io.atomix.protocols.raft.impl.PendingCommand)1 RaftContext (io.atomix.protocols.raft.impl.RaftContext)1 RaftServiceManager (io.atomix.protocols.raft.impl.RaftServiceManager)1 CommandResponse (io.atomix.protocols.raft.protocol.CommandResponse)1 QueryResponse (io.atomix.protocols.raft.protocol.QueryResponse)1 RaftServerProtocol (io.atomix.protocols.raft.protocol.RaftServerProtocol)1 TestPrimitiveType (io.atomix.protocols.raft.proxy.impl.TestPrimitiveType)1 RaftSessionRegistry (io.atomix.protocols.raft.session.RaftSessionRegistry)1 ThreadContext (io.atomix.utils.concurrent.ThreadContext)1 ThreadContextFactory (io.atomix.utils.concurrent.ThreadContextFactory)1