RetryFailed Operation in Project Reactor

We often work in applications that interact with external services (Rest APIs or DB…).  It can happen that a call to the external service sometimes fails, and we would want to have the ability to retry the failed call.

In Project Reactor, we have operators such as retry() and retryWhen() that we can use to retry the failed operation.

Retry failed operation with retry() method

The retry(long numRetries) re-subscribes to the Publisher sequence if it signals any error for a fixed number of times. 

In this example, let’s set the code to retry 3 times in case we get the onError signal from the Publisher:

class ReactiveJavaTutorial {

  public static void main(String[] args) {

    Mono.just("data1")
            .concatWith(Flux.error(new RuntimeException("Exception occurred.")))
            .doOnError(ex -> System.out.println("LOG: Exception caught: " + ex))
            .retry(3) //retry 3 times in case of an error
            .log()
            .subscribe();

  }
}
Output: reactor.Flux.Retry.1 : onSubscribe(FluxRetry.RetrySubscriber) reactor.Flux.Retry.1 : request(unbounded) reactor.Flux.Retry.1 : onNext(data1) LOG: Exception caught: java.lang.RuntimeException: Exception occurred. reactor.Flux.Retry.1 : onNext(data1) LOG: Exception caught: java.lang.RuntimeException: Exception occurred. reactor.Flux.Retry.1 : onNext(data1) LOG: Exception caught: java.lang.RuntimeException: Exception occurred. reactor.Flux.Retry.1 : onNext(data1) LOG: Exception caught: java.lang.RuntimeException: Exception occurred. reactor.Flux.Retry.1 : onError(java.lang.RuntimeException: Exception occurred.) reactor.Flux.Retry.1 : java.lang.RuntimeException: Exception occurred. at com.example.demo.ReactiveJavaTutorial.main(ReactiveJavaTutorial.java:14)
 
Here, you can see that the log got printed 4 times: 1 original + 3 retries.

Retry failed with retryWhen() method

The retryWhen() method can perform a retry on a specific exception. Also, we can specify the delay between each of the retries.

Let’s write a program that retries 3 times with a delay of 2 seconds between each attempt. In this way, we can give the failing service time to recover.

class ReactiveJavaTutorial {

  public static void main(String[] args) {

    Mono.just("data1")
            .concatWith(Flux.error(new RuntimeException("Exception occurred.")))
            .retryWhen(Retry.fixedDelay(3, Duration.ofSeconds(2)))
            .log()
            .subscribe();

  }
}

Retrying using Backoff

With Backoff, the delay will be increased progressively after each attempt. In our example, it will be 2, 4, 8 seconds of delay.

class ReactiveJavaTutorial {

  public static void main(String[] args) {

    Mono.just("data1")
            .concatWith(Flux.error(new RuntimeException("Exception occurred.")))
            .retryWhen(Retry.backoff(3, Duration.ofSeconds(2)))
            .log()
            .subscribe();
  }
}

Retry only on specific exception

We would want to retry only when we get a specific exception from the server in real-world applications. Let’s set the code to retry only in case of the “InternalServerError” exception from the server.

class ReactiveJavaTutorial {

  public static void main(String[] args) {

    Mono.just("data1")
            .concatWith(Flux.error(new RuntimeException("Exception occurred.")))
            .retryWhen(Retry.backoff(3, Duration.ofSeconds(2))
                    .filter(throwable -> throwable instanceof InternalServerError))
            .log()
            .subscribe();

  }

}

That was all about retrying the failed operations in Project Reactor. Proceed to the next lesson.

Happy coding!

Leave a Reply

Your email address will not be published. Required fields are marked *