pub struct Governor { /* private fields */ }Expand description
Both gates, and the occupancy they meter.
Implementations§
Source§impl Governor
impl Governor
Sourcepub fn new(band: LimitBand, budget: u64) -> Governor
pub fn new(band: LimitBand, budget: u64) -> Governor
A governor over band with budget bytes of memory to give out.
Sourcepub fn memory(&self) -> &MemoryLedger
pub fn memory(&self) -> &MemoryLedger
The memory ledger.
Sourcepub fn occupied_in(&self, lane: Lane) -> usize
pub fn occupied_in(&self, lane: Lane) -> usize
Operations in flight in one lane.
Sourcepub fn lane_ceiling(&self, lane: Lane, limit: usize) -> usize
pub fn lane_ceiling(&self, lane: Lane, limit: usize) -> usize
How many slots lane may hold right now: the governed limit, less
the part of every other lane’s floor that somebody is actually
waiting for.
§A floor that reserves an idle lane’s slots is the old bug again
The per-principal cap used to bind unconditionally, and measured on the
32-core desktop that meant 32 served and 32 refused against
224 idle slots
(crate::admission). A lane floor that reserved an eighth of the box for
ls-remote whether or not any ls-remote existed would be the same
mistake with a different noun: it would refuse a push the box could
serve, to keep a slot warm for a request nobody had made.
So the floor is claimed on demand, and demand is now observable:
since 2026-08-30 an operation over the limit parks
(crate::Admission::admit_within) instead of being refused, so the
number of parked operations per lane is a live, exact count of who is
asking. A lane with nobody waiting reserves nothing; the instant one
ls-remote parks, one slot of the cheap floor is held back and the next
release goes to it rather than to the push storm that would otherwise
take every slot for as long as it lasted.
min(floor, waiting) is what makes it a floor and not a queue: a lane
can never reserve more than Lane::floor however many of its
operations are waiting, so cheap cannot starve write either.
Sourcepub fn waiting_in(&self, lane: Lane) -> usize
pub fn waiting_in(&self, lane: Lane) -> usize
Operations of lane parked waiting for a slot right now.
Sourcepub fn entered_wait(&self, lane: Lane)
pub fn entered_wait(&self, lane: Lane)
Say that one operation of lane has started waiting.
Paired with Governor::left_wait by
crate::Admission::admit_within, which is the only caller and which
pairs them on every path including cancellation.
Sourcepub fn shed_counts(&self) -> (u64, u64, u64)
pub fn shed_counts(&self) -> (u64, u64, u64)
Refusals by gate: (concurrency, lane, memory).
Sourcepub fn overdrawn(&self) -> u64
pub fn overdrawn(&self) -> u64
How many transfers have charged the ledger past its budget while
running (Growth::Overdrawn).
Sourcepub fn try_claim(&self, class: OpClass) -> Result<Claim, Shed>
pub fn try_claim(&self, class: OpClass) -> Result<Claim, Shed>
The admission decision. Time first, then the lane, then space, then take all three.
Nothing is allocated on any refusal path, and nothing the operation
needs is allocated on the success path either: a Claim is three
Copy words. That is the point of the gate — the refusal has to arrive
before the cost, or it is only a slower way of paying it.
Sourcepub fn claim_for(&self, class: OpClass, count: bool) -> Result<Claim, Shed>
pub fn claim_for(&self, class: OpClass, count: bool) -> Result<Claim, Shed>
Governor::try_claim or Governor::attempt, chosen by the caller.
Sourcepub fn claim_reserving(
&self,
class: OpClass,
reserve: usize,
count: bool,
) -> Result<Claim, Shed>
pub fn claim_reserving( &self, class: OpClass, reserve: usize, count: bool, ) -> Result<Claim, Shed>
Governor::claim_for against a limit reserve slots lower than
the governed one, so that this claim can never take one of the last
reserve slots.
§Why the reserve is enforced here and not by the caller
A caller can read Governor::occupied, compare it to a threshold and
only then ask for a claim — and that is a race, for exactly the reason
written over the compare-and-swap below: N racing admissions all read
the same occupancy and all decide they fit. Measured, on a four-slot gate
with two hundred anonymous readers: strangers holding transient claims
while they made that decision filled the server-wide gate, and a keyed
caller arriving into that window was refused by gate 1 with
AtCapacity — the reserve that existed to protect it having never been
consulted. A limit that is checked outside the atomic that enforces it is
not a limit.
So the reserve rides in the same fetch_update. N racing strangers can
then no more collectively exceed limit - reserve than they can exceed
limit, and a refused one never holds a slot at all — which matters
twice, because the refusal path of a flood is the path an attacker is
trying to make expensive.
§Why this moves no shed counter
The three Governor::shed_counts counters count this governor’s own
gates — the limit it controls, the lane floors it computes, the ledger it
keeps. The reserve is none of those: it is a number the caller handed in,
for a policy the caller owns, and the caller counts it
(AdmissionStats::refused_keyed_reserve). Counting it here as a
concurrency shed would tell an operator the CoDel limit was full when it
was nowhere near, and counting it here under a fourth name would give the
same event two owners.
Sourcepub fn attempt(&self, class: OpClass) -> Result<Claim, Shed>
pub fn attempt(&self, class: OpClass) -> Result<Claim, Shed>
Governor::try_claim without moving the shed counters.
The retry inside crate::Admission::admit_within asks this door many
times for one operation. Counting every ask would make
shed_concurrency a count of polls rather than of refusals — the
counter would read 21 where one client was refused once, which is worse
than no counter. So the wait loop attempts without counting and counts
once, at the end, when the refusal is final.
Sourcepub fn charge(&self, held_after: u64, extra: u64) -> Growth
pub fn charge(&self, held_after: u64, extra: u64) -> Growth
Charge a running transfer for what has actually crossed the wire.
Called by crate::AdmissionGuard::reserve_more and by nothing else,
so the guard stays the single owner of “what this operation holds” and
the release cannot drift. See Growth for why it cannot refuse and
WIRE_MEMORY_NUMERATOR for what fraction of the wire is charged.
Sourcepub fn release(&self, claim: &Claim, grown: u64)
pub fn release(&self, claim: &Claim, grown: u64)
Give a claim’s slot and reservation back. Called from exactly one place:
AdmissionGuard’s Drop, which passes what it
grew the reservation to on top of Claim::bytes.
Sourcepub fn record_sojourn(&self, class: OpClass, sojourn: Duration)
pub fn record_sojourn(&self, class: OpClass, sojourn: Duration)
File one admission wait against its class’s window.
This is the controller’s input, and the change of input is the point:
see LimitBand::for_budget for what it used to be (the runtime’s
spawn-to-run gap) and why a controller that cannot move its own input
runs to its floor. A wait of zero — the ordinary case on an unloaded
server — is a sample too, and it is the one that grows the limit back.