From cd791e7246611ece07f8fba911c28c945da60011 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 29 Jun 2026 00:37:17 +0200 Subject: [PATCH 1/2] Support timeout option in RetryPolicySettings See gh-50891 Signed-off-by: Chris --- .../boot/retry/RetryPolicySettings.java | 31 +++++++++++++++++++ .../boot/retry/RetryPolicySettingsTests.java | 5 +++ 2 files changed, 36 insertions(+) diff --git a/core/spring-boot/src/main/java/org/springframework/boot/retry/RetryPolicySettings.java b/core/spring-boot/src/main/java/org/springframework/boot/retry/RetryPolicySettings.java index 6b406e2850c..1eb3a96c356 100644 --- a/core/spring-boot/src/main/java/org/springframework/boot/retry/RetryPolicySettings.java +++ b/core/spring-boot/src/main/java/org/springframework/boot/retry/RetryPolicySettings.java @@ -56,6 +56,11 @@ public final class RetryPolicySettings { */ public static final Duration DEFAULT_MAX_DELAY = Duration.ofMillis(RetryPolicy.Builder.DEFAULT_MAX_DELAY); + /** + * Default maximum elapsed time (infinite). + */ + public static final Duration DEFAULT_TIMEOUT = RetryPolicy.withDefaults().getTimeout(); + private List> exceptionIncludes = new ArrayList<>(); private List> exceptionExcludes = new ArrayList<>(); @@ -72,6 +77,8 @@ public final class RetryPolicySettings { private Duration maxDelay = DEFAULT_MAX_DELAY; + private Duration timeout = DEFAULT_TIMEOUT; + private @Nullable Function factory; /** @@ -89,6 +96,7 @@ public final class RetryPolicySettings { map.from(this::getJitter).to(builder::jitter); map.from(this::getMultiplier).to(builder::multiplier); map.from(this::getMaxDelay).to(builder::maxDelay); + map.from(this::getTimeout).to(builder::timeout); return (this.factory != null) ? this.factory.apply(builder) : builder.build(); } @@ -250,6 +258,29 @@ public final class RetryPolicySettings { this.maxDelay = maxDelay; } + /** + * Return the timeout for the maximum amount of elapsed time allowed for the initial + * invocation and any subsequent retry attempts, including delays. + * @return the maximum duration the request can take + */ + public Duration getTimeout() { + return this.timeout; + } + + /** + * Specify a timeout for the maximum amount of elapsed time allowed for the initial + * invocation and any subsequent retry attempts, including delays. + *

+ * The default is {@link Duration#ZERO}, which signals that no timeout should be + * applied. + * @param timeout the timeout, typically in milliseconds or seconds; must be greater + * than or equal to zero + * @see #DEFAULT_TIMEOUT + */ + public void setTimeout(Duration timeout) { + this.timeout = timeout; + } + /** * Set the factory to use to create the {@link RetryPolicy}, or {@code null} to use * the default. The function takes a {@link Builder RetryPolicy.Builder} initialized diff --git a/core/spring-boot/src/test/java/org/springframework/boot/retry/RetryPolicySettingsTests.java b/core/spring-boot/src/test/java/org/springframework/boot/retry/RetryPolicySettingsTests.java index cd42a3734ba..7254f7e3191 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/retry/RetryPolicySettingsTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/retry/RetryPolicySettingsTests.java @@ -98,6 +98,11 @@ class RetryPolicySettingsTests { assertThat(new RetryPolicySettings().getExceptionPredicate()).isNull(); } + @Test + void getDefaultTimeout() { + assertThat(new RetryPolicySettings().getTimeout()).isEqualTo(RetryPolicy.withDefaults().getTimeout()); + } + @Test void createRetryPolicyWithExceptionPredicate() { IllegalArgumentException exception = new IllegalArgumentException("test"); From b77f7fbc9353fc593315b5fb0c13b92801f8353a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 28 Jul 2026 06:44:52 +0200 Subject: [PATCH 2/2] Polish "Support timeout option in RetryPolicySettings" See gh-50891 --- .../boot/retry/RetryPolicySettings.java | 65 ++++++++++--------- .../boot/retry/RetryPolicySettingsTests.java | 8 +-- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/core/spring-boot/src/main/java/org/springframework/boot/retry/RetryPolicySettings.java b/core/spring-boot/src/main/java/org/springframework/boot/retry/RetryPolicySettings.java index 1eb3a96c356..80f6d7320bb 100644 --- a/core/spring-boot/src/main/java/org/springframework/boot/retry/RetryPolicySettings.java +++ b/core/spring-boot/src/main/java/org/springframework/boot/retry/RetryPolicySettings.java @@ -41,6 +41,12 @@ public final class RetryPolicySettings { */ public static final long DEFAULT_MAX_RETRIES = RetryPolicy.Builder.DEFAULT_MAX_RETRIES; + /** + * Default maximum elapsed time (infinite). + * @since 4.2.0 + */ + public static final Duration DEFAULT_TIMEOUT = RetryPolicy.Builder.DEFAULT_TIMEOUT; + /** * Default initial delay. */ @@ -56,11 +62,6 @@ public final class RetryPolicySettings { */ public static final Duration DEFAULT_MAX_DELAY = Duration.ofMillis(RetryPolicy.Builder.DEFAULT_MAX_DELAY); - /** - * Default maximum elapsed time (infinite). - */ - public static final Duration DEFAULT_TIMEOUT = RetryPolicy.withDefaults().getTimeout(); - private List> exceptionIncludes = new ArrayList<>(); private List> exceptionExcludes = new ArrayList<>(); @@ -69,6 +70,8 @@ public final class RetryPolicySettings { private Long maxRetries = DEFAULT_MAX_RETRIES; + private Duration timeout = DEFAULT_TIMEOUT; + private Duration delay = DEFAULT_DELAY; private @Nullable Duration jitter; @@ -77,8 +80,6 @@ public final class RetryPolicySettings { private Duration maxDelay = DEFAULT_MAX_DELAY; - private Duration timeout = DEFAULT_TIMEOUT; - private @Nullable Function factory; /** @@ -92,11 +93,11 @@ public final class RetryPolicySettings { map.from(this::getExceptionExcludes).to(builder::excludes); map.from(this::getExceptionPredicate).to(builder::predicate); map.from(this::getMaxRetries).to(builder::maxRetries); + map.from(this::getTimeout).to(builder::timeout); map.from(this::getDelay).to(builder::delay); map.from(this::getJitter).to(builder::jitter); map.from(this::getMultiplier).to(builder::multiplier); map.from(this::getMaxDelay).to(builder::maxDelay); - map.from(this::getTimeout).to(builder::timeout); return (this.factory != null) ? this.factory.apply(builder) : builder.build(); } @@ -176,6 +177,31 @@ public final class RetryPolicySettings { this.maxRetries = maxRetries; } + /** + * Return the timeout for the maximum amount of elapsed time allowed for the initial + * invocation and any subsequent retry attempts, including delays. + * @return the maximum duration the request can take + * @since 4.2.0 + */ + public Duration getTimeout() { + return this.timeout; + } + + /** + * Specify a timeout for the maximum amount of elapsed time allowed for the initial + * invocation and any subsequent retry attempts, including delays. + *

+ * The default is {@link Duration#ZERO}, which signals that no timeout should be + * applied. + * @param timeout the timeout, typically in milliseconds or seconds; must be greater + * than or equal to zero + * @since 4.2.0 + * @see #DEFAULT_TIMEOUT + */ + public void setTimeout(Duration timeout) { + this.timeout = timeout; + } + /** * Return the base delay after the initial invocation. * @return the base delay @@ -258,29 +284,6 @@ public final class RetryPolicySettings { this.maxDelay = maxDelay; } - /** - * Return the timeout for the maximum amount of elapsed time allowed for the initial - * invocation and any subsequent retry attempts, including delays. - * @return the maximum duration the request can take - */ - public Duration getTimeout() { - return this.timeout; - } - - /** - * Specify a timeout for the maximum amount of elapsed time allowed for the initial - * invocation and any subsequent retry attempts, including delays. - *

- * The default is {@link Duration#ZERO}, which signals that no timeout should be - * applied. - * @param timeout the timeout, typically in milliseconds or seconds; must be greater - * than or equal to zero - * @see #DEFAULT_TIMEOUT - */ - public void setTimeout(Duration timeout) { - this.timeout = timeout; - } - /** * Set the factory to use to create the {@link RetryPolicy}, or {@code null} to use * the default. The function takes a {@link Builder RetryPolicy.Builder} initialized diff --git a/core/spring-boot/src/test/java/org/springframework/boot/retry/RetryPolicySettingsTests.java b/core/spring-boot/src/test/java/org/springframework/boot/retry/RetryPolicySettingsTests.java index 7254f7e3191..5ee43329536 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/retry/RetryPolicySettingsTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/retry/RetryPolicySettingsTests.java @@ -98,11 +98,6 @@ class RetryPolicySettingsTests { assertThat(new RetryPolicySettings().getExceptionPredicate()).isNull(); } - @Test - void getDefaultTimeout() { - assertThat(new RetryPolicySettings().getTimeout()).isEqualTo(RetryPolicy.withDefaults().getTimeout()); - } - @Test void createRetryPolicyWithExceptionPredicate() { IllegalArgumentException exception = new IllegalArgumentException("test"); @@ -120,6 +115,7 @@ class RetryPolicySettingsTests { void createRetryPolicyWithDefaultsMatchesBackOffDefaults() { RetryPolicy defaultRetryPolicy = RetryPolicy.builder().build(); RetryPolicy retryPolicy = new RetryPolicySettings().createRetryPolicy(); + assertThat(retryPolicy.getTimeout()).isEqualByComparingTo(defaultRetryPolicy.getTimeout()); assertThat(retryPolicy.getBackOff()).isInstanceOf(ExponentialBackOff.class); ExponentialBackOff defaultBackOff = (ExponentialBackOff) defaultRetryPolicy.getBackOff(); ExponentialBackOff backOff = (ExponentialBackOff) retryPolicy.getBackOff(); @@ -134,11 +130,13 @@ class RetryPolicySettingsTests { void createRetryPolicyWithCustomAttributes() { RetryPolicySettings settings = new RetryPolicySettings(); settings.setMaxRetries(10L); + settings.setTimeout(Duration.ofHours(1)); settings.setDelay(Duration.ofSeconds(2)); settings.setJitter(Duration.ofMillis(500)); settings.setMultiplier(2.0); settings.setMaxDelay(Duration.ofSeconds(20)); RetryPolicy retryPolicy = settings.createRetryPolicy(); + assertThat(retryPolicy.getTimeout()).isEqualTo(Duration.ofHours(1)); assertThat(retryPolicy.getBackOff()).isInstanceOfSatisfying(ExponentialBackOff.class, (backOff) -> { assertThat(backOff.getMaxAttempts()).isEqualTo(10); assertThat(backOff.getInitialInterval()).isEqualTo(2000);