Skip to main content

Crate better_bucket

Crate better_bucket 

Source
Expand description

§better-bucket

A genuinely better token bucket for Rust. The hot path — try_acquire — is designed to be lock-free, allocation-free, and cache-aligned: a single compare-and-swap over a packed (tokens, last_refill_tick) word. Refill is lazy, computed from a monotonic clock the instant you ask, so an idle bucket costs nothing — no background timer thread, no per-tick wakeups. The defining correctness property is that the bucket never over-grants: across any concurrent interleaving, the total tokens handed out never exceed capacity plus accrued refill.

The crate is a single-purpose primitive. It owns token-bucket accounting and nothing else, so it can sit at the bottom of a dependency tree (its first consumer is the rate-net rate limiter) without dragging in an async runtime or a keyed-state store.

§Guarantees

The safety contract holds under any concurrent interleaving, adversarial input, and extreme uptime: no panic, no wrap, no over-grant, and tokens always within [0, capacity]. It is defended by loom model checking, a multi-thread stress test, an allocation audit, an adversarial/edge suite, and proptest. try_acquire is a single compare_exchange_weak on a packed atomic word — allocation-free, cache-line aligned, with a division-free fixed-point refill. The bucket’s own accounting measures a few nanoseconds; end-to-end try_acquire is bounded by the monotonic clock read (see docs/BENCHMARKS.md).

Token bucket is the crate’s sole algorithm by design — leaky-bucket and sliding-window limiting live in the downstream rate-net crate.

use better_bucket::Bucket;

// 100 tokens per second, capacity 100.
let bucket = Bucket::per_second(100);

if bucket.try_acquire(1) {
    // allowed — do the work
} else {
    // denied — shed load / return 429 / back off
}

The bucket reads time from clock-lib; the clock feature (on by default) provides it and implies std. The lock-free accounting core needs only core, but the shipped Bucket constructors read the clock, so a bare no_std build (default-features = false) exposes only VERSION.

§Design goals

  • Lock-free acquire. One compare_exchange_weak on a packed atomic word; no Mutex, no parking on the hot path.
  • Allocation-free steady state. A bucket is a small, cache-line-aligned value with no heap tail; acquiring never allocates.
  • Lazy refill. Tokens accrue from elapsed monotonic time on access — no timer thread, no wakeups, no watts burned while idle.
  • Overflow-safe. Every refill and capacity computation is checked or saturating; a hostile request count or a multi-day idle gap can neither wrap the counter nor over-fill the bucket.
  • no_std-capable. The core runs without the standard library; the caller drives time when std is disabled.

§Feature flags

FeatureDefaultDescription
stdyesStandard library. Off → no_std, caller drives time.
clockyesPluggable clock-lib time source plus a mockable clock for deterministic tests.

Structs§

Bucket
A token bucket: a counter that refills over time and grants tokens on demand.
BucketBuilder
A fluent builder for a Bucket when the Tier-1 constructors are not enough.
BucketConfig
The parameters that define a token bucket.

Enums§

BucketError
A configuration rejected at construction time.
Decision
The result of attempting to take tokens from a bucket.

Constants§

VERSION
The version of this crate, taken from Cargo.toml at compile time.

Traits§

TokenBucket
The token-bucket surface a consumer depends on.