failsafe-lib / failsafe

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

Provide result when retries exceeded #181

Open gm2211 opened 5 years ago

gm2211 commented 5 years ago

Currently there isn't a good way to return a result if the retry count is exceeded - this is the best I've found:

private static final int MAX_ATTEMPTS = 10;
private static final RetryPolicy<Result> RETRY_POLICY = new RetryPolicy()
       .handle(RetriableException.class)
       .withMaxAttempts(MAX_ATTEMPTS);

Result foo() {
   Fallback<Result> fallback = Fallback.of(event -> {
       if (event.getAttemptCount() == MAX_ATTEMPTS) { 
            return Result.retryCountExceeded(event.getLastFailure());
       } 
       return Result.unrecoverableFailure(event.getLastFailure());
   });

   return FailSafe.with(fallback, RETRY_POLICY)
      .get(this::bar);
}

But I would prefer either:

private static final RetryPolicy<Result> RETRY_POLICY = new RetryPolicy()
       .handle(RetriableException.class)
       .withMaxAttempts(10);

Result foo() {
   Fallback<Result> fallback = Fallback.of(event -> {
       if (event.retriesExceeded()) { 
            return Result.retryCountExceeded(event.getLastFailure());
       } 
       return Result.unrecoverableFailure(event.getLastFailure());
   });

   return FailSafe.with(fallback, RETRY_POLICY)
      .get(this::bar);
}

or, even better, something like

private static final RetryPolicy<Result> RETRY_POLICY = new RetryPolicy<>()
       .handle(RetriableException.class)
       .withMaxAttempts(10);

Result foo() {
   return FailSafe.with(RETRY_POLICY)
      .ifFailed(event -> Result.unrecoverableFailure(event.getLastFailure()))
      .ifRetriesExceeded(event -> Result.retryCountExceeded(event.getLastFailure()))
      .get(this::bar);
}
jhalterman commented 5 years ago

The challenge with either of these solutions is that Failsafe treats Policies a bit generically and allows arbitrarily nesting/combining policies, which could include multiple retry policies:

RetryPolicy<Connection> rp1 = ...
RetryPolicy<Connection> rp2 = ...
Fallback<Connection> fallback = ...

Failsafe.with(fallback, rp2, rp1).get(this:connect);

Perhaps we could collect all of the policies that considered the current attempt a failure prior to the fallback attempt, and make those available via the ExecutionAttemptedEvent?