pub struct WarmingAllowance { /* private fields */ }Expand description
Bounds speculative warming losses per upstream source node.
See the module docs for the serve-vindicated accounting model.
§Poisoning
Every acquisition of state recovers the inner value rather than
propagating the poison. The whole of each critical section is a HashMap
entry insert, an Instant read (Instant::elapsed saturates rather than
panicking), and saturating i64 arithmetic, so there is no reachable unwind
and recovered state is never torn.
Recovering is also the safe direction on each of the three paths that take
the lock, which is why it is uniform here rather than split per method.
Skipping a Self::debit_speculative would let a source buy speculatively
without paying the grief cap for it, and skipping the aggregator’s apply
would strand a serve’s realized margin. Refusing on Self::available is
the conservative direction for that one call, but it blocks every
speculative buy node-wide for the process lifetime with no signal, which is
a worse operational failure than the unreachable one it guards against.
Implementations§
Source§impl WarmingAllowance
impl WarmingAllowance
Sourcepub fn new(budget: u64, refill_per_sec: u64) -> Self
pub fn new(budget: u64, refill_per_sec: u64) -> Self
Creates a new allowance tracker. A source with no recorded activity reads as fully available.
Sourcepub fn available(&self, source: SourceId) -> bool
pub fn available(&self, source: SourceId) -> bool
Reports whether source currently has any warming allowance left. A
source with no recorded activity holds the full budget and is
available; an existing ledger is available only while its allowance is
still positive.
A zero budget disables warming (Self::enabled): every source reads as
unavailable, including never-seen ones, so economic_ceiling never
selects the warming regime and no speculative buy is authorized. Without
this short-circuit a fresh source would read available (None => true)
even with warming off, and buy at the market price for its first hash —
the “zero budget disables warming” semantics would hold only after each
source’s first buy created its bucket.
Sourcepub const fn enabled(&self) -> bool
pub const fn enabled(&self) -> bool
Whether speculative warming is configured at all. A zero budget disables it: every above-floor buy is refused and no source ever has a positive allowance to spend. Callers gate the “source blocked” telemetry on this so a node with warming deliberately off does not report every source as griefed down.
Sourcepub fn blocked_source_count(&self) -> usize
pub fn blocked_source_count(&self) -> usize
How many sources currently hold no warming allowance — a spent ledger at
or below zero once its time refill is projected forward. Read-only: it
projects each bucket’s refill without persisting it (unlike
Self::available, which refills in place), so a metrics scrape never
mutates the ledger. Sampled onto decdn_warming_sources_blocked.
Reads zero when warming is disabled (Self::enabled), even if buckets
linger from a config that previously ran warming on: with the budget at
zero no source is being griefed down, so the gauge stays at zero across
the reload rather than reporting every spent ledger as blocked.
Sourcepub fn debit_speculative(&self, source: SourceId, hash: Hash, units: u64)
pub fn debit_speculative(&self, source: SourceId, hash: Hash, units: u64)
Debits source’s ledger for a speculative buy of hash and tags the
hash with its source so a later serve can credit the right ledger.
The buy loop checks Self::available when it selects a candidate and
debits only after the pull completes, so concurrent pulls from one source
can each pass the check and drive the ledger below zero: the real spend
past the allowance is every speculative buy still in flight when it
crossed zero. The ledger is floored at -budget, so however far those
buys overshoot, the source needs at most budget + 1 units of credit or
refill to warm again. The floor bounds that recovery debt, not the spend.
The tag is published while the bucket lock is held, before the debit.
The serve path reads the tag map without that lock, but every credit
reaches the ledger through credit_source, which takes it. So a
serve that sees the tag has its credit applied only after this debit, and
a serve that misses the tag also precedes the debit. Publishing the tag
outside the lock would let a credit land on a bucket this debit is about
to create full, where the +budget cap discards it; publishing it after
the debit would let a serve see a committed debit with no tag and drop
its credit.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for WarmingAllowance
impl !RefUnwindSafe for WarmingAllowance
impl Send for WarmingAllowance
impl Sync for WarmingAllowance
impl Unpin for WarmingAllowance
impl UnsafeUnpin for WarmingAllowance
impl UnwindSafe for WarmingAllowance
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> FutureExt for T
impl<T> FutureExt for T
Source§fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
Source§fn with_current_context(self) -> WithContext<Self> ⓘ
fn with_current_context(self) -> WithContext<Self> ⓘ
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 moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request