Issue
I'm using a fixedRetry in order to repeat HTTP calls using WebClient. I want the first retry to be called after 5 minutes, the second one after 30 minutes, and the rest after 60 minutes. Is there a way to do this using the fixedRetry? I checked href="https://www.baeldung.com/spring-webflux-retry" rel="nofollow noreferrer">https://www.baeldung.com/spring-webflux-retry several times, but I haven't found a way to use 3 different values for the delay time.
This how the constructor looks like:
public MyService(WebClient client,
@Value("${retry.quantity}") int retries,
@Value("${retry.time.first}") int firstTime,
@Value("${retry.time.second}") int secondTime,
@Value("${retry.time.rest}") int restTime)
{
this.client = client;
this.fixedRetry = Retry.anyOf(ResponseException.class)
.fixedBackoff(Duration.ofSeconds(restTime))
.retryMax(retries)
.doOnRetry(exception ->
log.error("Exception on Retry is {} .", exception)
);
}
Solution
You would need to implement custom Retry
to achieve this. Here is an example
private static class CustomRetrySpec extends Retry {
private final int retries;
private final Duration firstTime;
private final Duration secondTime;
private final Duration restTime;
public CustomRetrySpec(int retries, Duration firstTime, Duration secondTime, Duration restTime) {
this.retries = retries;
this.firstTime = firstTime;
this.secondTime = secondTime;
this.restTime = restTime;
}
@Override
public Publisher<?> generateCompanion(Flux<RetrySignal> retrySignals) {
return retrySignals.flatMap(this::getRetry);
}
private Mono<Long> getRetry(Retry.RetrySignal rs) {
if (rs.totalRetries() < retries) {
Duration delay;
if (rs.totalRetries() == 0) {
delay = firstTime;
} else if (rs.totalRetries() == 1) {
delay = secondTime;
} else {
delay = restTime;
}
log.debug("retry {} with backoff {}min", rs.totalRetries(), delay.toMinutes());
return Mono.delay(delay)
.thenReturn(rs.totalRetries());
} else {
log.debug("retries exhausted with error: {}", rs.failure().getMessage());
throw Exceptions.propagate(rs.failure());
}
}
}
Here is a test using StepVerifier.withVirtualTime
@Test
void testCustomRetrySpec() {
StepVerifier.withVirtualTime(() ->
process()
.retryWhen(new CustomRetrySpec(3,
Duration.ofMinutes(5),
Duration.ofMinutes(30),
Duration.ofMinutes(60))
)
)
.expectSubscription()
.expectNoEvent(Duration.ofMinutes(5))
.expectNoEvent(Duration.ofMinutes(30))
.expectNoEvent(Duration.ofMinutes(60))
.expectError()
.verify();
}
private Mono<?> process() {
return Mono.error(new RuntimeException("oops"));
}
Answered By - Alex
Answer Checked By - David Goodson (JavaFixing Volunteer)