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.
§Status
Pre-1.0 (release candidate), hardened, API frozen until 1.0: the lock-free
Bucket, the Tier-2 BucketBuilder, BucketConfig, Decision,
BucketError, and the TokenBucket trait. The surface is validated
against the first-consumer pattern (trait-based checks, injected shared
clock, Decision-to-retry mapping) with no friction. 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 safety
contract (no panic, no wrap, no over-grant, tokens in [0, capacity]) holds
under adversarial inputs and extreme uptime, defended by loom, a stress
test, an allocation audit, an adversarial/edge suite, and proptest. The
bucket’s own accounting measures around six 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. A bare
no_std build (default-features = false) currently exposes only
VERSION — the no_std-capable, caller-driven core lands with the lock-free
rewrite in 0.3.
§Design goals
- Lock-free acquire. One
compare_exchange_weakon a packed atomic word; noMutex, 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 whenstdis disabled.
§Feature flags
| Feature | Default | Description |
|---|---|---|
std | yes | Standard library. Off → no_std, caller drives time. |
clock | yes | Pluggable 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.
- Bucket
Builder - A fluent builder for a
Bucketwhen the Tier-1 constructors are not enough. - Bucket
Config - The parameters that define a token bucket.
Enums§
- Bucket
Error - 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.tomlat compile time.
Traits§
- Token
Bucket - The token-bucket surface a consumer depends on.