Skip to main content

Bucket

Struct Bucket 

Source
pub struct Bucket<C: Clock = SystemClock> { /* private fields */ }
Expand description

A token bucket: a counter that refills over time and grants tokens on demand.

A bucket holds up to its capacity in tokens, accrues more at a fixed rate, and hands them out when asked. The hot path is lock-free — a single compare_exchange_weak on a packed atomic word — and allocation-free. Refill is lazy: there is no background thread or timer, the token count is brought current from the monotonic clock the instant you call acquire, try_acquire, or available.

The type parameter C is the time source. It defaults to SystemClock (the OS monotonic clock); inject a ManualClock with with_clock to drive time by hand in tests. Bucket is Send + Sync whenever its clock is, which every Clock implementation guarantees.

§Limits

The packed representation caps capacity at about 4.29 million tokens (u32::MAX millitokens). Time is tracked as 32-bit milliseconds since construction and wraps every ~49.7 days; the wrap is handled, so an actively-used bucket refills correctly indefinitely (only a bucket idle for longer than that may under-refill once, safely, on its next use).

§Examples

The one-line common case:

use better_bucket::Bucket;

let bucket = Bucket::per_second(100);
if bucket.try_acquire(1) {
    // allowed — do the work
}

Implementations§

Source§

impl Bucket<SystemClock>

Source

pub fn per_second(rate: u32) -> Self

Creates a bucket of capacity rate that refills rate tokens per second, starting full, driven by the OS monotonic clock.

This is the headline Tier-1 constructor. A rate of 0 yields a bucket that grants nothing (capacity 0); use BucketConfig::new when you want zero rejected as an error.

§Examples
use better_bucket::Bucket;

let bucket = Bucket::per_second(50);
assert_eq!(bucket.capacity(), 50);
assert!(bucket.try_acquire(1));
Source

pub fn per_duration(amount: u32, period: Duration) -> Self

Creates a bucket of capacity amount that refills amount tokens every period, starting full, driven by the OS monotonic clock.

Use this when the natural rate is not per-second — e.g. 5 tokens per 100 milliseconds, or 1000 per minute. An amount of 0 or a zero period yields a bucket that grants nothing.

§Examples
use better_bucket::Bucket;
use std::time::Duration;

// 5 tokens every 100ms.
let bucket = Bucket::per_duration(5, Duration::from_millis(100));
assert_eq!(bucket.capacity(), 5);
Source

pub fn from_config(config: BucketConfig) -> Self

Creates a bucket from a validated BucketConfig, driven by the OS monotonic clock.

Use this when you need full control over capacity, rate, and initial fill independently (e.g. a large burst ceiling with a slow refill, or a bucket that starts empty).

§Examples
use better_bucket::{Bucket, BucketConfig};
use std::time::Duration;

// 500-token burst, 100/sec refill, starting empty.
let config = BucketConfig::new(500, 100, Duration::from_secs(1), 0)?;
let bucket = Bucket::from_config(config);
assert_eq!(bucket.available(), 0);
Source§

impl<C: Clock> Bucket<C>

Source

pub fn with_clock<C2: Clock>(self, clock: C2) -> Bucket<C2>

Replaces the bucket’s time source, resetting it to its initial fill anchored at the new clock’s current reading.

This is the clock-injection seam. The intended use is immediately after construction — chiefly in tests, where injecting a ManualClock makes refill behaviour deterministic with no sleep.

§Examples
use better_bucket::Bucket;
use clock_lib::ManualClock;
use std::sync::Arc;
use std::time::Duration;

let clock = Arc::new(ManualClock::new());
let bucket = Bucket::per_second(10).with_clock(Arc::clone(&clock));

assert!(bucket.try_acquire(10)); // drain it
assert!(!bucket.try_acquire(1)); // empty

clock.advance(Duration::from_secs(1)); // no real sleep
assert_eq!(bucket.available(), 10);  // fully refilled
Source

pub fn acquire(&self, n: u32) -> Decision

Attempts to take n tokens, returning the full Decision.

