pub struct TokenBucket { /* private fields */ }Implementations§
Source§impl TokenBucket
impl TokenBucket
Sourcepub fn try_consume(&self, requested: u64) -> u64
pub fn try_consume(&self, requested: u64) -> u64
Tries to consume up to requested whole tokens.
This method returns the whole tokens actually granted. A part token in
the bucket is not granted; it stays for a later consume. A bucket in
debt from Self::record grants nothing. Rate-0 grants the full
request.
The refill, the grant, and the new balance are one critical section on
the bucket’s group, so a concurrent
Self::set_token_rate_with_burst runs wholly before or wholly after
this call, and concurrent consumes never lose each other’s refill. See
the module documentation of the runtime for the design.
§Panics
Panics if the injected clock reads more than u64::MAX nanoseconds
since its origin, about 584 years.
Sourcepub fn quota_exceeded(&self) -> bool
pub fn quota_exceeded(&self) -> bool
Whether the bucket has nothing left to grant: its balance, refilled up to now, is spent or in debt. A rate-0 bucket has no limit and is never exceeded.
This is Kafka’s ReplicationQuotaManager.isQuotaExceeded, which a
replication throttle asks before it reads rather than after. While it
answers false the read goes out whole, and the caller then charges
its size with Self::record_bounded, past the balance if it must.
The overshoot is debt, and the debt makes the next ask answer true
until the refill has repaid it. So the long-term rate holds, and a
batch larger than the whole burst still moves.
§Panics
Panics if the injected clock reads more than u64::MAX nanoseconds
since its origin, about 584 years.
Sourcepub fn record(&self, tokens: u64) -> u64
pub fn record(&self, tokens: u64) -> u64
Charges tokens whole tokens whatever the bucket holds, and returns
the debt the charge leaves, in micro-tokens, MICROS_PER_TOKEN to a
token. The debt is 0 while the charge fits the balance.
This is Kafka’s Sensor.record(value, timeMs, true) on a quota’s
Rate: the value is recorded first and the quota is checked after, so
an over-quota request is charged in full. The part the balance could
not cover is debt, and the refill repays it before it adds to the
balance, so a client that keeps sending above its quota is throttled by
everything it has sent above the bound and not only by its latest
request. A caller turns the debt into the throttle time debt / rate,
which is Kafka’s QuotaUtils.throttleTime.
A rate-0 bucket has no limit, records nothing and reports no debt.
The debt is not capped, which is right for a byte-rate client quota:
its throttle is exactly debt / rate. A quota whose throttle is
bounded, or whose debt only ever starves a consumer of the bucket,
uses Self::record_bounded.
§Panics
Panics if the injected clock reads more than u64::MAX nanoseconds
since its origin, about 584 years.
Sourcepub fn record_bounded(&self, tokens: u64, max_wait: Time) -> u64
pub fn record_bounded(&self, tokens: u64, max_wait: Time) -> u64
Charges tokens as Self::record does, and keeps at most max_wait
of debt: the debt the refill repays in max_wait at the current rate.
Kafka’s quota sample window forgets what a client recorded about
quota.window.num * quota.window.size.seconds ago (SampledStat purges
an expired sample), so the debt of an overload does not outlast the
window. A debt that grows without a bound outlasts it: a client whose
throttle is capped at max_wait would be throttled at the cap on every
request until the whole uncapped debt was repaid, and a bucket that
grants nothing while in debt would starve its other consumers for as
long. The cap bounds the throttle derived from the debt by the wait
converted to clock nanoseconds. It rounds credit down to complete
micro-tokens, so fractional credit cannot extend debt beyond that wait.
§Panics
Panics if the injected clock reads more than u64::MAX nanoseconds
since its origin, about 584 years.
Sourcepub fn refund(&self, tokens: u64)
pub fn refund(&self, tokens: u64)
Gives back tokens that an earlier charge took and the caller did not
use, capped at the burst.
This is Kafka’s ClientQuotaManager.unrecordQuotaSensor: a fetch that
is throttled sends no records, so the bytes it was charged come off
the quota again. A rate-0 bucket holds no balance to give back to.
Sourcepub fn refund_micros(&self, micros: u64)
pub fn refund_micros(&self, micros: u64)
Gives back micros micro-tokens that an earlier charge took, capped at
the burst. Debt is repaid first. Refunding the effective micro-token
charge restores the refilled balance if no debt was discarded by
saturation or a cap and no other operation changed the bucket in between.
Source§impl TokenBucket
impl TokenBucket
Sourcepub fn set_token_rate(&self, tokens_per_sec: u64)
pub fn set_token_rate(&self, tokens_per_sec: u64)
Updates the rate in raw tokens per second, with a one-second burst at
the new rate. The balance is kept: see
Self::set_token_rate_with_burst.
This is the primitive. The bucket counts tokens and does not know what a
token means. Callers that meter a dimensioned quantity should use the
typed pair that names the dimension: Self::set_byte_rate or
Self::set_event_rate.
Sourcepub fn set_token_rate_with_burst(&self, new_rate: u64, burst: u64)
pub fn set_token_rate_with_burst(&self, new_rate: u64, burst: u64)
Updates the rate and the independent burst capacity, both in raw tokens.
A bucket that already has a rate keeps its balance across the change,
as Kafka’s ClientQuotaManager.updateQuotaMetricConfigs only swaps a
metric’s bound and leaves what it recorded: the time up to now is
refilled at the old rate, the balance is capped at the new burst, and
debt stays debt, so a client in debt is still throttled after a change
of its quota. A bucket that had no rate has recorded nothing, so it
starts at burst.
Sourcepub fn token_rate(&self) -> u64
pub fn token_rate(&self) -> u64
The configured rate in whole raw tokens per second, rounded down. 0
means no limit, or a rate under one token per second:
Self::byte_rate reads a fractional rate back exactly.
Sourcepub fn token_burst(&self) -> u64
pub fn token_burst(&self) -> u64
The configured burst capacity in whole raw tokens, rounded down. This is the most the bucket holds.
Sourcepub fn set_byte_rate(&self, new_rate: ByteRate)
pub fn set_byte_rate(&self, new_rate: ByteRate)
Updates a byte throughput and bursts one second’s worth, keeping the
balance as Self::set_token_rate_with_burst says.
The burst is rate * DEFAULT_BURST_WINDOW. uom type-checks this as a
[ByteRate] times a [Time], which gives a [ByteSize].
Sourcepub fn set_byte_rate_with_burst(&self, new_rate: ByteRate, burst: ByteSize)
pub fn set_byte_rate_with_burst(&self, new_rate: ByteRate, burst: ByteSize)
Updates a byte throughput and an independent byte burst capacity.
Both keep their fractional part to a millionth of a byte, so a rate of
half a byte per second is enforced as half a byte per second. Kafka
holds a byte-rate quota as a double. A positive rate is never stored
as the unlimited rate 0. The balance is kept, as
Self::set_token_rate_with_burst says.
Sourcepub fn byte_rate(&self) -> ByteRate
pub fn byte_rate(&self) -> ByteRate
The configured byte throughput, fractional part included.
[krabka_units::prelude::ByteRateExt::ZERO] means no limit.
Sourcepub fn runs_at_byte_rate(&self, rate: ByteRate) -> bool
pub fn runs_at_byte_rate(&self, rate: ByteRate) -> bool
Whether the bucket already runs at rate, as
Self::set_byte_rate_with_burst would store it.
A caller that re-applies a configured rate compares with this rather
than with Self::byte_rate: a rate finer than a micro-token per
second reads back rounded, so a comparison that never matched would
reset the bucket on every re-apply.
Sourcepub fn byte_burst(&self) -> ByteSize
pub fn byte_burst(&self) -> ByteSize
The configured byte burst capacity, fractional part included.
Sourcepub fn set_event_rate(&self, new_rate: Frequency)
pub fn set_event_rate(&self, new_rate: Frequency)
Updates an event throughput, such as samples, records, or requests, and bursts one second’s worth.
A token here is one event, not one byte. If you meter events with the byte pair above, the code compiles but the result is wrong. This is why the two pairs are separate.
Sourcepub fn set_event_rate_with_burst(&self, new_rate: Frequency, burst: u64)
pub fn set_event_rate_with_burst(&self, new_rate: Frequency, burst: u64)
Updates an event throughput and an independent burst, in whole events.
Sourcepub fn event_rate(&self) -> Frequency
pub fn event_rate(&self) -> Frequency
The configured event throughput in whole events per second, rounded down. Zero means no limit.
Source§impl TokenBucket
impl TokenBucket
pub fn new() -> Self
Sourcepub fn with_clock(clock: Arc<dyn MonotonicClock>) -> Self
pub fn with_clock(clock: Arc<dyn MonotonicClock>) -> Self
Constructs a bucket backed by a caller-supplied [MonotonicClock].
Production code uses TokenBucket::new, which supplies a
[StdMonotonicClock]. Tests pass a
[qubit_clock::ManualMonotonicClock], so refill windows advance by an
exact, controlled amount instead of by wall-clock sleeping.