async-retry
async-retry copied to clipboard
Feature request: Callbacks onAbort, onRetry
I've looked into this API and saw that some callbacks (onAbort, onRetry) would be awesome. These should be defineable in each "RetryCallable" and provide some nice option to react on these events.
Speaking of this - the Java 7 way is easy, just extend the interface by 2 methods and add an abstract class. Shouldn't there be a simpler solution, that I'm just not aware of?
The problem is Java 8 - lambdas. I'm not used to them so I can't provide any solution on this. What's up with you guys out there? Any idea how to do this without breaking "ctx -> method()"
It's actually even simpler with Java 8 (which is the primary target for this library) - simply adding default
methods:
@FunctionalInterface
public interface RetryCallable<V> {
V call(RetryContext context) throws Exception;
default void onRetry(Throwable t) {}
default void onAbort(Throwable t) {}
}
When onRetry()
/onAbort()
is not used, simplified ctx -> method()
lambda syntax can be used. If callbacks are desired, more complex syntax must be used even in Java 8:
executor.getWithRetry(new RetryCallable<Integer>() {
@Override
public Integer call(RetryContext context) throws Exception {
return method();
}
@Override
public void onRetry(Throwable t) {
//...
}
@Override
public void onAbort(Throwable t) {
//...
}
})
Remember that RetryRunnable
has to be enhanced this way as well.
Another solution I can think of is adding AsyncRetryExecutor
-wide callbacks configuration:
executor.
onRetry(throwable -> /* ... */).
onAbort(throwable -> /* ... */).
doWithRetry(ctx -> method());
AsyncRetryExecutor
is immutable so onRetry()
/onAbort()
do not affect existing executor
but create new, enhanced one. What do you think?
Last but not least, what are the intended use cases for these callbacks, except e.g. logging (built-in)? Remember that RetryPolicy
is already called after each exception so you can get similar functionality by wrapping RetryPolicy
.
In my case I'd like to start a different action in case the operation did not succeed within the number of retries.
@felipesere Instead of using "failure callback" you can just register callback on returned CompletableFuture
:
executor.
getWithRetry(() -> new Socket("localhost", 8080)).
whenComplete((socket, error) -> {
if (socket != null) {
//connected OK, proceed
} else {
log.error("Can't connect, last error:", error);
}
});
or more succinctly:
executor.
getWithRetry(() -> new Socket("localhost", 8080)).
exceptionally(throwable -> {
//no more retries, failure callback here
});
Does this somewhat address your issues?
I'm stuck on Java 7. Does that work there too?
@felipesere It's possible, but much more verbose with ListenableFuture
API from Guava:
final ListenableFuture<Socket> future = executor.getWithRetry(new RetryCallable<Socket>() {
@Override
public Socket call(RetryContext context) throws Exception {
return new Socket("localhost", 8080);
}
});
Futures.withFallback(future, new FutureFallback<Socket>() {
@Override
public ListenableFuture<Socket> create(Throwable t) throws Exception {
//no more retries, failure callback here
return Futures.immediateFailedFuture(t);
}
});
I would love this feature as well. My use case is I am sending e-mails using Spring and when a MailSendException occurs the exception contains the failed messages. I'd like to retry only those messages found within the exception.
Last but not least, what are the intended use cases for these callbacks, except e.g. logging (built-in)? Remember that RetryPolicy is already called after each exception so you can get similar functionality by wrapping RetryPolicy.
What do you mean by "wrapping RetryPolicy"? Could you give an example? I too would be insterested in this addded functionality.