Package org.microproxy
Class RetryBackoff
java.lang.Object
org.microproxy.RetryBackoff
How long the proxy waits before trying the next route after a connection attempt failed: the
next chained proxy, or the direct fallback, that the
ChainedProxyManager offered. Set for
the whole server with HttpProxyServerBootstrap.withChainedProxyRetryBackoff(RetryBackoff)
and for one request with HttpFilters.chainedProxyRetryBackoff().
The built-in policies:
none(): no waiting; the next route is tried at once (the default).fixed(Duration): the same wait before every retry.exponential(Duration, Duration): deterministic doubling,initial,2 x initial,4 x initial, ... capped atmax, without jitter. Each wait is known in advance, which suits tests that depend on exact timings, as LittleProxy forks that added a backoff did.exponentialWithJitter(Duration, Duration): the same doubling ceiling, but each wait is a uniformly random time between zero and that ceiling ("full jitter"), so that clients failing together do not retry together.
Attempts are counted from the first: the wait before attempt n + 1 is delay(n), where n attempts have failed.
The waits of one request add up to at most a budget; once it is spent, the remaining routes
are tried without waiting. By default the budget is the request's connect timeout, at most 30
seconds, so a long route list cannot hold a request for minutes. withBudget(Duration)
sets another total, and unbudgeted() lifts it: every wait the policy names is then
waited, as LittleProxy forks that added a backoff did. Either way a client that leaves ends the
wait.
Instances are immutable and thread-safe, and equal when they wait the same way.
-
Method Summary
Modifier and TypeMethodDescriptionbudget()The budget set withwithBudget(Duration): the most all waits of one request may add up to.ceiling(int failedAttempts) The longest wait before the attempt that followsfailedAttemptsfailed ones:min(max, initial x 2^(failedAttempts-1)).delay(int failedAttempts) The wait before the attempt that followsfailedAttemptsfailed ones, drawing the jitter (if any) from aThreadLocalRandom.delay(int failedAttempts, RandomGenerator random) The wait before the attempt that followsfailedAttemptsfailed ones.booleanstatic RetryBackoffexponential(Duration initial, Duration max) Deterministic doubling: waitsinitialbefore the second attempt, then twice as long before each further one, capped atmax, without jitter.static RetryBackoffexponentialWithJitter(Duration initial, Duration max) Exponential backoff with "full jitter": before attemptn + 1, a uniformly random time from zero up to (not including) theexponential(java.time.Duration, java.time.Duration)waitmin(max, initial x 2^(n-1)).static RetryBackoffThe same wait before every retry, without jitter:exponential(delay, delay).inthashCode()initial()The wait before the second attempt (its ceiling, with jitter); zero fornone().booleanisNone()Whether this policy never waits (none()).booleanWhether the waits of one request have no budget (unbudgeted()).booleanjittered()Whether the waits are random (exponentialWithJitter(java.time.Duration, java.time.Duration)) rather than exact.max()The longest wait; zero fornone().static RetryBackoffnone()No waiting: the next route is tried as soon as an attempt fails.toString()Returns how the policy is made, such asexponential(PT0.1S, PT2S)orfixed(PT1S).unbudgeted().This policy without a budget: every wait it names is waited, however long they add up to (the route list and each wait'smax()still bound them), as LittleProxy forks that added a backoff did.withBudget(Duration total) This policy withtotalas the most that all waits of one request may add up to, in place of the default (the request's connect timeout, at most 30 seconds).This policy with the default budget again: the request's connect timeout, at most 30 seconds.
-
Method Details
-
none
No waiting: the next route is tried as soon as an attempt fails.- Returns:
- the policy that never waits
-
fixed
The same wait before every retry, without jitter:exponential(delay, delay).- Parameters:
delay- the wait, positive- Returns:
- the policy
- Throws:
IllegalArgumentException- ifdelayis not positiveNullPointerException- ifdelayis null
-
exponential
Deterministic doubling: waitsinitialbefore the second attempt, then twice as long before each further one, capped atmax, without jitter. For exampleexponential(100 ms, 1 s)waits 100, 200, 400, 800, 1000, 1000, ... ms.- Parameters:
initial- the wait before the second attempt, positivemax- the longest wait, at leastinitial- Returns:
- the policy
- Throws:
IllegalArgumentException- ifinitialis not positive ormaxis below itNullPointerException- if either is null
-
exponentialWithJitter
Exponential backoff with "full jitter": before attemptn + 1, a uniformly random time from zero up to (not including) theexponential(java.time.Duration, java.time.Duration)waitmin(max, initial x 2^(n-1)). On average it waits half as long asexponential(java.time.Duration, java.time.Duration), and spreads the retries of clients that failed at the same time.- Parameters:
initial- the ceiling of the wait before the second attempt, positivemax- the ceiling of every wait, at leastinitial- Returns:
- the policy
- Throws:
IllegalArgumentException- ifinitialis not positive ormaxis below itNullPointerException- if either is null
-
withBudget
This policy withtotalas the most that all waits of one request may add up to, in place of the default (the request's connect timeout, at most 30 seconds). Once the budget is spent, the remaining routes are tried without waiting; the wait that would overrun it is shortened to what is left. A budget too long to count in nanoseconds (about 292 years) is no budget, asunbudgeted().none()never waits, and stays as it is.- Parameters:
total- the budget for all waits of one request, positive- Returns:
- the policy with that budget
- Throws:
IllegalArgumentException- iftotalis not positiveNullPointerException- iftotalis null
-
unbudgeted
This policy without a budget: every wait it names is waited, however long they add up to (the route list and each wait'smax()still bound them), as LittleProxy forks that added a backoff did. A client that leaves still ends the wait.none()never waits, and stays as it is.- Returns:
- the policy without a budget
-
withDefaultBudget
This policy with the default budget again: the request's connect timeout, at most 30 seconds.- Returns:
- the policy with the default budget
-
budget
The budget set withwithBudget(Duration): the most all waits of one request may add up to. Empty when the default budget applies (the request's connect timeout, at most 30 seconds) and when there is none (isUnbudgeted()).- Returns:
- the policy's own budget, or empty
-
isUnbudgeted
public boolean isUnbudgeted()Whether the waits of one request have no budget (unbudgeted()).- Returns:
- whether every wait the policy names is waited
-
isNone
public boolean isNone()Whether this policy never waits (none()).- Returns:
- whether every delay is zero
-
initial
The wait before the second attempt (its ceiling, with jitter); zero fornone().- Returns:
- the initial wait
-
max
The longest wait; zero fornone().- Returns:
- the maximum wait
-
jittered
public boolean jittered()Whether the waits are random (exponentialWithJitter(java.time.Duration, java.time.Duration)) rather than exact.- Returns:
- whether the policy jitters its waits
-
ceiling
The longest wait before the attempt that followsfailedAttemptsfailed ones:min(max, initial x 2^(failedAttempts-1)). Without jitter it is the wait itself.- Parameters:
failedAttempts- how many attempts have failed, at least 1- Returns:
- the wait's ceiling
- Throws:
IllegalArgumentException- iffailedAttemptsis below 1
-
delay
The wait before the attempt that followsfailedAttemptsfailed ones, drawing the jitter (if any) from aThreadLocalRandom.- Parameters:
failedAttempts- how many attempts have failed, at least 1- Returns:
- the wait
- Throws:
IllegalArgumentException- iffailedAttemptsis below 1
-
delay
The wait before the attempt that followsfailedAttemptsfailed ones. With jitter it is theceiling(int)scaled byrandom.nextDouble()(clamped to[0, 1]). Without jitter it is the ceiling, andrandomis not used.- Parameters:
failedAttempts- how many attempts have failed, at least 1random- where the jitter comes from- Returns:
- the wait
- Throws:
IllegalArgumentException- iffailedAttemptsis below 1
-
equals
-
hashCode
public int hashCode() -
toString
Returns how the policy is made, such asexponential(PT0.1S, PT2S)orfixed(PT1S).unbudgeted().
-