pub struct BackoffPolicy {
pub first_attempt_grace: Duration,
pub base: Duration,
pub ceiling: Duration,
pub max_attempts: u32,
}Expand description
When a pending entry becomes eligible for another relay attempt.
Why: ADR-0034 §2 says “Console retries with backoff” and the first cut had
none — every pending entry was re-relayed every 60 s, each non-ack rewriting
the full base64 body plus two fsyncs. Until step 4 binds a listener that is
every delivery, forever.
What: a grace period for a never-attempted entry, exponential spacing keyed
on attempts, a ceiling, and a hard stop. Pure — BackoffPolicy::is_due
takes now so the rules are testable without sleeping.
Test: backoff_holds_off_a_freshly_spooled_entry,
backoff_spacing_grows_with_attempts, backoff_respects_the_ceiling,
backoff_stops_at_max_attempts.
Fields§
§first_attempt_grace: DurationHow long a never-attempted entry is left alone after being spooled.
Set to the relay timeout: within that window the request path that spooled it may still be relaying it, and its claim has not necessarily been taken yet at the instant the sweep lists the directory.
base: DurationSpacing after the first failure; doubles per subsequent attempt.
ceiling: DurationUpper bound on the spacing, however many attempts have failed.
max_attempts: u32After this many failed attempts the entry is never relayed again.
It is NOT deleted — it stays on disk and keeps the health signal red,
because an undeliverable webhook is an operator problem, not garbage.
The cap exists so a permanently unrelayable entry stops costing a
full-body rewrite and two fsyncs on every tick.
Implementations§
Source§impl BackoffPolicy
impl BackoffPolicy
Sourcepub fn delay_after(&self, attempts: u32) -> Duration
pub fn delay_after(&self, attempts: u32) -> Duration
Spacing required after attempts failures.
base << (attempts - 1), saturating into BackoffPolicy::ceiling.
The shift is bounded before it is applied, so a large attempt count
cannot overflow into a small delay.
Test: backoff_spacing_grows_with_attempts, backoff_respects_the_ceiling.
Sourcepub fn is_due(&self, entry: &SpoolEntry, now_unix_ms: u64) -> bool
pub fn is_due(&self, entry: &SpoolEntry, now_unix_ms: u64) -> bool
Whether entry may be relayed again at now_unix_ms.
Why: the sweep’s only admission test. Returning false leaves the entry
exactly where it is — pending, durable, and visible to the health scan —
so a “not due” entry is never a dropped one.
What: false past BackoffPolicy::max_attempts; otherwise the elapsed
time since the last attempt (or since receipt, for a never-attempted
entry) must meet BackoffPolicy::delay_after.
Test: backoff_holds_off_a_freshly_spooled_entry,
backoff_stops_at_max_attempts, backoff_admits_an_entry_past_its_delay.
Sourcepub fn is_exhausted(&self, entry: &SpoolEntry) -> bool
pub fn is_exhausted(&self, entry: &SpoolEntry) -> bool
Whether entry has exhausted its retries and needs an operator.
Distinguished from “not due yet” so the sweep can report the two separately — one resolves itself, the other never will.
Trait Implementations§
Source§impl Clone for BackoffPolicy
impl Clone for BackoffPolicy
Source§fn clone(&self) -> BackoffPolicy
fn clone(&self) -> BackoffPolicy
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreimpl Copy for BackoffPolicy
Source§impl Debug for BackoffPolicy
impl Debug for BackoffPolicy
Source§impl Default for BackoffPolicy
impl Default for BackoffPolicy
impl Eq for BackoffPolicy
Source§impl PartialEq for BackoffPolicy
impl PartialEq for BackoffPolicy
impl StructuralPartialEq for BackoffPolicy
Auto Trait Implementations§
impl Freeze for BackoffPolicy
impl RefUnwindSafe for BackoffPolicy
impl Send for BackoffPolicy
impl Sync for BackoffPolicy
impl Unpin for BackoffPolicy
impl UnsafeUnpin for BackoffPolicy
impl UnwindSafe for BackoffPolicy
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
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
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.impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
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