Lazy-Limit
Lazy-Limit is a lightweight and flexible Rust library for implementing rate limiting based on IP addresses or custom identifiers. It supports global rate limits, route-specific rules, and an override mode for fine-grained control. Designed for ease of use, it integrates seamlessly with asynchronous Rust applications using Tokio, making it ideal for web servers, APIs, or any networked application requiring rate limiting.
Features
- Global Rate Limiting: Apply a default rate limit across all requests.
- Route-Specific Rules: Define custom rate limits for specific routes or endpoints.
- Override Mode: Bypass global limits to enforce only route-specific rules when needed.
- Memory Management: Built-in garbage collection to manage memory usage for request records.
- Asynchronous Design: Built on Tokio for non-blocking, high-performance rate limiting.
- Customizable Configuration: Set maximum memory usage, garbage collection intervals, and more.
- Thread-Safe: Uses
Arc
andRwLock
for safe concurrent access. - Extensive Testing: Comprehensive unit tests and a demo example to verify functionality.
Installation
Add the following to your Cargo.toml
:
[]
= "1"
Ensure you have the required dependencies:
= { = "1", = ["full"] }
= "1"
Usage
Initializing the Rate Limiter
The rate limiter must be initialized once at application startup using the init_rate_limiter!
macro. You can specify a default global rule, optional route-specific rules, and memory limits.
use *;
use Duration as StdDuration;
async
Checking Rate Limits
Use the limit!
macro to check if a request should be allowed based on the identifier (e.g., IP address) and route.
let allowed = limit!.await;
if allowed else
Override Mode
Use the limit_override!
macro to apply only route-specific rules, ignoring the global limit.
let allowed = limit_override!.await;
if allowed else
Example Demo
The library includes a demo in examples/demo.rs
that showcases its features:
- Basic Global Rate Limiting: Tests the global limit of 5 requests per second.
- Route-Specific Rules: Demonstrates how global and route-specific limits interact.
- Override Mode: Shows how to bypass global limits for specific routes.
- Multiple Users: Verifies independent rate limiting for different identifiers.
- Long Interval Rules: Tests rules with longer time windows (e.g., 3 requests per minute).
To run the demo:
Expected output includes detailed test results for each scenario, confirming the rate limiter's behavior.
Project Structure
lazy-limit/
├── examples/
│ └── demo.rs # Example demonstrating rate limiting features
├── src/
│ ├── config.rs # Configuration for rate limiter rules
│ ├── gc.rs # Garbage collection for memory management
│ ├── lib.rs # Main library entry point and macros
│ ├── limiter.rs # Core rate limiter implementation
│ └── types.rs # Data types for duration, rules, and request records
├── Cargo.toml # Project metadata and dependencies
├── LICENSE # MIT License
└── README.md # This file
Configuration Options
- Default Rule: Set a global rate limit using
RuleConfig::new(Duration, limit)
. - Route-Specific Rules: Add rules for specific routes using the
routes
field ininit_rate_limiter!
. - Max Memory: Limit memory usage for request records (default: 64MB).
- Garbage Collection Interval: Configure how often stale records are cleaned (default: 10 seconds).
Example configuration:
let config = new
.add_route_rule
.with_max_memory // 32MB
.with_gc_interval; // GC every 5 seconds
Testing
The library includes comprehensive unit tests to ensure reliability:
This runs tests in src/lib.rs
and src/limiter.rs
, covering:
- Basic rate limiting
- Route-specific rules with global limits
- Override mode
- Multiple users
- Long interval rules
Memory Management
The rate limiter includes a garbage collector (gc.rs
) that:
- Routine Cleanup: Removes stale records older than the longest rule interval plus a 5-minute buffer.
- Aggressive Cleanup: Triggered when memory usage exceeds the configured limit, removing oldest entries to stay within 80% of the max memory.
The garbage collector runs asynchronously in a Tokio task, ensuring non-blocking operation.
Limitations
- Single Initialization: The rate limiter can only be initialized once. Attempting to call
init_rate_limiter!
multiple times will panic. - Static Configuration: Rules are set at initialization and cannot be modified at runtime.
- Memory Estimation: Memory usage calculations are approximate and may vary based on the Rust allocator.
Contributing
Contributions are welcome! Please submit issues or pull requests to the GitHub repository.
- Fork the repository.
- Create a new branch (
git checkout -b feature/your-feature
). - Make your changes and commit (
git commit -m "Add your feature"
). - Push to the branch (
git push origin feature/your-feature
). - Open a pull request.
License
This project is licensed under the MIT License. See the LICENSE file for details.
Contact
For questions or support, please open an issue on the GitHub repository.