Skip to main content

FillPlanner

Struct FillPlanner 

Source
pub(crate) struct FillPlanner {
Show 13 fields cfg: PixFillConfig, max_recovery_time: Duration, max_recovery_idle: Duration, cycle_shares: u64, last_tick: Instant, last_served_total: u64, organic_pps: f64, target: Option<SsaId<HoprPseudonym>>, target_seen: u64, target_progress_at: Option<Instant>, last_rate: FillRate, stall_warned: bool, stall_onset: bool,
}
Expand description

The pure rate law. See the module documentation for what it computes and why.

Fields§

§cfg: PixFillConfig§max_recovery_time: Duration

SupervisorConfig::max_recovery_time, the budget finish_fraction is a fraction of.

§max_recovery_idle: Duration

SupervisorConfig::max_recovery_idle, reused as the stall horizon so that fill gives up on a motionless cycle at the same point the idle rule would have, had it been able to see it.

§cycle_shares: u64

E — every share one cycle of the negotiated dimensions emits, and therefore every packet it takes to complete one.

§last_tick: Instant

When plan last ran. Also the base of next_tick.

§last_served_total: u64

The gate’s served counter at last_tick, so the next tick can difference it.

§organic_pps: f64

Exponential average of organic egress, in packets per second.

§target: Option<SsaId<HoprPseudonym>>

The cycle the counters below belong to, so a handoff resets them rather than reading the successor’s progress as the predecessor’s stall.

§target_seen: u64

largest_shares_seen as of the last tick that observed movement.

§target_progress_at: Option<Instant>

When the target last moved, or when it became the target. None while there is no target.

§last_rate: FillRate

The rate the stream is believed to be running at, and the value hysteresis compares against.

§stall_warned: bool

Whether the stall has already been reported, so a stalled cycle warns once rather than once a second for the whole of max_recovery_time.

§stall_onset: bool

Set on the tick a stall begins, and taken by the worker so it can count the event.

An edge rather than a level, because the metric it feeds counts stalls: a level would count one per second for as long as the cycle stayed motionless, which is a measure of how long the operator waited rather than of how often this happened.

Implementations§

Source§

impl FillPlanner

Source

pub(crate) fn new( cfg: &SupervisorConfig, dims: &PixParams, now: Instant, ) -> Self

Builds a planner for a Session of the given dimensions.

now seeds next_tick, so the first plan happens one sampling interval after the Session starts rather than immediately. Nothing is lost by that: a Session has no funded cycle to fill for until at least a commitment and a deposit have crossed the wire.

Source

pub(crate) fn is_enabled(&self) -> bool

Whether fill is configured on at all.

Source

pub(crate) fn take_stall_onset(&mut self) -> bool

Reports, once, that the target cycle has gone motionless and fill has fallen back.

Consumed rather than read so the caller counts the stall rather than its duration. The caller is the worker, which is the layer allowed to have side effects; this module stays pure.

Source

pub(crate) fn is_stalled(&self) -> bool

Whether the target has gone motionless and fill has fallen back to its heartbeat.

The level behind take_stall_onset’s edge, so the census can report a stall for as long as it lasts without the caller tracking it.

Source

pub(crate) fn is_idle(&self) -> bool

Whether the stream this planner drives is currently emitting nothing.

The supervisor uses it to decide whether a tick is worth waking for when there is no target: a planner that has already stopped has nothing left to say, while one that is still running owes the stream a zero.

Source

pub(crate) fn next_tick(&self) -> Instant

When the next plan is due.

Source

pub(crate) fn stop(&mut self) -> Option<FillRate>

Silences the stream, if it is running, and forgets the target.

Returns the action’s payload only when there is something to stop, so a Session that never filled — a disabled one, or one that closed before its first cycle funded — emits nothing and its action stream is byte-for-byte what it was before fill existed.

Source

