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}