async-retry icon indicating copy to clipboard operation
async-retry copied to clipboard

Feature request: Callbacks onAbort, onRetry

Open dennisfischer opened this issue 11 years ago • 7 comments

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()"

dennisfischer avatar Aug 28 '13 19:08 dennisfischer

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.

nurkiewicz avatar Aug 28 '13 20:08 nurkiewicz

In my case I'd like to start a different action in case the operation did not succeed within the number of retries.

felipesere avatar Nov 11 '14 17:11 felipesere

@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?

nurkiewicz avatar Nov 11 '14 18:11 nurkiewicz

I'm stuck on Java 7. Does that work there too?

felipesere avatar Nov 11 '14 19:11 felipesere

@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);
    }
});

nurkiewicz avatar Nov 11 '14 20:11 nurkiewicz

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.

derylspielman avatar Aug 07 '15 22:08 derylspielman

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.

pisarek avatar Jan 22 '16 11:01 pisarek