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.

§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_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.