Skip to content

Avoid overflow for default maxDelay with timeUnit in @Retryable - #37525

Open
namest504 wants to merge 1 commit into
spring-projects:7.0.xfrom
namest504:retryable-timeunit-default-max-delay
Open

namest504 wants to merge 1 commit into
spring-projects:7.0.xfrom
namest504:retryable-timeunit-default-max-delay

Conversation

@namest504

Copy link
Copy Markdown

The default maxDelay of @Retryable is Long.MAX_VALUE, meant as "unlimited". RetryAnnotationBeanPostProcessor converts it using the configured timeUnit, so with timeUnit = TimeUnit.SECONDS the conversion to milliseconds in RetryPolicy.Builder.build() overflows, and with MINUTES or larger Duration.of() itself overflows.

As a result, any method declaring for example

@Retryable(delay = 1, timeUnit = TimeUnit.SECONDS)

fails on every invocation with ArithmeticException: long overflow before the target method is invoked, even if the first attempt would succeed. Reproduced with a Spring Boot 4.0.8 application (Spring Framework 7.0.9) and on main; the same applies to 7.0.x.

This change converts the attributes with TimeUnit.toMillis(), which saturates instead of overflowing, so that the default as well as any other large value is treated as unlimited, consistent with the default maxDelay in MethodRetrySpec. Units finer than milliseconds keep using Duration.of() to preserve precision. The added test covers SECONDS, MINUTES, and an explicit large maxDelay.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Oct 20, 2026
@sbrannen sbrannen added the in: core Issues in core modules (aop, beans, core, context, expression) label Oct 20, 2026
@sbrannen sbrannen self-assigned this Oct 20, 2026
@sbrannen sbrannen added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Oct 20, 2026
@sbrannen sbrannen added this to the 7.0.11 milestone Oct 20, 2026
The default maxDelay of `@Retryable` is Long.MAX_VALUE, which was
converted to a Duration in the configured timeUnit. With SECONDS this
overflowed in RetryPolicy.Builder.build() and with MINUTES or larger
already in Duration.of(), so every invocation of a method declaring
for example `@Retryable(delay = 1, timeUnit = TimeUnit.SECONDS)`
failed with an ArithmeticException.

Convert with TimeUnit.toMillis(), which saturates instead of
overflowing, so that the default as well as any other large value is
treated as unlimited, consistent with MethodRetrySpec. Units finer
than milliseconds keep using Duration.of() to preserve precision.

Closes spring-projectsgh-37525

Signed-off-by: Seungtaek Lim <namest504@gmail.com>
@sbrannen
sbrannen force-pushed the retryable-timeunit-default-max-delay branch from ef5a8c0 to 982d021 Compare October 20, 2026 10:13
@sbrannen
sbrannen changed the base branch from main to 7.0.x October 20, 2026 10:13

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants