package io.github.resilience4j.retry; import io.vavr.collection.Stream; import java.time.Duration; import java.util.function.Function; import static io.github.resilience4j.retry.IntervalFunctionCompanion.*; import static java.util.Objects.requireNonNull; @FunctionalInterface public interface IntervalFunction extends Function<Integer, Long> { long DEFAULT_INITIAL_INTERVAL = 500; double DEFAULT_MULTIPLIER = 1.5; double DEFAULT_RANDOMIZATION_FACTOR = 0.5; static IntervalFunction ofDefaults() { return of(DEFAULT_INITIAL_INTERVAL); } static IntervalFunction of(long intervalMillis, Function<Long, Long> backoffFn) { checkInterval(intervalMillis); requireNonNull(backoffFn); return (attempt) -> { checkAttempt(attempt); return Stream.iterate(intervalMillis, backoffFn).get(attempt - 1); }; } static IntervalFunction of(Duration interval, Function<Long, Long> backoffFn) { return of(interval.toMillis(), backoffFn); } static IntervalFunction of(long intervalMillis) { return of(intervalMillis, x -> x); } static IntervalFunction of(Duration interval) { return of(interval.toMillis()); } static IntervalFunction ofRandomized(long intervalMillis, double randomizationFactor) { checkInterval(intervalMillis); checkRandomizationFactor(randomizationFactor); return (attempt) -> { checkAttempt(attempt); return (long) randomize(intervalMillis, randomizationFactor); }; } static IntervalFunction ofRandomized(Duration interval, double randomizationFactor) { return ofRandomized(interval.toMillis(), randomizationFactor); } static IntervalFunction ofRandomized(long interval) { return ofRandomized(interval, DEFAULT_RANDOMIZATION_FACTOR); } static IntervalFunction ofRandomized(Duration interval) { return ofRandomized(interval.toMillis(), DEFAULT_RANDOMIZATION_FACTOR); } static IntervalFunction ofRandomized() { return ofRandomized(DEFAULT_INITIAL_INTERVAL, DEFAULT_RANDOMIZATION_FACTOR); } static IntervalFunction ofExponentialBackoff(long initialIntervalMillis, double multiplier) { checkMultiplier(multiplier); return of(initialIntervalMillis, (x) -> (long) (x * multiplier)); } static IntervalFunction ofExponentialBackoff(Duration initialInterval, double multiplier) { return ofExponentialBackoff(initialInterval.toMillis(), multiplier); } static IntervalFunction ofExponentialBackoff(long initialIntervalMillis) { return ofExponentialBackoff(initialIntervalMillis, DEFAULT_MULTIPLIER); } static IntervalFunction ofExponentialBackoff(Duration initialInterval) { return ofExponentialBackoff(initialInterval.toMillis(), DEFAULT_MULTIPLIER); } static IntervalFunction ofExponentialBackoff() { return ofExponentialBackoff(DEFAULT_INITIAL_INTERVAL, DEFAULT_MULTIPLIER); } 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); }; } static IntervalFunction ofExponentialRandomBackoff( Duration initialInterval, double multiplier, double randomizationFactor ) { return ofExponentialRandomBackoff(initialInterval.toMillis(), multiplier, randomizationFactor); } static IntervalFunction ofExponentialRandomBackoff( long initialIntervalMillis, double multiplier ) { return ofExponentialRandomBackoff(initialIntervalMillis, multiplier, DEFAULT_RANDOMIZATION_FACTOR); } static IntervalFunction ofExponentialRandomBackoff( Duration initialInterval, double multiplier ) { return ofExponentialRandomBackoff(initialInterval.toMillis(), multiplier, DEFAULT_RANDOMIZATION_FACTOR); } static IntervalFunction ofExponentialRandomBackoff( long initialIntervalMillis ) { return ofExponentialRandomBackoff(initialIntervalMillis, DEFAULT_MULTIPLIER); } static IntervalFunction ofExponentialRandomBackoff( Duration initialInterval ) { return ofExponentialRandomBackoff(initialInterval.toMillis(), DEFAULT_MULTIPLIER); } static IntervalFunction ofExponentialRandomBackoff() { return ofExponentialRandomBackoff(DEFAULT_INITIAL_INTERVAL, DEFAULT_MULTIPLIER, DEFAULT_RANDOMIZATION_FACTOR); } } final class IntervalFunctionCompanion { private IntervalFunctionCompanion() { } static double randomize(final double current, final double randomizationFactor) { final double delta = randomizationFactor * current; final double min = current - delta; final double max = current + delta; return (min + (Math.random() * (max - min + 1))); } static void checkInterval(long v) { if (v < 10) { throw new IllegalArgumentException("Illegal argument interval: " + v + " millis"); } } static void checkMultiplier(double v) { if (v < 1.0) { throw new IllegalArgumentException("Illegal argument multiplier: " + v); } } static void checkRandomizationFactor(double v) { if (v < 0.0 || v >= 1.0) { throw new IllegalArgumentException("Illegal argument randomizationFactor: " + v); } } static void checkAttempt(long v) { if (v < 1) { throw new IllegalArgumentException("Illegal argument attempt: " + v); } } }