2

Could I be able to add multiple retryWhen to perform retries to handle different WebClient failure responses?

What I want to achieve:

I am using WebClient to make REST API Calls. There are few error scenarios, when happens I need to perform retry, but with different delays.

For eg, 1. if 401 Unauthorize happens, I can retry immediately after refreshing the token. 2. If 502/503 Server error happens, I need to delay the retries after 5 sec. 3. if 429 Too Many Request happens, I need to delay the retry little longer, say 20sec after.

I would like to create Retry spec like below:

    protected static final Predicate<Throwable> is401 =
                (throwable) -> throwable instanceof WebClientResponseException.Unauthorized;

        protected static final Predicate<Throwable> is5xx =
                (throwable) -> throwable instanceof WebClientResponseException.ServiceUnavailable;

        protected static final Predicate<Throwable> is429 =
                (throwable) -> throwable instanceof WebClientResponseException.TooManyRequests;

        Retry retry401 = Retry.fixedDelay(5, Duration.ofSeconds(1))
                    .filter(is401)
                    .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> retrySignal.failure());

        Retry retry5xx = Retry.fixedDelay(5, Duration.ofSeconds(10))
                    .filter(is5xx)
                    .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> retrySignal.failure());

        Retry retry429 = Retry.fixedDelay(5, Duration.ofSeconds(20))
                    .filter(is429)
                    .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> retrySignal.failure());

// trying to apply the to WebClient like below:
    WebClient.Builder()
        .get()
        .uri("endpointuri")
        .retrieve()
        .bodyToFlux(String.class)
        .retryWhen(retry401)
        .retryWhen(retry5xx)
        .retryWhen(retry429);

Looks like `.retryWhen(retry429)' overwrites the other retries.

Naveen Kumar
  • 893
  • 11
  • 19
  • So what do you expect to happen if you get 401,500,401,429? Should it reset? Should it keep count per error? – 123 May 30 '20 at 13:56

1 Answers1

3

Looks like `.retryWhen(retry429)' overwrites the other retries.

This is false. retryWhen is a composite operator that builds upon the existing publisher - you can chain it as many times as you like. The only time you need to worry about one retry "overriding" another is when your filter predicates overlap.

Even in this case, it looks like it's "first wins" (in the chain) not "last wins".

Your problems instead may be to do with this line:

protected static final Predicate<Throwable> is5xx =
            (throwable) -> throwable instanceof WebClientResponseException.ServiceUnavailable;

From your naming and your description, it looks like you want this to catch any 5xx error - but it'll only catch 503 (which is specifically assigned to "service unavailable".)

If you're trying with something different, like a 502 or 500 error - then none of the predicates (and therefore retries) you've defined will match.

Instead, to check for any 5xx error, you likely want:

protected static final Predicate<Throwable> is5xx =
        (throwable) -> throwable instanceof WebClientResponseException && ((WebClientResponseException)throwable).getStatusCode().is5xxServerError();
Michael Berry
  • 70,193
  • 21
  • 157
  • 216