Expand description
§Gardal
A performance-focused token bucket rate limiting and throttling library for Rust with optional async support.
§Features
- High Performance: Optimized for minimal overhead with pluggable storage strategies
- Flexible Clock Sources: Support for standard, manual, fast (quanta), and Tokio clocks
- Async Support: Optional async/await support with stream rate limiting
- Thread-Safe: Multiple storage options including atomic and shared storage
- Zero-Cost Abstractions: Generic design allows compile-time optimization
- Configurable: Support for different rate limits (per second, minute, hour) and burst sizes
§Quick Start
Add this to your Cargo.toml
:
[dependencies]
gardal = "0.0.1-alpha.2"
# For async support
gardal = { version = "0.0.1-alpha.2", features = ["async"] }
# For high-performance timing
gardal = { version = "0.0.1-alpha.2", features = ["quanta"] }
# For high-resolution async timers
gardal = { version = "0.0.1-alpha.2", features = ["async", "tokio-hrtime"] }
§Basic Usage
use gardal::{RateLimit, TokenBucket};
use nonzero_ext::nonzero;
// Create a token bucket: 10 tokens per second, burst of 20
let bucket = TokenBucket::new(RateLimit::per_second_and_burst(
nonzero!(10u32),
nonzero!(20u32),
));
// Consume 5 tokens
match bucket.consume(nonzero!(5u32)) {
Some(tokens) => println!("Consumed {} tokens", tokens.as_u64()),
None => println!("Not enough tokens available"),
}
§Async Stream Rate Limiting
use futures::{StreamExt, stream};
use gardal::futures::RateLimitedStreamExt;
use gardal::{RateLimit, TokenBucket, AtomicSharedStorage, QuantaClock};
use nonzero_ext::nonzero;
#[tokio::main]
async fn main() {
let limit = RateLimit::per_second(nonzero!(5u32));
let bucket = TokenBucket::<AtomicSharedStorage, _>::from_parts(
limit,
QuantaClock::default()
);
let mut stream = stream::iter(1..=100)
.rate_limit(bucket)
.boxed();
while let Some(item) = stream.next().await {
println!("Processed item: {}", item);
}
}
§Rate Limit Configuration
Gardal supports various rate limit configurations:
use gardal::RateLimit;
use nonzero_ext::nonzero;
// 10 requests per second
let limit = RateLimit::per_second(nonzero!(10u32));
// 10 requests per second with burst of 20
let limit = RateLimit::per_second_and_burst(nonzero!(10u32), nonzero!(20u32));
// 100 requests per minute
let limit = RateLimit::per_minute(nonzero!(100u32));
// 1000 requests per hour
let limit = RateLimit::per_hour(nonzero!(1000u32));
§Storage Strategies
Choose the appropriate storage strategy for your use case:
AtomicStorage
: Basic atomic storage for single-threaded or low-contention scenariosPaddedAtomicStorage
: Cache-line padded atomic storage (default) for better performanceAtomicSharedStorage
: Optimized for high-contention multi-threaded scenariosLocalStorage
: Thread-local storage for single-threaded applications
use gardal::{TokenBucket, AtomicSharedStorage, RateLimit};
use nonzero_ext::nonzero;
// Explicitly specify storage type
let bucket = TokenBucket::<AtomicSharedStorage>::from_parts(
RateLimit::per_second(nonzero!(10u32)),
gardal::StdClock::default()
);
§Clock Sources
Gardal supports multiple clock implementations:
FastClock
: High-performance quanta-based clock (requiresquanta
feature)StdClock
: Standard library clock (default)TokioClock
: Tokio-based clock for async applications (requiresasync
feature)ManualClock
: Manual clock for testing
§High-Resolution Async Timers
For applications requiring precise timing in async contexts, enable the tokio-hrtime
feature:
use futures::{StreamExt, stream};
use gardal::futures::RateLimitedStreamExt;
use gardal::{RateLimit, TokenBucket};
use nonzero_ext::nonzero;
#[tokio::main]
async fn main() {
let limit = RateLimit::per_second(nonzero!(1000u32)); // High-frequency rate limiting
let bucket = TokenBucket::new(limit);
let mut stream = stream::iter(1..=10000)
.rate_limit(bucket)
.boxed();
// Uses tokio-hrtime for microsecond-precision delays
while let Some(item) = stream.next().await {
println!("Processed item: {}", item);
}
}
The tokio-hrtime
feature provides:
- Microsecond precision: More accurate timing than standard Tokio timers
- Better performance: Optimized for high-frequency rate limiting scenarios
- Reduced jitter: More consistent timing behavior under load
§Performance
Gardal is designed for high-performance scenarios:
- Zero-allocation token consumption in the fast path
- Lock-free atomic operations
- Pluggable storage strategies for different contention patterns
- Optional high-resolution timing with quanta
- Compile-time optimizations through generics
Run benchmarks with:
cargo bench
§Features
async
: Enables async/await support and stream rate limitingtokio
: Enables Tokio clock integrationquanta
: Enables high-performance timing with the quanta cratetokio-hrtime
: Enables high-resolution async timers for microsecond-precision rate limiting
§Examples
See the examples/
directory for more usage patterns:
basic.rs
: Basic token bucket usagefast_clock.rs
: Using high-performance clocksstreams.rs
: Async stream rate limiting
§Minimum Supported Rust Version (MSRV)
Gardal requires Rust 1.87.0 or later.
§License
Apache License, Version 2.0 (LICENSE-APACHE
§Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
§Core Components
TokenBucket
- The main token bucket implementation with pluggable storage and clockRateLimit
- Configuration for rate and burst limitsClock
trait and implementations for time sources- Storage implementations for different concurrency needs
§Quick Start
use std::num::NonZeroU32;
use gardal::{TokenBucket, RateLimit};
// Create a rate limit: 10 tokens per second, burst of 20
let limit = RateLimit::per_second_and_burst(
NonZeroU32::new(10).unwrap(),
NonZeroU32::new(20).unwrap()
);
// Create token bucket
let bucket = TokenBucket::new(limit);
// Try to consume tokens
if let Some(tokens) = bucket.consume(NonZeroU32::new(5).unwrap()) {
println!("Consumed {} tokens", tokens.as_u64());
}
Structs§
- Atomic
Shared Storage - Shared atomic storage that can be cloned and shared across multiple token buckets.
- Atomic
Storage - Thread-safe atomic storage implementation.
- Exceeded
Burst Capacity - Error returned when trying to borrow more tokens than the burst capacity allows.
- Local
Storage - Non-atomic storage implementation for single-threaded use.
- Manual
Clock - Manual clock implementation for testing and simulation.
- Padded
Atomic Storage - Cache-line padded atomic storage to prevent false sharing.
- Rate
Limit - Configuration for token bucket rate and burst limits.
- Rate
Limited - Error returned when a token consumption request is rate limited.
- StdClock
- Standard clock implementation using
std::time::Instant
. - Token
Bucket - A token bucket rate limiter with configurable storage and clock implementations.
- Tokens
- Represents a non-zero number of consumed tokens.
Traits§
- Clock
- Trait for monotonic clock implementations used by token buckets.