pub struct RetryPolicy {
pub max_attempts: u32,
pub initial_backoff: Duration,
pub max_backoff: Duration,
pub multiplier: u32,
pub jitter: bool,
pub max_advice: Duration,
}Expand description
How many times to repeat a failed effect, and how far apart.
The defaults are safe rather than timid: three attempts with exponential
backoff. They are safe because Disposition
gates them — an effect whose failure is in-doubt is not repeated under this
policy unless its Recovery permits guessing, no
matter what max_attempts says.
Fields§
§max_attempts: u32Total attempts including the first. 1 means “never repeat”.
initial_backoff: DurationDelay before the second attempt.
max_backoff: DurationCeiling on the delay, however many attempts have passed.
Also the bound on how long one effect holds a worker — see the module
docs. For waits longer than a few seconds, the run should suspend
instead: that is StepCtx::sleep, not
a retry policy.
multiplier: u32Growth factor per attempt. An integer, so the schedule is exactly reproducible on any platform without depending on float rounding.
jitter: boolWhether to spread the delay across runs. See the module docs — this is derived from a hash, not drawn from an RNG.
max_advice: DurationThe longest this run will wait because a peer named a time.
A separate ceiling from max_backoff, because the
two bound different risks. max_backoff bounds a guess: nobody knows
when the service recovers, so waiting long is waste. This bounds
somebody else’s word — a Retry-After from the one party with an
interest in never being called again — and the number that makes the
wait useful is theirs, not ours. Sharing one ceiling would mean either
guessing for as long as a peer may demand, or discarding advice that is
merely longer than a guess would have been.
A minute covers every published provider rate-limit window and is short enough that a hostile or broken value costs one wasted minute of one worker. Advice past it is clamped, not discarded: waiting part of a window is closer to right than ignoring it, and if the window really was longer the next refusal names what is left.
Raising it holds a worker for that long. A rate limit measured in more
than minutes is not a retry at all — that is
StepCtx::sleep, which costs a row.
Implementations§
Source§impl RetryPolicy
impl RetryPolicy
Sourcepub const fn never() -> Self
pub const fn never() -> Self
Never repeat. The first failure is final.
The right choice for an effect that is expensive, externally rate limited, or whose driver already retries internally — a policy stacked on a driver that retries is a multiplication, not an addition.
Sourcepub fn with_backoff(self, initial: Duration, max: Duration) -> Self
pub fn with_backoff(self, initial: Duration, max: Duration) -> Self
Replace the backoff schedule, keeping the attempt count.
Sourcepub fn without_jitter(self) -> Self
pub fn without_jitter(self) -> Self
Turn jitter off, making the schedule identical across runs.
Sourcepub const fn wait_at_most(self, advice: Duration) -> Self
pub const fn wait_at_most(self, advice: Duration) -> Self
Change how long a peer’s own Retry-After may hold a worker.
See max_advice for why this is not max_backoff.
Sourcepub fn wait_before(
&self,
run: RunId,
key: EffectKey,
attempt: u32,
advice: Option<Duration>,
) -> Duration
pub fn wait_before( &self, run: RunId, key: EffectKey, attempt: u32, advice: Option<Duration>, ) -> Duration
How long to wait before attempt, given whatever the peer said.
advice wins when present, clamped to max_advice,
including when it is shorter than the computed backoff: the peer is
describing its own recovery, and waiting longer than it asked buys
nothing.
Sourcepub fn permits(&self, attempt: u32) -> bool
pub fn permits(&self, attempt: u32) -> bool
Whether another attempt is left after attempt has failed.
attempt is 1-based, matching what the journal records.
Sourcepub fn backoff(&self, run: RunId, key: EffectKey, attempt: u32) -> Duration
pub fn backoff(&self, run: RunId, key: EffectKey, attempt: u32) -> Duration
How long to wait before attempt (1-based; attempt 1 never waits).
Exponential with an integer multiplier and a ceiling, then optionally
spread by a hash-derived factor in [0.5, 1.0] of the computed delay.
Halving rather than scaling from zero keeps a floor under the schedule:
full jitter can pick a near-zero delay and hammer a service that is
already struggling.
Trait Implementations§
Source§impl Clone for RetryPolicy
impl Clone for RetryPolicy
impl Copy for RetryPolicy
Source§impl Debug for RetryPolicy
impl Debug for RetryPolicy
Source§impl Default for RetryPolicy
impl Default for RetryPolicy
Source§impl<'de> Deserialize<'de> for RetryPolicy
impl<'de> Deserialize<'de> for RetryPolicy
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Eq for RetryPolicy
Source§impl PartialEq for RetryPolicy
impl PartialEq for RetryPolicy
Source§impl Serialize for RetryPolicy
impl Serialize for RetryPolicy
impl StructuralPartialEq for RetryPolicy
Auto Trait Implementations§
impl Freeze for RetryPolicy
impl RefUnwindSafe for RetryPolicy
impl Send for RetryPolicy
impl Sync for RetryPolicy
impl Unpin for RetryPolicy
impl UnsafeUnpin for RetryPolicy
impl UnwindSafe for RetryPolicy
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more