Brings the bucket current (lazy refill) and, if at least n tokens are available, deducts them and returns Decision::Allowed. Otherwise the bucket is left untouched and Decision::Denied carries the minimum wait until the request would succeed. Requesting 0 always succeeds; requesting more than the capacity can never succeed (the denial’s retry_after is Duration::MAX).

This never blocks and never allocates.

§Examples
use better_bucket::{Bucket, Decision};

let bucket = Bucket::per_second(5);
assert_eq!(bucket.acquire(3), Decision::Allowed);
assert_eq!(bucket.available(), 2);
Source

pub fn try_acquire(&self, n: u32) -> bool

Attempts to take n tokens, returning whether it succeeded.

The one-line convenience over acquire: equivalent to self.acquire(n).is_allowed(), for the common case where you only need allow/deny and not the retry hint.

§Examples
use better_bucket::Bucket;

let bucket = Bucket::per_second(1);
assert!(bucket.try_acquire(1));
assert!(!bucket.try_acquire(1)); // drained
Source

pub fn available(&self) -> u32

Returns how many whole tokens are available right now, after lazy refill.

This is a momentary snapshot; under concurrent acquires it can be stale the instant it returns. Treat it as advisory.

§Examples
use better_bucket::Bucket;

let bucket = Bucket::per_second(10);
assert_eq!(bucket.available(), 10);
assert!(bucket.try_acquire(4));
assert_eq!(bucket.available(), 6);
Source

pub const fn capacity(&self) -> u32

Returns the bucket’s capacity (its burst ceiling), in whole tokens.

§Examples
use better_bucket::Bucket;

assert_eq!(Bucket::per_second(64).capacity(), 64);
Source

pub const fn config(&self) -> BucketConfig

Returns the configuration this bucket was built from.

§Examples
use better_bucket::Bucket;
use std::time::Duration;

let bucket = Bucket::per_second(10);
assert_eq!(bucket.config().refill_period(), Duration::from_secs(1));
Source

pub fn reset(&self)

Refills the bucket to full and marks it current as of now.

Use it to discard accumulated debt and grant a fresh burst — for example at the start of a new billing window, or to clear a backlog after a dependency recovers. Long uptime needs no special handling: the millisecond counter wraps safely, so an actively-used bucket never stalls.

§Examples
use better_bucket::Bucket;

let bucket = Bucket::per_second(4);
assert!(bucket.try_acquire(4));
assert_eq!(bucket.available(), 0);
bucket.reset();
assert_eq!(bucket.available(), 4);
Source§

impl Bucket<SystemClock>

Source

pub fn builder() -> BucketBuilder

Starts a BucketBuilder for explicit configuration.

The Tier-2 entry point, for when per_second / per_duration are not enough — e.g. a capacity and refill rate that differ, or a non-full initial fill.

§Examples
use better_bucket::Bucket;
use std::time::Duration;

let bucket = Bucket::builder()
    .capacity(500)
    .refill(100, Duration::from_secs(1))
    .build()?;

Trait Implementations§

Source§

impl<C: Clock> Debug for Bucket<C>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<C: Clock> TokenBucket for Bucket<C>

Source§

fn acquire(&self, n: u32) -> Decision

Attempts to take n tokens, returning the full Decision.
Source§

fn try_acquire(&self, n: u32) -> bool

Attempts to take n tokens, returning whether it succeeded.
Source§

fn available(&self) -> u32

Returns the whole tokens available right now, after lazy refill.
Source§

fn capacity(&self) -> u32

Returns the bucket’s capacity (its burst ceiling).

Auto Trait Implementations§

§

impl<C = SystemClock> !Freeze for Bucket<C>

§

impl<C> RefUnwindSafe for Bucket<C>
where C: RefUnwindSafe,

§

impl<C> Send for Bucket<C>

§

impl<C> Sync for Bucket<C>

§

impl<C> Unpin for Bucket<C>
where C: Unpin,

§

impl<C> UnsafeUnpin for Bucket<C>
where C: UnsafeUnpin,

§

impl<C> UnwindSafe for Bucket<C>
where C: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<E> WithErrorCode<E> for E

Source§

fn with_code(self, code: impl Into<String>) -> CodedError<E>

Attach an error code to an error