I am implementing a non-blocking retry logic for a POST request in Java using CompletableFuture. The goal is to retry the request up to n times and return the final result (either success or failure) after all attempts are complete. Here's how I start the process:
sendClusterPost(restRouteOfNode, jwtToken, CLUSTER_POST_RETRY_ATTEMPT)
.thenAccept(success -> {
if (success) {
logger.info("Cluster POST redirection succeeded for {}", restRouteOfNode);
} else {
logger.error("Cluster POST redirection failed for {}. Local node {} will fetch the stream.",
restRouteOfNode, getServerSettings().getHostAddress());
// Handle failure here.
}
})
.exceptionally(ex -> {
logger.error("Cluster POST encountered an exception: {}", ExceptionUtils.getStackTrace(ex));
return null;
});
Here is the logic for the CompletableFuture implementation:
public CompletableFuture<Boolean> sendClusterPost(String url, String clusterCommunicationToken, int retryAttempts) {
CompletableFuture<Boolean> future = new CompletableFuture<>();
vertx.executeBlocking(promise -> {
try (CloseableHttpClient httpClient = getHttpClient()) {
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(CLUSTER_POST_TIMEOUT_MS)
.setConnectionRequestTimeout(CLUSTER_POST_TIMEOUT_MS)
.setSocketTimeout(CLUSTER_POST_TIMEOUT_MS)
.build();
httpPost.setConfig(requestConfig);
httpPost.setHeader(TokenFilterManager.TOKEN_HEADER_FOR_NODE_COMMUNICATION, clusterCommunicationToken);
try (CloseableHttpResponse httpResponse = httpClient.execute(httpPost)) {
int statusCode = httpResponse.getStatusLine().getStatusCode();
logger.info("Cluster POST Response Status: {}", statusCode);
if (statusCode == HttpStatus.SC_OK) {
promiseplete(true);
} else if (retryAttempts > 0) {
logger.info("Retrying Cluster POST in {} ms due to non-200 response: {}",
appSettings.getWebhookRetryDelay(), statusCode);
retrySendClusterPostWithDelay(url, clusterCommunicationToken, retryAttempts - 1)
.thenAccept(promise::complete);
} else {
logger.info("No more retry attempts left. Giving up.");
promiseplete(false);
}
}
} catch (IOException e) {
if (retryAttempts > 0) {
logger.info("Retrying Cluster POST in {} ms due to IOException: {}",
appSettings.getWebhookRetryDelay(), ExceptionUtils.getStackTrace(e));
retrySendClusterPostWithDelay(url, clusterCommunicationToken, retryAttempts - 1)
.thenAccept(promise::complete);
} else {
logger.info("No more retry attempts left. Giving up.");
promiseplete(false);
}
}
}, result -> {
if (result.succeeded()) {
futureplete((Boolean) result.result());
} else {
futurepleteExceptionally(result.cause());
}
});
return future;
}
public CompletableFuture<Boolean> retrySendClusterPostWithDelay(String url, String clusterCommunicationToken, int retryAttempts) {
CompletableFuture<Boolean> future = new CompletableFuture<>();
vertx.setTimer(appSettings.getWebhookRetryDelay(), timerId ->
sendClusterPost(url, clusterCommunicationToken, retryAttempts).thenAccept(future::complete)
);
return future;
}
I'm not very experienced with CompletableFuture, and while this implementation looks correct, I feel it might be overcomplicated. Specifically:
Is this a proper non-blocking implementation for retry logic?
Are there any redundant or unnecessary parts in this code that could be simplified without compromising functionality?
Any suggestions for improvement or simplification are appreciated!