diff --git a/extended/src/main/java/io/kubernetes/client/extended/workqueue/ratelimiter/ItemExponentialFailureRateLimiter.java b/extended/src/main/java/io/kubernetes/client/extended/workqueue/ratelimiter/ItemExponentialFailureRateLimiter.java index 2ebd9e2b92..871a5504bb 100644 --- a/extended/src/main/java/io/kubernetes/client/extended/workqueue/ratelimiter/ItemExponentialFailureRateLimiter.java +++ b/extended/src/main/java/io/kubernetes/client/extended/workqueue/ratelimiter/ItemExponentialFailureRateLimiter.java @@ -37,7 +37,7 @@ public ItemExponentialFailureRateLimiter(Duration baseDelay, Duration maxDelay) public Duration when(T item) { long exp = failures.computeIfAbsent(item, k -> new AtomicLong()).getAndIncrement(); long d = maxDelay.toMillis() >> exp; - return d > baseDelay.toMillis() ? baseDelay.multipliedBy(1 << exp) : maxDelay; + return d > baseDelay.toMillis() ? baseDelay.multipliedBy(1L << exp) : maxDelay; } @Override diff --git a/extended/src/test/java/io/kubernetes/client/extended/workqueue/ratelimiter/ItemExponentialFailureRateLimiterTest.java b/extended/src/test/java/io/kubernetes/client/extended/workqueue/ratelimiter/ItemExponentialFailureRateLimiterTest.java index e5d4d214a3..8aa6f6a769 100644 --- a/extended/src/test/java/io/kubernetes/client/extended/workqueue/ratelimiter/ItemExponentialFailureRateLimiterTest.java +++ b/extended/src/test/java/io/kubernetes/client/extended/workqueue/ratelimiter/ItemExponentialFailureRateLimiterTest.java @@ -94,4 +94,23 @@ void negativeMaxDelay() { assertThat(rateLimiter.when("one")).isEqualTo(Duration.ofSeconds(-1000)); assertThat(rateLimiter.when("one")).isEqualTo(Duration.ofSeconds(-1000)); } + + @Test + void highFailureCountStaysPositive() { + // maxDelay is large enough that the baseDelay*2^exp branch is still selected at exp == 31. + // The multiplier must be computed in long arithmetic; an int shift (1 << 31) overflows to + // Integer.MIN_VALUE and yields a negative backoff, which is never a valid delay. + Duration baseDelay = Duration.ofMillis(1); + Duration maxDelay = Duration.ofMillis(1L << 40); + RateLimiter rateLimiter = new ItemExponentialFailureRateLimiter<>(baseDelay, maxDelay); + + // Advance to exp == 31 (the 32nd call returns the delay computed with exp == 31). + for (int i = 0; i < 31; i++) { + rateLimiter.when("item"); + } + + Duration backoff = rateLimiter.when("item"); + assertThat(backoff).isPositive(); + assertThat(backoff).isEqualTo(baseDelay.multipliedBy(1L << 31)); + } }