pub struct LimitBand {
pub min: usize,
pub max: usize,
}Expand description
The range the governed limit is clamped to.
Spelled as a type rather than two constants because it has to be narrowed
by an operator’s configured ceiling: a server told --max-concurrent-transfers 4
must serve 4, not the design’s floor of 10. See LimitBand::for_ceiling.
Fields§
§min: usizeThe smallest the controller may shrink to.
max: usizeThe largest it may grow to, and where it starts.
Implementations§
Source§impl LimitBand
impl LimitBand
Sourcepub fn for_ceiling(ceiling: usize) -> LimitBand
pub fn for_ceiling(ceiling: usize) -> LimitBand
The band for a server whose operator pinned a hard ceiling.
The operator’s number is the ceiling, and the FLOOR is clamped under it:
pinning 4 gives [4, 4], a governor that cannot move, which is exactly
what “I told you 4” should mean. A configured ceiling below
MIN_LIMIT must not be silently RAISED — a gate admitting more than
it was told to is the one failure mode a limit may not have.
The ceiling is NOT capped at MAX_LIMIT, and that was a bug. The
first form did MAX_LIMIT.min(ceiling), so a server configured with the
shipped default of 256 got a band of [10, 100] and quietly served 100.
Lowering an operator’s explicit number is the same defect as raising it,
in the other direction: both mean the configured value is not the value.
MAX_LIMIT is the design’s default for a server that has NOT been told a
number — it is not an opinion that outranks one that has.
Sourcepub fn for_budget(budget: usize) -> LimitBand
pub fn for_budget(budget: usize) -> LimitBand
The band for a server whose ceiling is a declared, measured budget rather than a round number an operator typed.
§Why the floor moves with the ceiling — MEASURED
LimitBand::for_ceiling pins the floor at MIN_LIMIT = 10. With the
shipped ceiling of 256 that is a band of [10, 256], and the controller
walks the whole of it: multiplicative decrease is limit -= limit/8, so
256 → 224 → 196 → … → 24 → 21 → 19 → 17 … On the appliance under a
64-client push storm every 100 ms window stands, and the measurement
took the gate refusing by name at exactly that — already serving its limit of 19, 268 of 1000 push-creates admitted. 19 was not a constant
anybody chose; it is a point on a descent.
The descent does not stop because shedding does not fix what the controller is measuring. The sojourn it reads was the gap between admission and the spawned task’s first line — the tokio ready queue and the blocking-pool handoff — and that gap is set by how many clients are connected and doing crypto, not by how many transfers are admitted. A refused client reconnects, so a refusal makes the input worse. A controller with no authority over its own input runs to its floor.
Both halves are fixed here. The controller’s input is now the
admission wait — the queue this gate actually owns
(DEFAULT_ADMISSION_WAIT) — and its floor is a share of the budget
rather than a constant: below BUDGET_FLOOR_DENOMINATOR⁻¹ of a
number derived from cores, descriptors and MemAvailable, the box is
provably not the bottleneck and refusing costs more than serving.