static IntervalFunction ofDefaults() { return of(DEFAULT_INITIAL_INTERVAL); }
static IntervalFunction ofExponentialBackoff(long initialIntervalMillis) { return ofExponentialBackoff(initialIntervalMillis, DEFAULT_MULTIPLIER); }
static IntervalFunction ofExponentialRandomBackoff( long initialIntervalMillis ) { return ofExponentialRandomBackoff(initialIntervalMillis, DEFAULT_MULTIPLIER); }
@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(); }
@Test public void shouldPassPositiveMultiplier() { // Given final Duration duration = Duration.ofMillis(100); final float greaterThanOneMultiplier = 1.0001f; // When IntervalFunction.ofExponentialBackoff(duration, greaterThanOneMultiplier); IntervalFunction.ofExponentialRandomBackoff(duration, greaterThanOneMultiplier); }
@Test public void generatesExponentialIntervals() { final IntervalFunction f = IntervalFunction.ofExponentialBackoff(100, 1.5); long prevV = f.apply(1); for (int i = 2; i < 50; i++) { //When final long v = f.apply(i); // Then Assertions.assertThat(v).isGreaterThan(prevV); prevV = v; } }
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 shouldPassPositiveRandomizationFactor() { // Given final Duration duration = Duration.ofMillis(100); final float multiplier = 1.5f; final List<Float> correctFactors = List.of(0.0f, 0.25f, 0.5f, 0.75f, 0.1f); // When correctFactors.forEach(v -> IntervalFunction.ofRandomized(duration, v)); correctFactors.forEach(v -> IntervalFunction.ofExponentialRandomBackoff(duration, multiplier, v)); }
static IntervalFunction ofRandomized() { return ofRandomized(DEFAULT_INITIAL_INTERVAL, DEFAULT_RANDOMIZATION_FACTOR); }
@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 shouldRejectOutOfBoundsMultiplier() { // Given final Duration duration = Duration.ofMillis(100); final float lessThenOneMultiplier = 0.9999f; // When final List<Try> tries = List.of( Try.of(() -> IntervalFunction.ofExponentialBackoff(duration, lessThenOneMultiplier)), Try.of(() -> IntervalFunction.ofExponentialRandomBackoff(duration, lessThenOneMultiplier)) ); // Then Assertions.assertThat(tries.forAll(Try::isFailure)).isTrue(); Assertions.assertThat(tries.map(Try::getCause).forAll(t -> t instanceof IllegalArgumentException)).isTrue(); }
static IntervalFunction ofRandomized(long interval) { return ofRandomized(interval, DEFAULT_RANDOMIZATION_FACTOR); }
static IntervalFunction of(Duration interval, Function<Long, Long> backoffFunction) { return of(interval.toMillis(), backoffFunction); }
static IntervalFunction ofExponentialRandomBackoff() { return ofExponentialRandomBackoff(DEFAULT_INITIAL_INTERVAL, DEFAULT_MULTIPLIER, DEFAULT_RANDOMIZATION_FACTOR); }
static IntervalFunction ofExponentialBackoff() { return ofExponentialBackoff(DEFAULT_INITIAL_INTERVAL, DEFAULT_MULTIPLIER); }
static IntervalFunction ofRandomized(Duration interval, double randomizationFactor) { return ofRandomized(interval.toMillis(), randomizationFactor); }
static IntervalFunction of(Duration interval) { return of(interval.toMillis()); }
static IntervalFunction ofExponentialRandomBackoff( long initialIntervalMillis, double multiplier ) { return ofExponentialRandomBackoff(initialIntervalMillis, multiplier, DEFAULT_RANDOMIZATION_FACTOR); }