skp_ratelimit/algorithm/
mod.rs

1//! Rate limiting algorithm trait and implementations.
2//!
3//! This module defines the `Algorithm` trait and provides implementations
4//! for various rate limiting algorithms.
5//!
6//! # Available Algorithms
7//!
8//! - **GCRA** (`gcra` feature): Generic Cell Rate Algorithm - precise, low memory
9//! - **Token Bucket** (default): Controlled bursts with refilling tokens
10//! - **Leaky Bucket** (`leaky-bucket` feature): Smooth constant output rate
11//! - **Sliding Log** (`sliding-log` feature): High precision, stores all timestamps
12//! - **Sliding Window** (default): Weighted window for balanced accuracy
13//! - **Fixed Window** (default): Simple counter per time window
14//! - **Concurrent** (`concurrent` feature): Limit simultaneous requests
15
16#[cfg(feature = "gcra")]
17mod gcra;
18#[cfg(feature = "leaky-bucket")]
19mod leaky_bucket;
20#[cfg(feature = "sliding-log")]
21mod sliding_log;
22#[cfg(feature = "concurrent")]
23mod concurrent;
24mod fixed_window;
25mod sliding_window;
26mod token_bucket;
27
28#[cfg(feature = "gcra")]
29pub use gcra::GCRA;
30#[cfg(feature = "leaky-bucket")]
31pub use leaky_bucket::LeakyBucket;
32#[cfg(feature = "sliding-log")]
33pub use sliding_log::SlidingLog;
34#[cfg(feature = "concurrent")]
35pub use concurrent::ConcurrentLimiter;
36pub use fixed_window::FixedWindow;
37pub use sliding_window::SlidingWindow;
38pub use token_bucket::TokenBucket;
39
40use std::future::Future;
41
42use crate::decision::Decision;
43use crate::error::Result;
44use crate::quota::Quota;
45use crate::storage::Storage;
46
47/// Rate limiting algorithm trait.
48///
49/// Each algorithm provides different trade-offs between accuracy, memory usage,
50/// and burst handling. All implementations must be thread-safe.
51///
52/// # Algorithm Comparison
53///
54/// | Algorithm | Accuracy | Memory | Burst | Best For |
55/// |-----------|----------|--------|-------|----------|
56/// | GCRA | Highest | Low (1 timestamp) | Controlled | Precise rate control |
57/// | Token Bucket | High | Low | Excellent | Bursty traffic |
58/// | Leaky Bucket | High | Medium | None | Smooth output |
59/// | Sliding Log | Highest | High | Good | Precision critical |
60/// | Sliding Window | Medium | Low | Good | General purpose |
61/// | Fixed Window | Low | Low | Poor | Simple use cases |
62/// | Concurrent | N/A | Low | N/A | Limit parallelism |
63pub trait Algorithm: Send + Sync + 'static {
64    /// Get the algorithm name (for logging/metrics).
65    fn name(&self) -> &'static str;
66
67    /// Check if a request is allowed AND record it atomically.
68    ///
69    /// This is the primary method for rate limiting. It checks whether the
70    /// request should be allowed and, if so, records it against the quota.
71    fn check_and_record<S: Storage>(
72        &self,
73        storage: &S,
74        key: &str,
75        quota: &Quota,
76    ) -> impl Future<Output = Result<Decision>> + Send;
77
78    /// Check without recording (peek at current state).
79    ///
80    /// Useful for displaying rate limit info without consuming quota.
81    fn check<S: Storage>(
82        &self,
83        storage: &S,
84        key: &str,
85        quota: &Quota,
86    ) -> impl Future<Output = Result<Decision>> + Send;
87
88    /// Reset the rate limit for a key.
89    fn reset<S: Storage>(&self, storage: &S, key: &str) -> impl Future<Output = Result<()>> + Send {
90        async move { storage.delete(key).await }
91    }
92}
93
94/// Get the current timestamp in milliseconds since Unix epoch.
95pub(crate) fn current_timestamp_ms() -> u64 {
96    use std::time::{SystemTime, UNIX_EPOCH};
97    SystemTime::now()
98        .duration_since(UNIX_EPOCH)
99        .expect("Time went backwards")
100        .as_millis() as u64
101}
102
103/// Convert a timestamp to an Instant (approximate).
104pub(crate) fn timestamp_to_instant(timestamp_ms: u64) -> std::time::Instant {
105    let now = std::time::Instant::now();
106    let now_ms = current_timestamp_ms();
107
108    if timestamp_ms >= now_ms {
109        now + std::time::Duration::from_millis(timestamp_ms - now_ms)
110    } else {
111        now - std::time::Duration::from_millis(now_ms - timestamp_ms)
112    }
113}