#[non_exhaustive]pub struct EgressConfig {
pub max_pending_bytes: usize,
pub max_hold: Duration,
pub drain_timeout: Duration,
}Expand description
Engine-side knobs for action execution. Carried on the session config.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.max_pending_bytes: usizeBytes a per-stream pending queue may hold before the read side is
stalled. Past this point Delay and Hold are backpressure rather
than latency, and the transition is reported once per stream.
Default 1 MiB.
max_hold: DurationCeiling on any single Action::Hold and on any single
Action::Delay. A clamped delay is reported. Default 30 s.
The ceiling is an Instant deadline, and Instant does not
behave the same way across a machine suspend on every platform: a
30 s hold armed before a laptop sleeps may fire immediately on
resume (Windows QueryPerformanceCounter) or 30 s after resume
(Linux CLOCK_MONOTONIC). Irrelevant on CI; surprising when
debugging a run on a laptop.
drain_timeout: DurationHow long a requested close gives this session’s egress queues to flush before both legs are closed anyway. Default 100 ms.
Only ProxyControl::close_session
reads this. Every other way a session ends — a peer going away, a
hook’s Action::CloseSession, the proxy being cancelled — tears
down at once and has always done so. A requested close is different
because somebody is waiting for it to mean something: closing the
instant the request lands discards whatever a Delay or a Hold
was still holding, and the caller cannot tell that from a session
that had nothing queued.
The bound is the point. An unbounded drain turns a close into a call
that may never finish: a stream whose destination peer has stopped
reading never empties its queue, and a shaped class whose bucket is
dry empties it only at the configured rate. Whatever is still queued
when this elapses is discarded and reported as
ImpairmentKind::QueuedBytesAtTeardown,
so bytes that did not make it are named rather than lost quietly,
and the close still carries the code that was asked for.
§The default is a guess, and here is the measurement that replaces it
100 ms was chosen because it is long enough for a loopback flush and short enough that a caller closing sessions in a loop does not notice, not because anything was measured. To replace it: pin one queue state — a fixed object size, a fixed unit count, a fixed destination window, written down beside the figure — sweep this timeout across that state, and take the knee at which the stranded byte count reaches zero. Quote the queue state with the number; a knee measured against 4 KiB objects says nothing about 256 KiB ones.
Measure stranded bytes, never elapsed time. Timing the drain measures the fixture that filled the queue — how fast the source wrote, how the destination’s flow-control window happened to open — and a timeout tuned against it is tuned against the harness. The byte count is the thing that is either zero or not.
Trait Implementations§
Source§impl Clone for EgressConfig
impl Clone for EgressConfig
Source§fn clone(&self) -> EgressConfig
fn clone(&self) -> EgressConfig
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more