failsafe-lib / failsafe

Fault tolerance and resilience patterns for the JVM
https://failsafe.dev
Apache License 2.0
4.2k stars 297 forks source link

Throwing an exception onFailure #283

Closed sakshamchawla closed 3 years ago

sakshamchawla commented 3 years ago

I'm trying to throw a Runtime exception on Failure after reaching max attempts but all the exceptions seem to be handled by Failsafe.

Here's my retry policy

RetryPolicy<Object> retryPolicy = new RetryPolicy<>().withMaxAttempts(attempts).handleResultIf(result -> {
...
}).onFailure(e -> {throw new RuntimeException("Exception"); });

Here's my Failsafe.with

Failsafe.with(retryPolicy).get(() -> function.apply(params));

The exception on failure is never thrown. Any help appreciated, thanks.

jhalterman commented 3 years ago

To return an alternative result when a failure occurs, you'll want to use a Fallback:

Fallback<Object> fallback = Fallback.ofException(e -> new RuntimeException("Exception"));
Failsafe.with(fallback, retryPolicy).get(() -> function.apply(params));

Here the outer Fallback handles any failures that cannot be handled by the inner retryPolicy, such as if retries are exceeded. (See also the docs here).

Event listeners, like onFailure, are purely meant for side effects, and don't influence the outcome of a Failsafe execution. I'll update the docs to make this more clear.

sakshamchawla commented 3 years ago

With a Fallback, it still does not throw a RuntimeException when maxAttempts is reached (I copied your solution). I tried this when I first looked at Fallback docs, but I thought I missed something, so I opened an issue.

such as if retries are exceeded

According to the docs, maxRetries and maxAttempts have the same effect, either way, the RuntimeException was not thrown.

jhalterman commented 3 years ago

Each policy makes its own determination about what a failure is. So you'll likely want to use the same handleResultIf statement when configuring the Fallback as well. Here's a more complete example:

RetryPolicy<Boolean> retryPolicy = new RetryPolicy<Boolean>()
  .withMaxAttempts(3)
  .handleResultIf(result -> !result);
Fallback<Boolean> fallback = Fallback.<Boolean>ofException(e -> new RuntimeException("Exception"))
  .handleResultIf(result -> !result);

// Attempts 3 times then is handled by the Fallback
Failsafe.with(fallback, retryPolicy).get(() -> false);

This may seem a bit redundant, but the reason for this separation is so that different policies can be combined and composed with each other. By default, all policies handle any exception. If your failure is something other than an exception, or if you want to only handle certain exceptions, that's when you'd use one of the handle functions.

sakshamchawla commented 3 years ago

Thank you! That solved the issue, and I see the point of each policy handling their own exceptions.

jhalterman commented 3 years ago

Going to reopen this for myself as a reminder to improve the docs in this area...

jhalterman commented 3 years ago

Fixed by 7a92886efd6db7ca333d2754a856db5b46326dc4 and https://github.com/failsafe-lib/failsafe-lib.github.io/commit/36e0407008cffdfe61858d3ecb068fd78dbf9605