Skip to content

Kuanlin/rate-guard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rate-Guard

A flexible and type-safe rate limiting library for Rust.

Crates.io Documentation License: MIT License: Apache 2.0

Rate-Guard provides a unified interface for multiple rate limiting algorithms with Duration-based configuration, type-safe time handling, and comprehensive error reporting. It's designed to integrate seamlessly with Rust's async ecosystem while maintaining high performance through zero-cost abstractions.

Features

  • Duration-Based Configuration: All timing parameters use std::time::Duration
  • Multiple Algorithms: token bucket, fixed window counter, sliding window counter, and approximate sliding window
  • Time Source Abstraction: Pluggable time sources (mock, std::time, etc.)
  • Precision Control: Compile-time precision selection for optimal performance
  • Builder Pattern: Fluent configuration API with compile-time validation
  • Zero-Cost Abstractions: High-level ergonomics without runtime overhead
  • Comprehensive Testing: Built-in mock time source for deterministic testing

Quick Start

Add this to your Cargo.toml:

[dependencies]
rate-guard = "0.1.0"

# Optional: Enable std::time support
rate-guard = { version = "0.1.0", features = ["std-time"] }

Basic usage:

use rate_guard::{Nanos, MockTimeSource, RateLimit};
use rate_guard::limits::TokenBucketBuilder;
use std::time::Duration;

// Create a token bucket that allows 100 requests with 10 refills every 100ms
let bucket = TokenBucketBuilder::builder()
    .capacity(100)
    .refill_amount(10)
    .refill_every(Duration::from_millis(100))
    .with_time(MockTimeSource::new())
    .with_precision::<Nanos>()
    .build()
    .unwrap();

// Check if request is allowed
match bucket.try_acquire(1) {
    Ok(()) => println!("Request allowed"),
    Err(_) => println!("Rate limited"),
}

// Check remaining capacity
println!("Remaining: {}", bucket.capacity_remaining().unwrap());

Usage Examples

Token Bucket (Burst-friendly)

Allows bursts up to capacity with sustained rate limiting:

use rate_guard::{Millis, MockTimeSource, RateLimit};
use rate_guard::limits::TokenBucketBuilder;
use std::time::Duration;

let time_source = MockTimeSource::new();
let bucket = TokenBucketBuilder::builder()
    .capacity(100)
    .refill_amount(10)
    .refill_every(Duration::from_millis(100))
    .with_time(time_source.clone())
    .with_precision::<Millis>()
    .build()
    .unwrap();

assert!(bucket.try_acquire(50).is_ok());
time_source.advance(Duration::from_millis(200));
assert_eq!(bucket.capacity_remaining().unwrap(), 70); // 50 + 20 refill

Fixed Window Counter

use rate_guard::{Millis, MockTimeSource, RateLimit};
use rate_guard::limits::FixedWindowCounterBuilder;
use std::time::Duration;

let counter = FixedWindowCounterBuilder::builder()
    .capacity(100)
    .window_duration(Duration::from_secs(60))
    .with_time(MockTimeSource::new())
    .with_precision::<Millis>()
    .build()
    .unwrap();

assert!(counter.try_acquire(50).is_ok());

Sliding Window Counter

use rate_guard::{Millis, MockTimeSource, RateLimit};
use rate_guard::limits::SlidingWindowCounterBuilder;
use std::time::Duration;

let counter = SlidingWindowCounterBuilder::builder()
    .capacity(100)
    .bucket_duration(Duration::from_secs(10))
    .bucket_count(6)
    .with_time(MockTimeSource::new())
    .with_precision::<Millis>()
    .build()
    .unwrap();

assert!(counter.try_acquire(25).is_ok());

Approximate Sliding Window

use rate_guard::{Millis, MockTimeSource, RateLimit};
use rate_guard::limits::ApproximateSlidingWindowBuilder;
use std::time::Duration;

let window = ApproximateSlidingWindowBuilder::builder()
    .capacity(100)
    .window_duration(Duration::from_secs(60))
    .with_time(MockTimeSource::new())
    .with_precision::<Millis>()
    .build()
    .unwrap();

assert!(window.try_acquire(30).is_ok());

Production Usage

For detailed production environment integration examples, please refer to the examples/ directory:

Recommended Production Configuration

