Update RetryPolicySettings to configure exception policy as well
Closes gh-47264
This commit is contained in:
parent
cb51a44e2b
commit
da8eac3483
|
@ -17,7 +17,10 @@
|
||||||
package org.springframework.boot.retry;
|
package org.springframework.boot.retry;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
@ -53,6 +56,12 @@ public final class RetryPolicySettings {
|
||||||
*/
|
*/
|
||||||
public static final Duration DEFAULT_MAX_DELAY = Duration.ofMillis(RetryPolicy.Builder.DEFAULT_MAX_DELAY);
|
public static final Duration DEFAULT_MAX_DELAY = Duration.ofMillis(RetryPolicy.Builder.DEFAULT_MAX_DELAY);
|
||||||
|
|
||||||
|
private List<Class<? extends Throwable>> exceptionIncludes = new ArrayList<>();
|
||||||
|
|
||||||
|
private List<Class<? extends Throwable>> exceptionExcludes = new ArrayList<>();
|
||||||
|
|
||||||
|
private @Nullable Predicate<Throwable> exceptionPredicate;
|
||||||
|
|
||||||
private Long maxAttempts = DEFAULT_MAX_ATTEMPTS;
|
private Long maxAttempts = DEFAULT_MAX_ATTEMPTS;
|
||||||
|
|
||||||
private Duration delay = DEFAULT_DELAY;
|
private Duration delay = DEFAULT_DELAY;
|
||||||
|
@ -72,6 +81,9 @@ public final class RetryPolicySettings {
|
||||||
public RetryPolicy createRetryPolicy() {
|
public RetryPolicy createRetryPolicy() {
|
||||||
PropertyMapper map = PropertyMapper.get();
|
PropertyMapper map = PropertyMapper.get();
|
||||||
RetryPolicy.Builder builder = RetryPolicy.builder();
|
RetryPolicy.Builder builder = RetryPolicy.builder();
|
||||||
|
map.from(this::getExceptionIncludes).to(builder::includes);
|
||||||
|
map.from(this::getExceptionExcludes).to(builder::excludes);
|
||||||
|
map.from(this::getExceptionPredicate).to(builder::predicate);
|
||||||
map.from(this::getMaxAttempts).to(builder::maxAttempts);
|
map.from(this::getMaxAttempts).to(builder::maxAttempts);
|
||||||
map.from(this::getDelay).to(builder::delay);
|
map.from(this::getDelay).to(builder::delay);
|
||||||
map.from(this::getJitter).to(builder::jitter);
|
map.from(this::getJitter).to(builder::jitter);
|
||||||
|
@ -80,6 +92,64 @@ public final class RetryPolicySettings {
|
||||||
return (this.factory != null) ? this.factory.apply(builder) : builder.build();
|
return (this.factory != null) ? this.factory.apply(builder) : builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the applicable exception types to attempt a retry for.
|
||||||
|
* <p>
|
||||||
|
* The default is empty, leading to a retry attempt for any exception.
|
||||||
|
* @return the applicable exception types
|
||||||
|
*/
|
||||||
|
public List<Class<? extends Throwable>> getExceptionIncludes() {
|
||||||
|
return this.exceptionIncludes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the applicable exception types to attempt a retry for by the given
|
||||||
|
* {@code includes}. Alternatively consider using {@link #getExceptionIncludes()} to
|
||||||
|
* mutate the existing list.
|
||||||
|
* @param includes the applicable exception types
|
||||||
|
*/
|
||||||
|
public void setExceptionIncludes(List<Class<? extends Throwable>> includes) {
|
||||||
|
this.exceptionIncludes = new ArrayList<>(includes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the non-applicable exception types to avoid a retry for.
|
||||||
|
* <p>
|
||||||
|
* The default is empty, leading to a retry attempt for any exception.
|
||||||
|
* @return the non-applicable exception types
|
||||||
|
*/
|
||||||
|
public List<Class<? extends Throwable>> getExceptionExcludes() {
|
||||||
|
return this.exceptionExcludes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the non-applicable exception types to attempt a retry for by the given
|
||||||
|
* {@code excludes}. Alternatively consider using {@link #getExceptionExcludes()} to
|
||||||
|
* mutate the existing list.
|
||||||
|
* @param excludes the non-applicable types
|
||||||
|
*/
|
||||||
|
public void setExceptionExcludes(List<Class<? extends Throwable>> excludes) {
|
||||||
|
this.exceptionExcludes = new ArrayList<>(excludes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the predicate to use to determine whether to retry a failed operation based
|
||||||
|
* on a given {@link Throwable}.
|
||||||
|
* @return the predicate to use
|
||||||
|
*/
|
||||||
|
public @Nullable Predicate<Throwable> getExceptionPredicate() {
|
||||||
|
return this.exceptionPredicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the predicate to use to determine whether to retry a failed operation based on
|
||||||
|
* a given {@link Throwable}.
|
||||||
|
* @param exceptionPredicate the predicate to use
|
||||||
|
*/
|
||||||
|
public void setExceptionPredicate(@Nullable Predicate<Throwable> exceptionPredicate) {
|
||||||
|
this.exceptionPredicate = exceptionPredicate;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the maximum number of retry attempts.
|
* Return the maximum number of retry attempts.
|
||||||
* @return the maximum number of retry attempts
|
* @return the maximum number of retry attempts
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
package org.springframework.boot.retry;
|
package org.springframework.boot.retry;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -25,6 +28,8 @@ import org.springframework.util.backoff.BackOff;
|
||||||
import org.springframework.util.backoff.ExponentialBackOff;
|
import org.springframework.util.backoff.ExponentialBackOff;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.BDDMockito.then;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,6 +39,78 @@ import static org.mockito.Mockito.mock;
|
||||||
*/
|
*/
|
||||||
class RetryPolicySettingsTests {
|
class RetryPolicySettingsTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void exceptionIncludesCanBeReplaced() {
|
||||||
|
RetryPolicySettings settings = new RetryPolicySettings();
|
||||||
|
settings.getExceptionIncludes().add(IllegalStateException.class);
|
||||||
|
settings.setExceptionIncludes(List.of(IllegalArgumentException.class));
|
||||||
|
assertThat(settings.getExceptionIncludes()).containsExactly(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void exceptionIncludesListIsCopied() {
|
||||||
|
RetryPolicySettings settings = new RetryPolicySettings();
|
||||||
|
List<Class<? extends Throwable>> includes = new ArrayList<>();
|
||||||
|
includes.add(IllegalStateException.class);
|
||||||
|
settings.setExceptionIncludes(includes);
|
||||||
|
includes.add(IllegalArgumentException.class);
|
||||||
|
assertThat(settings.getExceptionIncludes()).containsExactly(IllegalStateException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createRetryPolicyWithExceptionIncludes() {
|
||||||
|
RetryPolicySettings settings = new RetryPolicySettings();
|
||||||
|
settings.getExceptionIncludes().add(IllegalStateException.class);
|
||||||
|
RetryPolicy retryPolicy = settings.createRetryPolicy();
|
||||||
|
assertThat(retryPolicy.shouldRetry(new IllegalStateException("test"))).isTrue();
|
||||||
|
assertThat(retryPolicy.shouldRetry(new IllegalArgumentException("test"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void exceptionExcludesCanBeReplaced() {
|
||||||
|
RetryPolicySettings settings = new RetryPolicySettings();
|
||||||
|
settings.getExceptionExcludes().add(IllegalStateException.class);
|
||||||
|
settings.setExceptionExcludes(List.of(IllegalArgumentException.class));
|
||||||
|
assertThat(settings.getExceptionExcludes()).containsExactly(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void exceptionExcludesListIsCopied() {
|
||||||
|
RetryPolicySettings settings = new RetryPolicySettings();
|
||||||
|
List<Class<? extends Throwable>> excludes = new ArrayList<>();
|
||||||
|
excludes.add(IllegalStateException.class);
|
||||||
|
settings.setExceptionExcludes(excludes);
|
||||||
|
excludes.add(IllegalArgumentException.class);
|
||||||
|
assertThat(settings.getExceptionExcludes()).containsExactly(IllegalStateException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createRetryPolicyWithExceptionExcludes() {
|
||||||
|
RetryPolicySettings settings = new RetryPolicySettings();
|
||||||
|
settings.getExceptionExcludes().add(IllegalStateException.class);
|
||||||
|
RetryPolicy retryPolicy = settings.createRetryPolicy();
|
||||||
|
assertThat(retryPolicy.shouldRetry(new IllegalStateException("test"))).isFalse();
|
||||||
|
assertThat(retryPolicy.shouldRetry(new IllegalArgumentException("test"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getDefaultExceptionPredicate() {
|
||||||
|
assertThat(new RetryPolicySettings().getExceptionPredicate()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createRetryPolicyWithExceptionPredicate() {
|
||||||
|
IllegalArgumentException exception = new IllegalArgumentException("test");
|
||||||
|
Predicate<Throwable> exceptionPredicate = mock();
|
||||||
|
given(exceptionPredicate.test(exception)).willReturn(true);
|
||||||
|
RetryPolicySettings settings = new RetryPolicySettings();
|
||||||
|
settings.setExceptionPredicate(exceptionPredicate);
|
||||||
|
RetryPolicy retryPolicy = settings.createRetryPolicy();
|
||||||
|
assertThat(retryPolicy.shouldRetry(exception)).isTrue();
|
||||||
|
then(exceptionPredicate).should().test(exception);
|
||||||
|
then(exceptionPredicate).shouldHaveNoMoreInteractions();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createRetryPolicyWithDefaultsMatchesBackOffDefaults() {
|
void createRetryPolicyWithDefaultsMatchesBackOffDefaults() {
|
||||||
RetryPolicy defaultRetryPolicy = RetryPolicy.builder().build();
|
RetryPolicy defaultRetryPolicy = RetryPolicy.builder().build();
|
||||||
|
|
Loading…
Reference in New Issue