pub struct BalancerStateValues {
pub target_surb_buffer_size: AtomicU64,
pub max_surbs_per_sec: AtomicU64,
pub decay_duration_msec: AtomicU64,
pub decay_volume_pct: AtomicU8,
pub buffer_level: AtomicU64,
pub sustain_on_return_path_loss: AtomicBool,
pub counterparty_buffer_capacity: AtomicU64,
pub return_path_degraded_until_ms: AtomicU64,
pub counterparty_in_surb_distress: AtomicBool,
}Expand description
Runtime state of the SurbBalancer.
Fields§
§target_surb_buffer_size: AtomicU64§max_surbs_per_sec: AtomicU64§decay_duration_msec: AtomicU64§decay_volume_pct: AtomicU8§buffer_level: AtomicU64§sustain_on_return_path_loss: AtomicBoolWhether this session opted into sustaining production through return-path loss.
counterparty_buffer_capacity: AtomicU64How many SURBs the counterparty can physically hold, or 0 when unknown.
The estimate is produced - consumed, and consumption is only observed once a reply
arrives – so a return path that drops replies lets the believed level grow without bound.
The counterparty’s store is a ring buffer that evicts the oldest entry on overflow, so
everything above its capacity was discarded on arrival and was never a real level. Measured
during an outage: 51 917 believed against a 15 000-entry store.
§Why evictions are not subtracted from the level
The counterparty reports its evictions (num_evicted_surbs on the incoming packet), so the
level could be corrected to the exact truth instead of merely bounded here. It deliberately
is not, because the clamp below is max(capacity, target) rather than capacity: a level
inflated past a full buffer still climbs to the target and shuts organic production off,
whereas an accurate level pins at the counterparty’s real capacity. If that capacity is below
the target – which nothing prevents, since this figure is the local store size and the
counterparty may be smaller – the accurate level never reaches the target, production never
stops, and the buffer evicts forever. The imprecise estimate fails safe and the precise one
does not, so the eviction count stays an observability signal.
return_path_degraded_until_ms: AtomicU64Milliseconds from the crate-internal EPOCH monotonic origin until which the return path
counts as degraded.
A deadline rather than a flag: it is set by a layer that observes the return path and read here, and nothing is guaranteed to come back and clear it. Expiring on its own bounds the damage of a marker that is never withdrawn to a short over-production instead of a session that mints forever.
counterparty_in_surb_distress: AtomicBoolWhether the counterparty’s last packet said it was running low on SURBs of its own.
A plain flag with no deadline, unlike return_path_degraded_until_ms above, because the two
fail in opposite directions: a degraded-path marker that is never withdrawn makes this side
mint forever, whereas a distress flag that is never withdrawn merely keeps organic production
at one SURB per packet — the behaviour that predates the gate. A flag whose stuck state is
the old behaviour does not need to expire.
It clears on its own in the normal case: the counterparty recomputes both SURB signals on every return packet and strips them once its pool recovers, so the next healthy packet resets this.
Last write wins, and the signal is recorded at dispatch – ahead of Session sequencing – so
a reordered clean packet can clear a distress signal the counterparty sent after it. That
costs the safety valve rather than the recovery: surb_decay subtracts from the level
estimate on a timer regardless of what any packet says, so once the estimate falls back under
target both this gate and the keep-alives reopen on their own.
How long that takes is a property of the configuration, not a guarantee of this type. It
scales with the decay rate and with how far above target the estimate sits, and the
max(capacity, target) clamp bounds the latter only while counterparty_buffer_capacity is
known – its 0 (“unknown”) arm leaves the estimate unclamped. With decay switched off the
estimate does not drain on its own at all, and only a fresh distress signal or observed
consumption reopens the gate.
Sequencing the flag would buy a faster reopen, at the cost of making a hot-path signal depend on the Session’s reassembly.
Implementations§
Source§impl BalancerStateValues
impl BalancerStateValues
Sourcepub fn new(cfg: SurbBalancerConfig) -> Self
pub fn new(cfg: SurbBalancerConfig) -> Self
Constructor from a SurbBalancerConfig.
Sourcepub fn update(&self, cfg: &SurbBalancerConfig)
pub fn update(&self, cfg: &SurbBalancerConfig)
Performs update of the BalancerStateValues from the SurbBalancerConfig and
enables it.
Sourcepub fn set_counterparty_buffer_capacity(&self, capacity: u64)
pub fn set_counterparty_buffer_capacity(&self, capacity: u64)
Declares how many SURBs the counterparty’s store can hold, bounding the level estimate.
Taken from the session manager’s maximum_surb_buffer_size, which is the same capacity
already used to clamp a counterparty’s requested target. Zero leaves the estimate unbounded.
Sourcefn clamp_to_counterparty_capacity(&self, level: u64) -> u64
fn clamp_to_counterparty_capacity(&self, level: u64) -> u64
Caps level at what the counterparty can actually hold.
Never below the configured target: a target above the counterparty’s capacity is unreachable by construction, and clamping to capacity there would hold the error permanently negative and pin production at maximum forever – a worse failure than the unbounded estimate this exists to prevent. In that configuration the capacity figure is simply not usable for this session.
Sourcepub fn observe_counterparty_signals(&self, signals: PacketSignals)
pub fn observe_counterparty_signals(&self, signals: PacketSignals)
Records what the counterparty’s latest packet said about its own SURB supply.
Takes the whole signal set rather than a bool so the containment rule lives here: OutOfSurbs
is a superset of SurbDistress on the wire, so contains catches both, whereas an equality
match against SurbDistress would silently ignore the more severe of the two.
Sourcepub fn organic_surbs_per_packet(&self) -> usize
pub fn organic_surbs_per_packet(&self) -> usize
Returns the number of organic SURBs to attach to an outgoing Session data packet.
Only the Entry uses this value. Return 0 when the estimated counterparty buffer
has reached its target. Return 1 when balancing is disabled, the estimate is
below target, or the counterparty signals SURB distress.
This method uses the raw buffer estimate. During return-path loss, a zero estimate correctly keeps organic SURB production enabled.
Sourcepub fn mark_return_path_degraded(&self, grace: Duration)
pub fn mark_return_path_degraded(&self, grace: Duration)
Marks the return path as degraded for the next grace period.
Called by whichever layer can actually tell a dead return path from a quiet peer – from here the two are indistinguishable, since neither delivers replies. Re-marking simply extends the window.
Sourcepub fn return_path_estimate_is_stale(&self) -> bool
pub fn return_path_estimate_is_stale(&self) -> bool
Whether buffer_level is currently an instruction rather than a
measurement.
While this holds, the controller deliberately writes 0 into the level to drive production
to its maximum. That zero says “produce flat out”, not “the counterparty holds nothing”, so
anything reading the level as a supply ceiling must consult this first or it will read the
instruction as an order to send nothing.
True only when both the opt-in (sustain_on_return_path_loss) and live evidence
(mark_return_path_degraded, within its window) are
present: without the opt-in this is not our behaviour to change, and without evidence there
is nothing to tell a dead return path from an idle one.
pub because it is not only the controller’s business — hence the emphasis above on what
the flag does not mean. It is not a general “the return path is degraded” signal.
fn should_sustain_through_return_path_loss(&self) -> bool
Sourcepub fn as_config(&self) -> SurbBalancerConfig
pub fn as_config(&self) -> SurbBalancerConfig
Extracts the SurbBalancerConfig from the BalancerStateValues.
Sourcepub fn is_disabled(&self) -> bool
pub fn is_disabled(&self) -> bool
Checks if SURB balancing is disabled (no target buffer size set).
Sourcepub fn surb_decay(&self) -> Option<(Duration, f64)>
pub fn surb_decay(&self) -> Option<(Duration, f64)>
Extracts the SURB decay configuration from the BalancerStateValues.
Sourcepub fn buffer_level(&self) -> u64
pub fn buffer_level(&self) -> u64
Gets the current estimated SURB buffer level.
Sourcepub fn controller_bounds(&self) -> BalancerControllerBounds
pub fn controller_bounds(&self) -> BalancerControllerBounds
Returns the current BalancerControllerBounds from the BalancerStateValues.
Trait Implementations§
Source§impl Debug for BalancerStateValues
impl Debug for BalancerStateValues
Source§impl Default for BalancerStateValues
impl Default for BalancerStateValues
Source§fn default() -> BalancerStateValues
fn default() -> BalancerStateValues
Source§impl From<SurbBalancerConfig> for BalancerStateValues
impl From<SurbBalancerConfig> for BalancerStateValues
Source§fn from(cfg: SurbBalancerConfig) -> Self
fn from(cfg: SurbBalancerConfig) -> Self
Auto Trait Implementations§
impl !Freeze for BalancerStateValues
impl RefUnwindSafe for BalancerStateValues
impl Send for BalancerStateValues
impl Sync for BalancerStateValues
impl Unpin for BalancerStateValues
impl UnsafeUnpin for BalancerStateValues
impl UnwindSafe for BalancerStateValues
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
§impl<T> Conv for T
impl<T> Conv for T
§impl<T> FmtForward for T
impl<T> FmtForward for T
§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
§impl<T> FutureExt for T
impl<T> FutureExt for T
§fn with_context(self, otel_cx: Context) -> WithContext<Self>
fn with_context(self, otel_cx: Context) -> WithContext<Self>
§fn with_current_context(self) -> WithContext<Self>
fn with_current_context(self) -> WithContext<Self>
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§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§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
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) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
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
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
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
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
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
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.§impl<T> Pointable for T
impl<T> Pointable for T
§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
§impl<T> Tap for T
impl<T> Tap for T
§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read more§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read more§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read more§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read more§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read more§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read more§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.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
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.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
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.