mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-24 15:49:26 +00:00
Introduce onRetryPolicyInterruption() callback in RetryListener
In RetryTemplate, if we encounter an InterruptedException while sleeping for the configured back-off duration, we throw a RetryException with the InterruptedException as the cause. However, prior to this commit, that RetryException propagated to the caller without notifying the registered RetryListener. To address that, this commit introduces a new onRetryPolicyInterruption() callback in RetryListener as a companion to the existing onRetryPolicyExhaustion() callback. Closes gh-35442
This commit is contained in:
@@ -30,8 +30,11 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments.ArgumentSet;
|
||||
import org.junit.jupiter.params.provider.FieldSource;
|
||||
import org.junit.platform.commons.util.ExceptionUtils;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import org.springframework.util.backoff.BackOff;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.junit.jupiter.params.provider.Arguments.argumentSet;
|
||||
@@ -213,6 +216,36 @@ class RetryTemplateTests {
|
||||
verifyNoMoreInteractions(retryListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
void retryWithInterruptionDuringSleep() {
|
||||
Exception exception = new RuntimeException("Boom!");
|
||||
InterruptedException interruptedException = new InterruptedException();
|
||||
|
||||
// Simulates interruption during sleep:
|
||||
BackOff backOff = () -> () -> {
|
||||
throw ExceptionUtils.throwAsUncheckedException(interruptedException);
|
||||
};
|
||||
|
||||
RetryPolicy retryPolicy = RetryPolicy.builder().backOff(backOff).build();
|
||||
RetryTemplate retryTemplate = new RetryTemplate(retryPolicy);
|
||||
retryTemplate.setRetryListener(retryListener);
|
||||
Retryable<String> retryable = () -> {
|
||||
throw exception;
|
||||
};
|
||||
|
||||
assertThatExceptionOfType(RetryException.class)
|
||||
.isThrownBy(() -> retryTemplate.execute(retryable))
|
||||
.withMessageMatching("Unable to back off for retryable operation '.+?'")
|
||||
.withCause(interruptedException)
|
||||
.satisfies(throwable -> assertThat(throwable.getSuppressed()).containsExactly(exception))
|
||||
// TODO Fix retry count for InterruptedException scenario.
|
||||
// Retry count should actually be 0.
|
||||
.satisfies(throwable -> assertThat(throwable.getRetryCount()).isEqualTo(1))
|
||||
.satisfies(throwable -> inOrder.verify(retryListener).onRetryPolicyInterruption(retryPolicy, retryable, throwable));
|
||||
|
||||
verifyNoMoreInteractions(retryListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
void retryWithFailingRetryableAndMultiplePredicates() {
|
||||
var invocationCount = new AtomicInteger();
|
||||
|
||||
+10
@@ -92,4 +92,14 @@ class CompositeRetryListenerTests {
|
||||
verify(listener3).onRetryPolicyExhaustion(retryPolicy, retryable, exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void onRetryPolicyInterruption() {
|
||||
RetryException exception = new RetryException("", new Exception());
|
||||
compositeRetryListener.onRetryPolicyInterruption(retryPolicy, retryable, exception);
|
||||
|
||||
verify(listener1).onRetryPolicyInterruption(retryPolicy, retryable, exception);
|
||||
verify(listener2).onRetryPolicyInterruption(retryPolicy, retryable, exception);
|
||||
verify(listener3).onRetryPolicyInterruption(retryPolicy, retryable, exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user