Basic Usage
Common patterns and best practices for using Flux Limiter.
Simple Rate Limiting
The most basic use case - check if a request should be allowed:
#![allow(unused)]
fn main() {
use flux_limiter::{FluxLimiter, FluxLimiterConfig, SystemClock};
let config = FluxLimiterConfig::new(10.0, 5.0);
let limiter = FluxLimiter::with_config(config, SystemClock).unwrap();
match limiter.check_request("user_123") {
Ok(decision) if decision.allowed => {
// Process request
println!("Request allowed");
}
Ok(decision) => {
// Rate limited
println!("Rate limited - retry after {:.2}s",
decision.retry_after_seconds.unwrap_or(0.0));
}
Err(e) => {
// Handle error
eprintln!("Error: {}", e);
}
}
}
Working with Different Client ID Types
String Client IDs
#![allow(unused)]
fn main() {
// User IDs, session IDs, API keys
let limiter = FluxLimiter::<String, _>::with_config(config, SystemClock).unwrap();
limiter.check_request("user_123".to_string())?;
limiter.check_request("session_abc".to_string())?;
}
IP Address Client IDs
#![allow(unused)]
fn main() {
use std::net::IpAddr;
let limiter = FluxLimiter::<IpAddr, _>::with_config(config, SystemClock).unwrap();
let client_ip: IpAddr = "192.168.1.1".parse().unwrap();
limiter.check_request(client_ip)?;
}
Numeric Client IDs
#![allow(unused)]
fn main() {
// High-performance numeric IDs
let limiter = FluxLimiter::<u64, _>::with_config(config, SystemClock).unwrap();
limiter.check_request(12345)?;
limiter.check_request(67890)?;
}
Custom Client IDs
#![allow(unused)]
fn main() {
use std::hash::{Hash, Hasher};
#[derive(Clone, PartialEq, Eq, Hash)]
struct ClientId {
user_id: u64,
endpoint: String,
}
let limiter = FluxLimiter::<ClientId, _>::with_config(config, SystemClock).unwrap();
let client = ClientId {
user_id: 123,
endpoint: "/api/data".to_string(),
};
limiter.check_request(client)?;
}
Accessing Configuration
You can query the current rate limiter configuration:
#![allow(unused)]
fn main() {
let limiter = FluxLimiter::with_config(config, SystemClock).unwrap();
println!("Rate: {} req/sec", limiter.rate());
println!("Burst: {}", limiter.burst());
}
Using Decision Metadata
Extracting All Metadata
#![allow(unused)]
fn main() {
match limiter.check_request("user_123") {
Ok(decision) => {
println!("Allowed: {}", decision.allowed);
if let Some(retry_after) = decision.retry_after_seconds {
println!("Retry after: {:.2}s", retry_after);
}
if let Some(remaining) = decision.remaining_capacity {
println!("Remaining capacity: {:.2}", remaining);
}
println!("Reset time: {} ns", decision.reset_time_nanos);
}
Err(e) => eprintln!("Error: {}", e),
}
}
Computing Reset Time
#![allow(unused)]
fn main() {
use std::time::{SystemTime, UNIX_EPOCH, Duration};
match limiter.check_request("user_123") {
Ok(decision) => {
// Convert reset_time_nanos to a timestamp
let reset_duration = Duration::from_nanos(decision.reset_time_nanos);
let reset_time = UNIX_EPOCH + reset_duration;
let now = SystemTime::now();
if let Ok(time_until_reset) = reset_time.duration_since(now) {
println!("Reset in {:.2}s", time_until_reset.as_secs_f64());
}
}
Err(e) => eprintln!("Error: {}", e),
}
}
Sharing Across Threads
Flux Limiter is thread-safe and designed to be shared:
#![allow(unused)]
fn main() {
use std::sync::Arc;
use std::thread;
let config = FluxLimiterConfig::new(100.0, 50.0);
let limiter = Arc::new(FluxLimiter::with_config(config, SystemClock).unwrap());
// Share across multiple threads
let handles: Vec<_> = (0..10)
.map(|i| {
let limiter = Arc::clone(&limiter);
thread::spawn(move || {
let client_id = format!("client_{}", i);
limiter.check_request(client_id)
})
})
.collect();
for handle in handles {
match handle.join().unwrap() {
Ok(decision) => println!("Allowed: {}", decision.allowed),
Err(e) => eprintln!("Error: {}", e),
}
}
}
Async Usage
While Flux Limiter is synchronous, it works seamlessly in async contexts:
use std::sync::Arc;
use tokio;
#[tokio::main]
async fn main() {
let config = FluxLimiterConfig::new(100.0, 50.0);
let limiter = Arc::new(FluxLimiter::with_config(config, SystemClock).unwrap());
// Use in async tasks
let limiter_clone = Arc::clone(&limiter);
tokio::spawn(async move {
match limiter_clone.check_request("user_123") {
Ok(decision) if decision.allowed => {
// Process async request
println!("Processing request...");
}
Ok(_) => println!("Rate limited"),
Err(e) => eprintln!("Error: {}", e),
}
})
.await
.unwrap();
}
Multiple Rate Limiters
You can create different rate limiters for different purposes:
#![allow(unused)]
fn main() {
// Global rate limiter
let global_limiter = FluxLimiter::with_config(
FluxLimiterConfig::new(1000.0, 500.0),
SystemClock
).unwrap();
// Per-user rate limiter
let user_limiter = FluxLimiter::with_config(
FluxLimiterConfig::new(10.0, 5.0),
SystemClock
).unwrap();
// Apply both limits
fn check_both_limits(user_id: &str) -> bool {
match global_limiter.check_request("global") {
Ok(decision) if !decision.allowed => return false,
Err(_) => return false,
_ => {}
}
match user_limiter.check_request(user_id) {
Ok(decision) => decision.allowed,
Err(_) => false,
}
}
}
Idiomatic Error Handling
#![allow(unused)]
fn main() {
use flux_limiter::{FluxLimiter, FluxLimiterError};
fn process_request(limiter: &FluxLimiter<String, SystemClock>, client_id: String) -> Result<(), String> {
let decision = limiter
.check_request(client_id)
.map_err(|e| format!("Rate limiter error: {}", e))?;
if !decision.allowed {
return Err(format!(
"Rate limited. Retry after {:.2}s",
decision.retry_after_seconds.unwrap_or(0.0)
));
}
// Process the request
Ok(())
}
}
Next Steps
- Error Handling - Handle errors gracefully
- Advanced Usage - Memory management and optimization
- Web Integration - Use with web frameworks