static IntervalFunction ofExponentialRandomBackoff( long initialIntervalMillis, double multiplier, double randomizationFactor ) { checkInterval(initialIntervalMillis); checkMultiplier(multiplier); checkRandomizationFactor(randomizationFactor); return (attempt) -> { checkAttempt(attempt); final long interval = of(initialIntervalMillis, (x) -> (long) (x * multiplier)).apply(attempt); return (long) randomize(interval, randomizationFactor); }; }
@Test public void generatesRandomizedIntervals() { final IntervalFunction f = IntervalFunction.ofRandomized(100, 0.5); for (int i = 1; i < 50; i++) { //When final long delay = f.apply(i); // Then Assertions.assertThat(delay).isGreaterThanOrEqualTo(50).isLessThanOrEqualTo(150); } }
@Test public void generatesExponentialRandomIntervals() { final IntervalFunction f = IntervalFunction.ofExponentialRandomBackoff(100, 1.5, 0.5); long expectedV = 100; for (int i = 1; i < 50; i++) { //When final long v = f.apply(i); // Then Assertions.assertThat(v) .isGreaterThanOrEqualTo( (long)(expectedV * 0.5) - 1) .isLessThanOrEqualTo((long)(expectedV * 1.5) + 1); expectedV = (long) (expectedV * 1.5); } } }
@Test public void shouldPassAttemptGreaterThenZero() { // Given final List<IntervalFunction> fns = List.of( IntervalFunction.ofDefaults(), IntervalFunction.ofRandomized(), IntervalFunction.ofExponentialBackoff(), IntervalFunction.ofExponentialRandomBackoff() ); // When final List<Try> tries1 = fns.map(fn -> Try.of(() -> fn.apply(1))); final List<Try> tries2 = fns.map(fn -> Try.of(() -> fn.apply(2))); // Then Assertions.assertThat(tries1.forAll(Try::isFailure)).isFalse(); Assertions.assertThat(tries2.forAll(Try::isFailure)).isFalse(); }
@Test public void shouldRejectAttemptLessThenOne() { // Given final List<IntervalFunction> fns = List.of( IntervalFunction.ofDefaults(), IntervalFunction.ofRandomized(), IntervalFunction.ofExponentialBackoff(), IntervalFunction.ofExponentialRandomBackoff() ); // When final List<Try> tries = fns.map(fn -> Try.of(() -> fn.apply(0))); // Then Assertions.assertThat(tries.forAll(Try::isFailure)).isTrue(); Assertions.assertThat(tries.map(Try::getCause).forAll(t -> t instanceof IllegalArgumentException)).isTrue(); }