envoy/rate_limit/
state.rs1use std::time::Duration;
4
5#[derive(Debug, Clone)]
10pub struct TokenBucket {
11 pub tokens: u64,
12 pub max_tokens: u64,
13 pub replenish_rate: u64,
14 pub last_replenish: std::time::Instant,
15}
16
17impl TokenBucket {
18 pub fn new(max_tokens: u64, replenish_rate: u64) -> Self {
19 Self {
20 tokens: max_tokens,
21 max_tokens,
22 replenish_rate,
23 last_replenish: std::time::Instant::now(),
24 }
25 }
26
27 pub fn replenish(&mut self, elapsed: Duration) {
29 let secs = elapsed.as_secs_f64();
30 let tokens_to_add = (secs * self.replenish_rate as f64) as u64;
31 self.tokens = (self.tokens + tokens_to_add).min(self.max_tokens);
32 self.last_replenish = std::time::Instant::now();
33 }
34
35 pub fn consume(&mut self, amount: u64) {
37 self.tokens = self.tokens.saturating_sub(amount);
38 }
39
40 pub fn try_consume(&mut self, amount: u64) -> Result<(), u64> {
42 if self.tokens >= amount {
43 self.tokens -= amount;
44 Ok(())
45 } else {
46 Err(self.tokens)
47 }
48 }
49}
50
51#[derive(Debug, Clone, PartialEq)]
53pub struct RateLimitDecision {
54 pub allowed: bool,
55 pub retry_after: Option<Duration>,
56}
57
58#[derive(Debug, Clone)]
60pub struct RateLimitState {
61 pub agent_id: String,
62 bucket: TokenBucket,
63}
64
65impl RateLimitState {
66 pub fn new(agent_id: &str, max_tokens: u64, replenish_rate: u64) -> Self {
67 Self {
68 agent_id: agent_id.to_string(),
69 bucket: TokenBucket::new(max_tokens, replenish_rate),
70 }
71 }
72
73 pub(crate) fn from_bucket(agent_id: String, bucket: TokenBucket) -> Self {
75 Self { agent_id, bucket }
76 }
77
78 pub fn check(&mut self, cost: u64) -> RateLimitDecision {
80 if self.bucket.tokens >= cost {
81 self.bucket.consume(cost);
82 RateLimitDecision {
83 allowed: true,
84 retry_after: None,
85 }
86 } else {
87 let deficit = cost - self.bucket.tokens;
88 let retry_after =
89 Duration::from_secs_f64(deficit as f64 / self.bucket.replenish_rate as f64);
90 RateLimitDecision {
91 allowed: false,
92 retry_after: Some(retry_after),
93 }
94 }
95 }
96
97 pub fn replenish(&mut self, elapsed: Duration) {
99 self.bucket.replenish(elapsed);
100 }
101
102 pub fn bucket(&self) -> &TokenBucket {
104 &self.bucket
105 }
106
107 pub fn bucket_mut(&mut self) -> &mut TokenBucket {
109 &mut self.bucket
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn test_token_bucket_initial_state() {
119 let bucket = TokenBucket::new(100, 10);
120 assert_eq!(bucket.tokens, 100);
121 assert_eq!(bucket.max_tokens, 100);
122 assert_eq!(bucket.replenish_rate, 10);
123 }
124}