Use double division to calculate applied jitter in ExponentialBackOff

In order to avoid the staircase scaling effect that results from our
current use of integer division, this commit revises applyJitter(long)
in ExponentialBackOffExecution to use floating-point (double) division
to calculate the applied jitter.

Closes gh-36943
This commit is contained in:
Sam Brannen
2026-06-17 13:02:49 +02:00
parent 472e610c4a
commit b611fcf114
2 changed files with 16 additions and 1 deletions
@@ -317,7 +317,7 @@ public class ExponentialBackOff implements BackOff {
long initialInterval = getInitialInterval();
// When initialInterval is 0 the interval never grows, so the scale factor
// stays at its baseline value of 1 and the full configured jitter is applied.
long applicableJitter = jitter * (initialInterval > 0 ? (interval / initialInterval) : 1);
long applicableJitter = (long) (jitter * (initialInterval > 0 ? ((double) interval / initialInterval) : 1));
long min = Math.max(interval - applicableJitter, initialInterval);
long max = Math.min(interval + applicableJitter, getMaxInterval());
return min + (long) (Math.random() * (max - min));
@@ -131,6 +131,21 @@ class ExponentialBackOffTests {
assertThatNoException().isThrownBy(execution::nextBackOff);
}
@Test // gh-36943
void jitterScalesProportionallyWithInterval() {
ExponentialBackOff backOff = new ExponentialBackOff();
backOff.setJitter(100);
BackOffExecution execution = backOff.start();
// Default: initialInterval=2000, multiplier=1.5
// Attempt 1: interval=2000, scale=1.0, applicableJitter=100 → [max(1900,2000), 2100) = [2000, 2099]
assertThat(execution.nextBackOff()).isBetween(2000L, 2099L);
// Attempt 2: interval=3000, scale=1.5, applicableJitter=150 → [max(2850,2000), 3150) = [2850, 3149]
assertThat(execution.nextBackOff()).isBetween(2850L, 3149L);
// Attempt 3: interval=4500, scale=2.25, applicableJitter=225 → [max(4275,2000), 4725) = [4275, 4724]
assertThat(execution.nextBackOff()).isBetween(4275L, 4724L);
}
@Test
void maxIntervalReachedImmediately() {
ExponentialBackOff backOff = new ExponentialBackOff(1000L, 2.0);