Skip to content

Add support for cancelling inner CompletableFutures #266

@marcphilipp

Description

@marcphilipp

Among other things, we use Failsafe for uploading/downloading files via HTTP. In such cases, when the upload/download of a file is cancelled we want to stop not just Failsafe's retries but also the underlying file transfer. Our file transfer client returns a CompleteableFuture and thus we call FailsafeExecutor.getStageAsync(() -> uploadFile(...)). However, cancelling the returned CompletableFuture does not cancel the CompletableFuture returned by uploadFile(...). This is kind of obvious since getStageAsync() only works with CompletionStage but it would be great if Failsafe could also cancel the CompletableFutures returned by the passed supplier.

Here's a full example:

public class Demo {

    public static void main(String[] args) {
        CompletableFuture<String> future = computeSomethingWithRetries();
        future.whenComplete((r, t) -> {
            if (t instanceof CancellationException) {
                System.out.println("Failsafe's CompletableFuture was cancelled");
            }
        });
        future.cancel(true);
    }

    static CompletableFuture<String> computeSomethingWithRetries() {
        Policy<String> retryPolicy = new RetryPolicy<String>()
                .handle(RuntimeException.class)
                .withMaxAttempts(-1);

        return Failsafe.with(retryPolicy).getStageAsync(Demo::computeSomething);
    }

    static CompletableFuture<String> computeSomething() {
        CompletableFuture<String> future = new CompletableFuture<>();
        future.whenComplete((r, t) -> {
            if (t instanceof CancellationException) {
                System.out.println("Inner CompletableFuture was cancelled");
            }
        });
        return future;
    }
}

This currently prints

Failsafe's CompletableFuture was cancelled

but should print

Inner CompletableFuture was cancelled
Failsafe's CompletableFuture was cancelled

Full Gradle project: https://github.com/marcphilipp/gradle-sandbox/tree/master/failsafe-completable-future

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions