Skip to main content

RefreshCoalescer

Struct RefreshCoalescer 

Source
pub struct RefreshCoalescer<T> { /* private fields */ }
Expand description

Runs one refresh at a time; a refresh asked for while another is in flight joins it instead of issuing a second.

04-subsystem-contracts.md: “Manual refresh coalesces with an in-flight request.” The requirement is a budget one before it is a latency one — F5 held down on the dashboard would otherwise be an operator-driven denial of service against a 5,000/hour ceiling shared with the polling that keeps runners starting.

The mechanism is the generation-and-gate pattern crate::AuthenticatedClient::revalidate already uses for single-flight re-validation, and it is here rather than there because the two coalesce different things. A caller samples the generation before queuing on the gate; if it moved while the caller waited, some other refresh covered it and this one returns that result without calling work at all. work being FnOnce is what makes “no second request” structural rather than remembered: the joining path never has a future to poll.

§One instance per target. This is a requirement, not a convention

last is a single slot and generation is a single counter, so an instance can only ever be a cache of one thing. Sharing one coalescer across two targets does not merely lose cache hits — it hands target A’s caller target B’s snapshot, silently and with no error, because joining a generation that moved is precisely how this type reports “somebody else already refreshed what you asked for”. Nothing here can detect that the somebody else was refreshing something different.

e1 and g2 therefore hold one instance per ScaleTarget — keyed by target in whatever map they already keep — and never one per host. The type cannot enforce it, which is exactly why it is written down.

Implementations§

Source§

impl<T: Clone> RefreshCoalescer<T>

Source

pub fn new() -> Self

Source

pub async fn refresh<F, Fut>(&self, work: F) -> T
where F: FnOnce() -> Fut, Fut: Future<Output = T>,

Refresh, or join the refresh already running.

§Panics

If a previous holder panicked while the result lock was held.

Source

pub fn performed(&self) -> u64

How many refreshes actually ran.

Source

pub fn joined(&self) -> u64

How many refreshes were served by joining one already in flight.

Source

pub fn last(&self) -> Option<T>

The most recent outcome, if there has been one.

§Panics

If a previous holder panicked while the result lock was held.

Trait Implementations§

Source§

impl<T: Debug> Debug for RefreshCoalescer<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T: Clone> Default for RefreshCoalescer<T>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<T> !Freeze for RefreshCoalescer<T>

§

impl<T> !RefUnwindSafe for RefreshCoalescer<T>

§

impl<T> Send for RefreshCoalescer<T>
where Mutex<Option<T>>: Send,

§

impl<T> Sync for RefreshCoalescer<T>
where Mutex<Option<T>>: Sync,

§

impl<T> Unpin for RefreshCoalescer<T>
where Mutex<Option<T>>: Unpin,

§

impl<T> UnsafeUnpin for RefreshCoalescer<T>

§

impl<T> UnwindSafe for RefreshCoalescer<T>
where Mutex<Option<T>>: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more