Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add retry support for CompletableFuture #200

Closed
linzee1 opened this issue Jul 13, 2024 · 2 comments
Closed

add retry support for CompletableFuture #200

linzee1 opened this issue Jul 13, 2024 · 2 comments

Comments

@linzee1
Copy link
Contributor

linzee1 commented Jul 13, 2024

Here is a Spring Retry example:

RetryTemplate template = RetryTemplate.builder()
				.maxAttempts(3)
				.fixedBackoff(1000)
				.retryOn(RemoteAccessException.class)
				.build();
template.execute(ctx -> {
    // ... do something
});

It is easy to implement retry for CompletableFuture.

@Slf4j
class RetryNAttemptsDemo {
    public static void main(String[] args) {
      	// return succeed result by 4th try
        var times = new AtomicInteger();
        Supplier<Integer> task = () -> {
            if (times.getAndIncrement() < 3) {
                throw new RuntimeException("error result");
            } else {
                return 42;
            }
        };
      	// using retry method
        retry(4, () -> supplyAsync(task))
            .thenAcceptAsync(r -> log.info("success result: {}", r))
            .exceptionallyAsync(throwable -> {
                log.error("final result", throwable);
                return null;
            })
            .join();
    }

    public static <T> CompletableFuture<T> retry(int attempts, Supplier<CompletionStage<T>> supplier) {
        var result = new CompletableFuture<T>();
        retryNAttempts(result, attempts, supplier);
        return result;
    }

    private static <T> void retryNAttempts(CompletableFuture<T> result, int attempts, Supplier<CompletionStage<T>> supplier) {
        supplier.get()
            .thenAccept(result::complete)
            .exceptionally(throwable -> {
                if (attempts > 0L) {
                    log.warn("retrying");
                    retryNAttempts(result, attempts - 1, supplier);
                } else {
                    log.error("retry failed", throwable);
                    result.completeExceptionally(throwable);
                }
                return null;
            });
    }
}

but it is better to add static utility method for CompletableFuture with less bug and multiple retry strategy support.

There are 3 types of strategies for retrying:

  1. trigger strategy (for specific value or exception)
  2. backoff strategy (no delay, fixed delay, exponential backoff, etc.)
  3. temination strategy ( n times, with timeout, util success result)

Goal:

  1. add static utility method retry(Retry retry, Supplier(CompletionStage<T>) supplier) : CompetionStage<T>
  2. utility method to create Retry instance
  3. Retry support strategies discussed above
  4. Retry instance is immutable and reusable
@oldratlee
Copy link
Member

oldratlee commented Jul 13, 2024

@linzee1 Thanks for your professional issue 👍

It'll take some time to understand and feedback. 💕


more info about retry see the author's article 重试机制与CompletableFuture拓展. 🆙

@oldratlee
Copy link
Member

oldratlee commented Jul 23, 2024

Mature resilience4j fault tolerance library provides retry with first-class support of CompletionStage/CompletableFuture.

It's recommended to use resilience4j retry rather than re-implementing in cffu.

@oldratlee oldratlee self-assigned this Jul 23, 2024
@oldratlee oldratlee removed the ✨ feature New feature label Jul 23, 2024
@oldratlee oldratlee linked a pull request Jul 29, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

2 participants