ExponentialBackoff implements BackoffStrategy { @Override public Optional<Duration> nextBackoff() { retryNumber += 1; if (retryNumber > maxNumRetries) { return Optional.empty(); } int upperBound = (int) Math.pow(2, retryNumber); return Optional.of(Duration.ofNanos(Math.round(backoffSlotSize.toNanos() * random.getAsDouble() * upperBound))); } ExponentialBackoff(int maxNumRetries, Duration backoffSlotSize); @VisibleForTesting  ExponentialBackoff(int maxNumRetries, Duration backoffSlotSize, DoubleSupplier random); @Override Optional<Duration> nextBackoff(); @Override String toString();  }
@Test public void testNoRetry() { AtomicDouble random = new AtomicDouble(); ExponentialBackoff backoff = new ExponentialBackoff(0, ONE_SECOND, random::get); assertThat(backoff.nextBackoff()).isEmpty(); }
@Test public void testRetriesCorrectNumberOfTimesAndFindsRandomBackoffWithInExponentialInterval() { AtomicDouble random = new AtomicDouble(); ExponentialBackoff backoff = new ExponentialBackoff(3, ONE_SECOND, random::get); random.set(1.0); assertThat(backoff.nextBackoff()).contains(ONE_SECOND.multipliedBy(2)); random.set(1.0); assertThat(backoff.nextBackoff()).contains(ONE_SECOND.multipliedBy(4)); random.set(0.5); assertThat(backoff.nextBackoff()).contains(ONE_SECOND.multipliedBy(4 )); assertThat(backoff.nextBackoff()).isEmpty(); }