/** * Handles a response from the cluster. */ @SuppressWarnings("unchecked") private <T extends Request> void handleResponse(T request, BiFunction sender, Connection connection, Response response, Throwable error, CompletableFuture future) { if (open) { if (error == null) { if (response.status() == Response.Status.OK || response.error() == CopycatError.Type.COMMAND_ERROR || response.error() == CopycatError.Type.QUERY_ERROR || response.error() == CopycatError.Type.APPLICATION_ERROR || response.error() == CopycatError.Type.UNKNOWN_SESSION_ERROR || response.error() == CopycatError.Type.INTERNAL_ERROR) { LOGGER.trace("{} - Received {}", id, response); future.complete(response); } else { resendRequest(response.error().createException(), request, sender, connection, future); } } else if (error instanceof ConnectException || error instanceof TimeoutException || error instanceof TransportException || error instanceof ClosedChannelException) { resendRequest(error, request, sender, connection, future); } else { LOGGER.debug("{} - {} failed! Reason: {}", id, request, error); future.completeExceptionally(error); } } }
@Override public void accept(QueryResponse response, Throwable error) { if (error == null) { state.getLogger().trace("{} - Received {}", state.getSessionId(), response); if (response.status() == Response.Status.OK) { complete(response); } else { complete(response.error().createException()); } } else { fail(error); } }
@Override public void accept(QueryResponse response, Throwable error) { if (error == null) { state.getLogger().trace("{} - Received {}", state.getSessionId(), response); if (response.status() == Response.Status.OK) { complete(response); } else { complete(response.error().createException()); } } else { fail(error); } }
@Override public void accept(CommandResponse response, Throwable error) { if (error == null) { state.getLogger().trace("{} - Received {}", state.getSessionId(), response); if (response.status() == Response.Status.OK) { complete(response); } // COMMAND_ERROR indicates that the command was received by the leader out of sequential order. // We need to resend commands starting at the provided lastSequence number. else if (response.error() == CopycatError.Type.COMMAND_ERROR) { resubmit(response.lastSequence(), this); } // The following exceptions need to be handled at a higher level by the client or the user. else if (response.error() == CopycatError.Type.APPLICATION_ERROR || response.error() == CopycatError.Type.UNKNOWN_SESSION_ERROR || response.error() == CopycatError.Type.INTERNAL_ERROR) { complete(response.error().createException()); } // For all other errors, use fibonacci backoff to resubmit the command. else { retry(Duration.ofSeconds(FIBONACCI[Math.min(attempt-1, FIBONACCI.length-1)])); } } else if (EXCEPTION_PREDICATE.test(error) || (error instanceof CompletionException && EXCEPTION_PREDICATE.test(error.getCause()))) { retry(Duration.ofSeconds(FIBONACCI[Math.min(attempt-1, FIBONACCI.length-1)])); } else { fail(error); } }
/** * Handles a response from the cluster. */ @SuppressWarnings("unchecked") private <T extends Request> void handleResponse(T request, BiFunction sender, Connection connection, Response response, Throwable error, CompletableFuture future) { if (open) { if (error == null) { if (response.status() == Response.Status.OK || response.error() == CopycatError.Type.COMMAND_ERROR || response.error() == CopycatError.Type.QUERY_ERROR || response.error() == CopycatError.Type.APPLICATION_ERROR || response.error() == CopycatError.Type.UNKNOWN_SESSION_ERROR || response.error() == CopycatError.Type.INTERNAL_ERROR) { LOGGER.trace("{} - Received {}", id, response); future.complete(response); } else { resendRequest(response.error().createException(), request, sender, connection, future); } } else if (error instanceof ConnectException || error instanceof TimeoutException || error instanceof TransportException || error instanceof ClosedChannelException) { resendRequest(error, request, sender, connection, future); } else { LOGGER.debug("{} - {} failed! Reason: {}", id, request, error); future.completeExceptionally(error); } } }
@Override public void accept(CommandResponse response, Throwable error) { if (error == null) { state.getLogger().trace("{} - Received {}", state.getSessionId(), response); if (response.status() == Response.Status.OK) { complete(response); } // COMMAND_ERROR indicates that the command was received by the leader out of sequential order. // We need to resend commands starting at the provided lastSequence number. else if (response.error() == CopycatError.Type.COMMAND_ERROR) { resubmit(response.lastSequence(), this); } // The following exceptions need to be handled at a higher level by the client or the user. else if (response.error() == CopycatError.Type.APPLICATION_ERROR || response.error() == CopycatError.Type.UNKNOWN_SESSION_ERROR || response.error() == CopycatError.Type.INTERNAL_ERROR) { complete(response.error().createException()); } // For all other errors, use fibonacci backoff to resubmit the command. else { retry(Duration.ofSeconds(FIBONACCI[Math.min(attempt-1, FIBONACCI.length-1)])); } } else if (EXCEPTION_PREDICATE.test(error) || (error instanceof CompletionException && EXCEPTION_PREDICATE.test(error.getCause()))) { retry(Duration.ofSeconds(FIBONACCI[Math.min(attempt-1, FIBONACCI.length-1)])); } else { fail(error); } }
@Override public void accept(U response, Throwable error) { if (error == null) { state.getLogger().debug("{} - Received {}", state.getSessionId(), response); if (response.status() == Response.Status.OK) { complete(response); } else if (response.error() == CopycatError.Type.COMMAND_ERROR || response.error() == CopycatError.Type.QUERY_ERROR || response.error() == CopycatError.Type.APPLICATION_ERROR) { complete(response.error().createException()); } else if (response.error() != CopycatError.Type.UNKNOWN_SESSION_ERROR) { strategy.attemptFailed(this, response.error().createException()); } } else { strategy.attemptFailed(this, error); } }
/** * Recursively reconfigures the cluster. */ private void configure(Member.Type type, CompletableFuture<Void> future) { // Set a timer to retry the attempt to leave the cluster. configureTimeout = cluster.getContext().getThreadContext().schedule(cluster.getContext().getElectionTimeout(), () -> { configure(type, future); }); // Attempt to leave the cluster by submitting a LeaveRequest directly to the server state. // Non-leader states should forward the request to the leader if there is one. Leader states // will log, replicate, and commit the reconfiguration. cluster.getContext().getAbstractState().reconfigure(ReconfigureRequest.builder() .withIndex(cluster.getConfiguration().index()) .withMember(new ServerMember(type, serverAddress(), clientAddress(), updated)) .build()).whenComplete((response, error) -> { if (error == null) { if (response.status() == Response.Status.OK) { cancelConfigureTimer(); cluster.configure(new Configuration(response.index(), response.timestamp(), response.members())); future.complete(null); } else if (response.error() == null || response.error() == CopycatError.Type.NO_LEADER_ERROR) { cancelConfigureTimer(); configureTimeout = cluster.getContext().getThreadContext().schedule(cluster.getContext().getElectionTimeout().multipliedBy(2), () -> configure(type, future)); } else { cancelConfigureTimer(); future.completeExceptionally(response.error().createException()); } } }); }
} else { cancelConfigureTimer(); future.completeExceptionally(response.error().createException());
} else { cancelConfigureTimer(); future.completeExceptionally(response.error().createException());