pub struct Throttle<C: Clock = SystemClock> { /* private fields */ }std only.Expand description
A single outbound throttle backed by a token bucket.
This is the Tier-1 surface: construct one with per_second
or per_duration, then pace your outbound work with
acquire. Because throttle-net protects downstreams, the
headline operation waits for a token rather than rejecting the caller —
you are slowing your own requests, not dropping someone else’s. When you would
rather not wait, try_acquire reports the outcome
immediately.
The bucket refills smoothly and starts full, so a burst up to the capacity is
admitted at once and the sustained rate is the refill rate. Token accounting
is lock-free (a single atomic compare-and-swap per acquire), and time is read
from an injectable Clock — SystemClock in production, or a
ManualClock in tests via with_clock.
§Examples
use throttle_net::Throttle;
// 100 requests per second, bursting up to 100.
let throttle = Throttle::per_second(100);
throttle.acquire().await?; // returns as soon as a token is freeImplementations§
Source§impl Throttle<SystemClock>
impl Throttle<SystemClock>
Sourcepub fn per_second(rate: u32) -> Self
pub fn per_second(rate: u32) -> Self
Creates a throttle that admits rate units per second, bursting up to
rate, driven by the OS monotonic clock.
A rate of 0 yields a throttle that grants nothing; an
acquire on it returns
ThrottleError::CostExceedsCapacity.
§Examples
use throttle_net::Throttle;
let throttle = Throttle::per_second(50);
assert_eq!(throttle.capacity(), 50);
assert!(throttle.try_acquire());Sourcepub fn per_duration(amount: u32, period: Duration) -> Self
pub fn per_duration(amount: u32, period: Duration) -> Self
Creates a throttle that admits amount units every period, bursting up
to amount, driven by the OS monotonic clock.
Use this when the natural window is not one second — for example, sixty calls per minute, or five per hundred milliseconds.
§Examples
use std::time::Duration;
use throttle_net::Throttle;
// 60 requests per minute.
let throttle = Throttle::per_duration(60, Duration::from_secs(60));
assert_eq!(throttle.capacity(), 60);Source§impl<C: Clock> Throttle<C>
impl<C: Clock> Throttle<C>
Sourcepub fn with_clock<C2: Clock>(self, clock: C2) -> Throttle<C2>
pub fn with_clock<C2: Clock>(self, clock: C2) -> Throttle<C2>
Replaces the time source, returning a throttle driven by clock.
The common use is deterministic testing: inject a
ManualClock (shared via an
Arc) and drive refills by advancing it, with no real
sleeping. The bucket is re-anchored to the new clock and starts full.
§Examples
use std::sync::Arc;
use std::time::Duration;
use clock_lib::ManualClock;
use throttle_net::Throttle;
let clock = Arc::new(ManualClock::new());
let throttle = Throttle::per_second(2).with_clock(clock.clone());
assert!(throttle.try_acquire());
assert!(throttle.try_acquire());
assert!(!throttle.try_acquire()); // drained
clock.advance(Duration::from_secs(1)); // a full period refills it
assert!(throttle.try_acquire());Sourcepub fn capacity(&self) -> u32
pub fn capacity(&self) -> u32
The maximum number of tokens the throttle can hold (its burst size).
Sourcepub fn available(&self) -> u32
pub fn available(&self) -> u32
The number of whole tokens available right now.
A point-in-time read for observability and tests, not a reservation.
Sourcepub fn try_acquire(&self) -> bool
pub fn try_acquire(&self) -> bool
Attempts to take one token without waiting, returning whether it was granted.
§Examples
use throttle_net::Throttle;
let throttle = Throttle::per_second(1);
assert!(throttle.try_acquire()); // the one token
assert!(!throttle.try_acquire()); // none left this instantSourcepub fn try_acquire_with_cost(&self, cost: u32) -> bool
pub fn try_acquire_with_cost(&self, cost: u32) -> bool
Attempts to take cost tokens without waiting, returning whether they
were granted.
Granting is all-or-nothing: either every token is deducted or none is.
§Examples
use throttle_net::Throttle;
let throttle = Throttle::per_second(10);
assert!(throttle.try_acquire_with_cost(7));
assert!(!throttle.try_acquire_with_cost(7)); // only 3 leftSourcepub fn peek(&self, cost: u32) -> Decision
pub fn peek(&self, cost: u32) -> Decision
Reports whether cost tokens would be granted now, without taking them.
This is the non-consuming counterpart to try_acquire_with_cost,
used by composite limiters to poll a constituent before committing. The
Decision::Retry wait is estimated from the refill rate, so it is a
close guide rather than an exact promise.
§Examples
use throttle_net::{Decision, Throttle};
let throttle = Throttle::per_second(4);
assert_eq!(throttle.peek(3), Decision::Acquired); // would grant, took nothing
assert!(throttle.try_acquire_with_cost(4)); // still fullSource§impl<C: Clock> Throttle<C>
impl<C: Clock> Throttle<C>
Sourcepub async fn acquire(&self) -> Result<(), ThrottleError>
Available on crate feature runtime only.
pub async fn acquire(&self) -> Result<(), ThrottleError>
runtime only.Takes one token, waiting until one is available.
This is the marquee outbound operation: it paces the caller instead of
rejecting it. It returns once a token has been deducted, or
ThrottleError::CostExceedsCapacity if the throttle’s capacity is zero.
§Errors
Returns ThrottleError::CostExceedsCapacity when the capacity is 0,
because a single token can never be granted.
§Examples
use throttle_net::Throttle;
let throttle = Throttle::per_second(100);
throttle.acquire().await?;Sourcepub async fn acquire_with_cost(&self, cost: u32) -> Result<(), ThrottleError>
Available on crate feature runtime only.
pub async fn acquire_with_cost(&self, cost: u32) -> Result<(), ThrottleError>
runtime only.Takes cost tokens, waiting until they are available.
The cost lets one request weigh more than another — a batch of ten, or an LLM call billed by token count. The waiter sleeps for the bucket’s own estimate of the refill time and retries, so it converges without busy spinning even under contention.
§Errors
Returns ThrottleError::CostExceedsCapacity when cost exceeds the
throttle’s capacity; that request can never be granted, so it fails fast
rather than waiting forever.
§Examples
use throttle_net::Throttle;
let throttle = Throttle::per_second(1000);
throttle.acquire_with_cost(250).await?; // a heavier request