/** Registers the session. */ private CompletableFuture<Void> register() { context.checkThread(); CompletableFuture<Void> future = new CompletableFuture<>(); RegisterRequest request = RegisterRequest.builder().withClient(clientId).build(); this.<RegisterRequest, RegisterResponse>request(request, new CompletableFuture<>(), false, true) .whenComplete( (response, error) -> { if (error == null) { if (response.status() == Response.Status.OK) { setMembers(response.members()); setTimeout(response.timeout()); onOpen(response.session()); setupConnection(connection) .whenComplete((setupResult, setupError) -> future.complete(null)); resetMembers() .keepAlive( Duration.ofMillis(Math.round(response.timeout() * KEEP_ALIVE_RATIO))); } else { future.completeExceptionally(response.error().createException()); } } else { future.completeExceptionally(error); } }); return future; }
/** * Handles a publish request. * * @param request The publish request to handle. * @return A completable future to be completed with the publish response. */ @SuppressWarnings("unchecked") private CompletableFuture<PublishResponse> handlePublish(PublishRequest request) { if (request.session() != id) return Futures.exceptionalFuture(new UnknownSessionException("incorrect session ID")); if (request.previousVersion() != eventVersion) { return CompletableFuture.completedFuture( PublishResponse.builder() .withStatus(Response.Status.ERROR) .withError(RaftError.Type.INTERNAL_ERROR) .withVersion(eventVersion) .build()); } eventVersion = request.eventVersion(); List<CompletableFuture<Void>> futures = new ArrayList<>(request.events().size()); for (Event<?> event : request.events()) { Listeners<Object> listeners = eventListeners.get(event.name()); if (listeners != null) { futures.add(listeners.accept(event.message())); } } return CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[futures.size()])) .handleAsync( (result, error) -> { completeVersion = Math.max(completeVersion, request.eventVersion()); return PublishResponse.builder() .withStatus(Response.Status.OK) .withVersion(eventVersion) .build(); }, context.executor()); }
/** Recursively submits a query. */ @SuppressWarnings("unchecked") private <T> CompletableFuture<T> submit(QueryRequest request, CompletableFuture<T> future) { if (!isOpen()) { future.completeExceptionally(new IllegalStateException("session not open")); return future; } long sequence = ++requestSequence; this.<QueryRequest, QueryResponse>request(request) .whenComplete( (response, error) -> { if (error == null) { // If the query consistency level is CAUSAL, we can simply complete queries in // sequential order. if (request.query().consistency() == Query.ConsistencyLevel.CAUSAL) { sequenceResponse( sequence, () -> { responseVersion = Math.max(responseVersion, response.version()); completeResponse(response, future); }); } // If the query consistency level is strong, the query must be executed // sequentially. In order to ensure responses // are received in a sequential manner, we compare the response version number with // the highest version for which // we've received a response and resubmit queries with output resulting from stale // (prior) versions. else { sequenceResponse( sequence, () -> { if (response.version() > 0 && response.version() < responseVersion) { submit(request, future); } else { responseVersion = Math.max(responseVersion, response.version()); completeResponse(response, future); } }); } } else { future.completeExceptionally(error); } }); return future; }