Skip to main content

load_balancer/
rate_limit.rs

1use dashmap::DashMap;
2use dashmap::Entry;
3use std::hash::Hash;
4use std::sync::Arc;
5use std::time::{Duration, Instant};
6use tokio::sync::Mutex;
7use tokio::task::yield_now;
8use tokio::time::sleep;
9
10/// A fixed-window rate limiter.
11///
12/// Allows up to `rate` operations per `interval` window.  Counts reset
13/// at window boundaries; requests that exceed the limit are denied until
14/// the next window.
15#[derive(Clone)]
16pub struct RateLimiter {
17    rate: u64,
18    interval: Duration,
19    state: Arc<Mutex<Inner>>,
20}
21
22struct Inner {
23    count: u64,
24    last_reset: Instant,
25}
26
27impl RateLimiter {
28    /// Create a new rate limiter allowing `rate` operations per second.
29    pub fn new_rate(rate: u64) -> Self {
30        Self::new_rate_interval(rate, Duration::from_secs(1))
31    }
32
33    /// Create a new rate limiter with a custom rate and interval.
34    pub fn new_rate_interval(rate: u64, interval: Duration) -> Self {
35        RateLimiter {
36            rate,
37            interval,
38            state: Arc::new(Mutex::new(Inner {
39                count: 0,
40                last_reset: Instant::now(),
41            })),
42        }
43    }
44
45    /// Check whether an operation is allowed right now.
46    ///
47    /// Returns `true` if within the rate limit, `false` if the bucket is exhausted.
48    pub async fn check(&self) -> bool {
49        let mut state = self.state.lock().await;
50        let now = Instant::now();
51
52        if now >= state.last_reset + self.interval {
53            state.count = 0;
54            state.last_reset = now;
55        }
56
57        if state.count < self.rate {
58            state.count += 1;
59            true
60        } else {
61            false
62        }
63    }
64
65    /// Returns the duration until the next token becomes available.
66    ///
67    /// Returns `Duration::ZERO` if tokens are immediately available,
68    /// `Duration::MAX` if the rate is zero.
69    pub async fn time_until_available(&self) -> Duration {
70        if self.rate == 0 {
71            return Duration::MAX;
72        }
73
74        let state = self.state.lock().await;
75        let now = Instant::now();
76        let window_end = state.last_reset + self.interval;
77
78        if now >= window_end || state.count < self.rate {
79            Duration::ZERO
80        } else {
81            window_end - now
82        }
83    }
84
85    /// Block until a token is available, then consume it.
86    ///
87    /// Sleeps for the remaining window duration when the bucket is exhausted,
88    /// then retries. Returns immediately if tokens are available.
89    pub async fn wait(&self) {
90        loop {
91            let mut state = self.state.lock().await;
92            let now = Instant::now();
93
94            if now >= state.last_reset + self.interval {
95                state.count = 0;
96                state.last_reset = now;
97            }
98
99            if state.count < self.rate {
100                state.count += 1;
101                return;
102            }
103
104            let wait = (state.last_reset + self.interval).saturating_duration_since(now);
105
106            drop(state);
107
108            if wait > Duration::ZERO {
109                sleep(wait).await;
110            } else {
111                yield_now().await;
112            }
113        }
114    }
115}
116
117/// A per-key rate limiter map.
118///
119/// Keys must be explicitly inserted via [`insert_rate`](Self::insert_rate) before use;
120/// unknown keys are always rejected.
121#[derive(Clone)]
122pub struct RateLimiterMap<K> {
123    map: Arc<DashMap<K, RateLimiter>>,
124}
125
126impl<K> RateLimiterMap<K>
127where
128    K: Hash + Eq + Clone + Send + Sync + 'static,
129{
130    /// Create an empty map.
131    pub fn new() -> Self {
132        RateLimiterMap {
133            map: Arc::new(DashMap::new()),
134        }
135    }
136
137    /// Insert a rate limiter for `key` with the given `rate` and a 1-second interval.
138    ///
139    /// Overwrites any existing limiter for the same key.
140    pub fn insert_rate(&self, key: K, rate: u64) {
141        self.insert_rate_interval(key, rate, Duration::from_secs(1))
142    }
143
144    /// Insert a rate limiter for `key` with the given `rate` and `interval`.
145    pub fn insert_rate_interval(&self, key: K, rate: u64, interval: Duration) {
146        let limiter = RateLimiter::new_rate_interval(rate, interval);
147        self.map.insert(key, limiter);
148    }
149
150    /// Remove the rate limiter for `key`.
151    ///
152    /// After removal the key is unknown and will be rejected by
153    /// [`check`](Self::check) (same as a key that was never inserted).
154    pub fn remove(&self, key: &K) {
155        self.map.remove(key);
156    }
157
158    /// Returns the number of keys currently managed.
159    pub fn len(&self) -> usize {
160        self.map.len()
161    }
162
163    /// Returns `true` if no keys are being rate-limited.
164    pub fn is_empty(&self) -> bool {
165        self.map.is_empty()
166    }
167
168    /// Returns `true` if `key` has an active rate limiter.
169    pub fn contains_key(&self, key: &K) -> bool {
170        self.map.contains_key(key)
171    }
172
173    /// Remove all rate limiters.
174    pub fn clear(&self) {
175        self.map.clear();
176    }
177
178    /// Retain only the keys for which `f` returns `true`.
179    ///
180    /// Each remaining key's limiter is unchanged.
181    pub fn retain(&self, f: impl FnMut(&K, &mut RateLimiter) -> bool) {
182        self.map.retain(f);
183    }
184
185    /// Get an [`Entry`] for `key`, allowing in-place insertion or modification.
186    ///
187    /// Use `entry(key).or_insert(limiter)` to insert a limiter only if the key
188    /// is absent, or `entry(key).and_modify(...)` to adjust an existing one.
189    pub fn entry(&self, key: K) -> Entry<'_, K, RateLimiter> {
190        self.map.entry(key)
191    }
192
193    /// Check whether `key` is allowed right now.
194    ///
195    /// Returns `true` if the key's limiter has available tokens.
196    /// Returns `false` if the key has not been inserted (unknown keys are
197    /// rejected by default).
198    pub async fn check(&self, key: &K) -> bool {
199        match self.map.get(key).map(|r| r.clone()) {
200            Some(limiter) => limiter.check().await,
201            None => false,
202        }
203    }
204
205    /// Check `key` and distinguish "unknown" from "rate-limited".
206    ///
207    /// Returns `Some(true)` if allowed, `Some(false)` if rate-limited,
208    /// `None` if the key has not been inserted.
209    pub async fn check_opt(&self, key: &K) -> Option<bool> {
210        match self.map.get(key).map(|r| r.clone()) {
211            Some(limiter) => Some(limiter.check().await),
212            None => None,
213        }
214    }
215
216    /// Returns the duration until `key` becomes available again.
217    ///
218    /// Returns `Duration::MAX` if the key is not inserted or the rate is zero,
219    /// `Duration::ZERO` if tokens are immediately available.
220    pub async fn time_until_available(&self, key: &K) -> Duration {
221        match self.map.get(key).map(|r| r.clone()) {
222            Some(limiter) => limiter.time_until_available().await,
223            None => Duration::MAX,
224        }
225    }
226
227    /// Block until `key` is allowed, then consume a token.
228    ///
229    /// Returns immediately if the key is not inserted (no-op).
230    pub async fn wait(&self, key: &K) {
231        if let Some(limiter) = self.map.get(key).map(|r| r.clone()) {
232            limiter.wait().await;
233        }
234    }
235}