RandomJitter implements Jitter { @Override public Duration apply(BackoffDelay backoff) { if (backoff.delay.compareTo(backoff.min) < 0) { throw new IllegalArgumentException("jitter can only be applied on a delay that is >= to min backoff"); } if (backoff.delay.compareTo(backoff.max) > 0) { throw new IllegalArgumentException("jitter can only be applied on a delay that is <= to max backoff"); } if (backoff.delay.isZero()) { return backoff.delay; } ThreadLocalRandom random = ThreadLocalRandom.current(); long jitterOffset = jitterOffsetCapped(backoff); long lowBound = lowJitterBound(backoff, jitterOffset); long highBound = highJitterBound(backoff, jitterOffset); long jitter; if (highBound == lowBound) { if (highBound == 0) jitter = 0; else jitter = random.nextLong(highBound); } else { jitter = random.nextLong(lowBound, highBound); } return backoff.delay.plusMillis(jitter); } RandomJitter(double randomFactor); @Override Duration apply(BackoffDelay backoff); @Override String toString();  }
@Test public void applyToDelayUnderMinRejected() { Duration min = Duration.ofMillis(100); Duration max = Duration.ofMillis(100); Duration delay = Duration.ofMillis(99); BackoffDelay bd = new BackoffDelay(min, max, delay); RandomJitter jitter = new RandomJitter(0.5); assertThatIllegalArgumentException().isThrownBy(() -> jitter.apply(bd)) .withMessage("jitter can only be applied on a delay that is >= to min backoff"); }
@Test public void applyToDelayOverMaxRejected() { Duration min = Duration.ofMillis(100); Duration max = Duration.ofMillis(200); Duration delay = Duration.ofMillis(201); BackoffDelay bd = new BackoffDelay(min, max, delay); RandomJitter jitter = new RandomJitter(0.5); assertThatIllegalArgumentException().isThrownBy(() -> jitter.apply(bd)) .withMessage("jitter can only be applied on a delay that is <= to max backoff"); }
@Test public void propertyJitterDoesntCrossMinMax() { Gen<Long> minGen = SourceDSL.longs() .between(0, Long.MAX_VALUE - 2); Gen<Long> delayGen = minGen.flatMap(d -> SourceDSL.longs().between(d, Long.MAX_VALUE - 1)); Gen<Long> maxGen = delayGen.flatMap(d -> SourceDSL.longs().between(d, Long.MAX_VALUE)); QuickTheory.qt() .withGenerateAttempts(1000) .forAll(minGen, delayGen, maxGen) .assuming((min, d, max) -> min <= d && d <= max) .as((min, d, max) -> new BackoffDelay(Duration.ofMillis(min), Duration.ofMillis(max), Duration.ofMillis(d))) .checkAssert(bd -> assertThat(Jitter.random().apply(bd)) .isGreaterThanOrEqualTo(bd.min) .isLessThanOrEqualTo(bd.max)); }