pub(crate) fn plan( &mut self, now: Instant, served_total: u64, target: Option<FillTarget>, ) -> Option<FillRate>

Re-derives the fill rate, returning it only if it is worth emitting.

served_total is the service gate’s counter — organic egress and nothing else. target is the cycle the Exit is working for, or None when it has none, which is the case that stops the stream.

Source

fn observe_organic(&mut self, now: Instant, served_total: u64)

Folds this interval’s organic egress into the exponential average.

alpha = dt / ORGANIC_WINDOW, capped at one, so the average has the same time constant however irregularly the ticks land — a worker that was busy for five seconds contributes five seconds of weight, not one tick’s worth. A tick with no elapsed time contributes nothing rather than dividing by zero.

Source

fn observe_target(&mut self, now: Instant, target: &FillTarget)

Tracks whether the target has moved, which is the input to the stall rule.

A change of target resets the clock rather than inheriting the predecessor’s: a successor that has just reached the front has made no progress yet, and reading that as a stall would hold every cycle after the first at the heartbeat.

Source

fn required_rate(&self, now: Instant, target: &FillTarget) -> f64

The rate at which the cycle’s remaining emission clears the aim point, in packets per second.

The aim point sits (1 − finish_fraction) × max_recovery_time before the hard deadline, so the last part of the budget is margin: for loss beyond loss_margin, for a mixnet delay spike, and for the commitment and deposit round trip the successor still needs. An aim point already in the past collapses to one sampling interval, which asks for the whole remainder at once and is then bounded by max_rate — the correct behaviour for a cycle that is out of time, since there is no rate at which it can still be saved and no reason to stop trying.

A drain collapses the horizon the same way for a different reason: pacing a cycle to its deadline only makes sense while the Session is still there to be paced, and a drained one is spending a buffer nothing will refill. The ceiling in bound then becomes the only thing setting the rate, which is the intent rather than a side effect.

Source

fn bound(&mut self, now: Instant, wanted: f64) -> FillRate

Applies the stall rule and the two bounds, and expresses the result as a FillRate.

The heartbeat is a floor rather than a special case, so a Session whose application is keeping up still emits one packet per period: it refreshes the Entry’s idle eviction, it carries the SURB level the Entry’s balancer acts on, and it is what distinguishes a planner that decided on nothing from one that has stopped planning.

The stall rule still applies while draining.

Without it, a doomed drain could sit at max_rate until the deadline and waste SURBs. A healthy drain pays nothing for the rule, because progress refreshes target_progress_at. And max_recovery_idle does not replace it: that deadline ends the drain, this bounds what it spends before then.

Source

fn should_emit(&self, rate: FillRate) -> bool

Whether rate differs from what the stream is already doing by enough to be worth an action.

The planner runs every second for the whole life of a cycle — hours, at production dimensions — and the required rate drifts continuously as the remainder and the horizon both shrink. Emitting every one of those would put a per-second action on a channel sized for lifecycle transitions, and the driver would reconfigure a rate controller for a change no packet schedule can express. So a change is reported when it is a real one:

  • any change to or from zero, because that is the difference between filling and not;
  • any crossing of the heartbeat, because that is the difference between filling and idling;
  • otherwise, a relative change of more than ten per cent.

Auto Trait Implementations§

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
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<T> Conv for T

§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
§

impl<T> FmtForward for T

§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> FutureExt for T

§

fn with_context(self, otel_cx: Context) -> WithContext<Self>

Attaches the provided Context to this type, returning a WithContext wrapper. Read more
§

fn with_current_context(self) -> WithContext<Self>

Attaches the current Context to this type, returning a WithContext wrapper. Read more
§

impl<T> Instrument for T

§

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

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

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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
§

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

§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

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

§

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
§

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
§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<T> Tap for T

§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
§

impl<T> TryConv for T

§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

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

Source§

type Error = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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
§

fn with_current_subscriber(self) -> WithDispatch<Self>

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