#[cfg(feature = "std-time")]
use rate_guard::{Nanos, StdTimeSource, RateLimit};
#[cfg(feature = "std-time")]
use rate_guard::limits::TokenBucketBuilder;
#[cfg(feature = "std-time")]
use std::time::Duration;

#[cfg(feature = "std-time")]
fn create_production_limiter() -> impl RateLimit {
    TokenBucketBuilder::builder()
        .capacity(1000)
        .refill_amount(100)
        .refill_every(Duration::from_millis(100))
        .with_time(StdTimeSource::new())
        .with_precision::<Nanos>()
        .build()
        .unwrap()
}

Tip: Start with conservative settings and adjust based on your actual requirements


Verbose Error Handling

use rate_guard::{Millis, MockTimeSource, RateLimit, RateLimitError};
use rate_guard::limits::TokenBucketBuilder;
use std::time::Duration;

let bucket = TokenBucketBuilder::builder()
    .capacity(10)
    .refill_amount(1)
    .refill_every(Duration::from_secs(1))
    .with_time(MockTimeSource::new())
    .with_precision::<Millis>()
    .build()
    .unwrap();

bucket.try_acquire(10).unwrap();

match bucket.try_acquire_verbose(5) {
    Ok(()) => println!("Request allowed"),
    Err(RateLimitError::InsufficientCapacity { acquiring, available, retry_after }) => {
        println!("Need {} tokens, only {} available, retry after {:?}", 
                 acquiring, available, retry_after);
    }
    Err(e) => println!("Other error: {}", e),
}

Testing with Mock Time

use rate_guard::{Millis, MockTimeSource, RateLimit};
use rate_guard::limits::TokenBucketBuilder;
use std::time::Duration;

let time_source = MockTimeSource::new();
let bucket = TokenBucketBuilder::builder()
    .capacity(50)
    .refill_amount(5)
    .refill_every(Duration::from_millis(200))
    .with_time(time_source.clone())
    .with_precision::<Millis>()
    .build()
    .unwrap();

assert!(bucket.try_acquire(50).is_ok());
assert_eq!(bucket.capacity_remaining().unwrap(), 0);

time_source.advance(Duration::from_millis(200));
assert_eq!(bucket.capacity_remaining().unwrap(), 5);

Precision Types

use rate_guard::{Nanos, Micros, Millis, Secs, MockTimeSource};
use rate_guard::limits::TokenBucketBuilder;
use std::time::Duration;

let nano_bucket = TokenBucketBuilder::builder()
    .capacity(100).refill_amount(10)
    .refill_every(Duration::from_nanos(100))
    .with_time(MockTimeSource::new())
    .with_precision::<Nanos>()
    .build().unwrap();

let micro_bucket = TokenBucketBuilder::builder()
    .capacity(100).refill_amount(10)
    .refill_every(Duration::from_micros(100))
    .with_time(MockTimeSource::new())
    .with_precision::<Micros>()
    .build().unwrap();

let milli_bucket = TokenBucketBuilder::builder()
    .capacity(100).refill_amount(10)
    .refill_every(Duration::from_millis(100))
    .with_time(MockTimeSource::new())
    .with_precision::<Millis>()
    .build().unwrap();

let sec_bucket = TokenBucketBuilder::builder()
    .capacity(100).refill_amount(10)
    .refill_every(Duration::from_secs(1))
    .with_time(MockTimeSource::new())
    .with_precision::<Secs>()
    .build().unwrap();

Feature Flags

Time Sources (Optional)

  • std-time - Standard library time source (recommended for production)
  • tokio-time - Tokio time source (for async environments)

Precision Types (Mutually Exclusive, defaults to u64)

  • tick-u64 (default) - 64-bit precision, suitable for most scenarios
  • tick-u128 - 128-bit precision, for extreme long-duration use cases
# Default configuration
rate-guard = "0.1.0"

# Production recommended
rate-guard = { version = "0.1.0", features = ["std-time"] }

# Special requirements: high precision
rate-guard = { version = "0.1.0", features = ["std-time", "tick-u128"] }

Minimum Supported Rust Version (MSRV)

This crate supports Rust 1.70.0 or newer.

Feature-specific Requirements

  • Basic functionality: Works with the specified MSRV
  • tokio-time feature: Requires Rust 1.70.0+ (tokio requirement)

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.


Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

About

Thread-safe rate limiting library with multiple algorithms and Duration-based configuration

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages