Browse Source

Introduce RetryInterruptedException to address off-by-one error

Prior to this commit, a RetryException thrown for an
InterruptedException returned the wrong value from getRetryCount().
Specifically, the count was one more than it should have been, since the
suppressed exception list contains the initial exception as well as all
retry attempt exceptions.

To address that, this commit introduces an internal
RetryInterruptedException which accounts for this off-by-one error.

Closes gh-35434
pull/35447/head
Sam Brannen 3 months ago
parent
commit
1786eb2901
  1. 18
      spring-core/src/main/java/org/springframework/core/retry/RetryTemplate.java
  2. 4
      spring-core/src/test/java/org/springframework/core/retry/RetryTemplateTests.java

18
spring-core/src/main/java/org/springframework/core/retry/RetryTemplate.java

@ -164,7 +164,7 @@ public class RetryTemplate implements RetryOperations { @@ -164,7 +164,7 @@ public class RetryTemplate implements RetryOperations {
}
catch (InterruptedException interruptedException) {
Thread.currentThread().interrupt();
RetryException retryException = new RetryException(
RetryException retryException = new RetryInterruptedException(
"Unable to back off for retryable operation '%s'".formatted(retryableName),
interruptedException);
exceptions.forEach(retryException::addSuppressed);
@ -200,4 +200,20 @@ public class RetryTemplate implements RetryOperations { @@ -200,4 +200,20 @@ public class RetryTemplate implements RetryOperations {
}
}
private static class RetryInterruptedException extends RetryException {
private static final long serialVersionUID = 1L;
RetryInterruptedException(String message, InterruptedException cause) {
super(message, cause);
}
@Override
public int getRetryCount() {
return (getSuppressed().length - 1);
}
}
}

4
spring-core/src/test/java/org/springframework/core/retry/RetryTemplateTests.java

@ -238,9 +238,7 @@ class RetryTemplateTests { @@ -238,9 +238,7 @@ class RetryTemplateTests {
.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 -> assertThat(throwable.getRetryCount()).isZero())
.satisfies(throwable -> inOrder.verify(retryListener).onRetryPolicyInterruption(retryPolicy, retryable, throwable));
verifyNoMoreInteractions(retryListener);

Loading…
Cancel
Save