rate_limiter/
rate_limiter.rs1use do_over::{error::DoOverError, policy::Policy, rate_limit::RateLimiter};
7use std::time::{Duration, Instant};
8
9#[tokio::main]
10async fn main() {
11 println!("=== Do-Over Rate Limiter Example ===\n");
12
13 burst_example().await;
15
16 println!("\n{}\n", "─".repeat(60));
17
18 replenishment_example().await;
20}
21
22async fn burst_example() {
23 println!("📌 Scenario 1: Burst of Requests");
24 println!(" Configuration: capacity=5, interval=1s");
25 println!(" Sending 8 requests in rapid succession...\n");
26
27 let limiter = RateLimiter::new(5, Duration::from_secs(1));
28 let start = Instant::now();
29
30 let mut allowed = 0;
31 let mut rejected = 0;
32
33 for i in 1..=8 {
34 let result: Result<String, DoOverError<String>> = limiter
35 .execute(|| async move { Ok(format!("Request {} processed", i)) })
36 .await;
37
38 let elapsed = start.elapsed().as_millis();
39 match result {
40 Ok(msg) => {
41 println!(" [+{:>4}ms] Request {}: ✅ {}", elapsed, i, msg);
42 allowed += 1;
43 }
44 Err(DoOverError::BulkheadFull) => {
45 println!(" [+{:>4}ms] Request {}: 🚫 Rate limited", elapsed, i);
46 rejected += 1;
47 }
48 Err(e) => println!(" [+{:>4}ms] Request {}: Error - {:?}", elapsed, i, e),
49 }
50 }
51
52 println!("\n Summary:");
53 println!(" - Allowed: {} (capacity was 5)", allowed);
54 println!(" - Rejected: {} (exceeded capacity)", rejected);
55 println!("\n 💡 First 5 requests used the burst capacity, remaining 3 were rate limited");
56}
57
58async fn replenishment_example() {
59 println!("📌 Scenario 2: Token Replenishment");
60 println!(" Configuration: capacity=3, interval=500ms");
61 println!(" Demonstrating token refill after interval...\n");
62
63 let limiter = RateLimiter::new(3, Duration::from_millis(500));
64 let start = Instant::now();
65
66 println!(" Phase 1: Using up all 3 tokens");
68 for i in 1..=4 {
69 let result: Result<String, DoOverError<String>> = limiter
70 .execute(|| async move { Ok(format!("Request {}", i)) })
71 .await;
72
73 let elapsed = start.elapsed().as_millis();
74 match result {
75 Ok(_) => println!(" [+{:>4}ms] Request {}: ✅ Token consumed", elapsed, i),
76 Err(DoOverError::BulkheadFull) => {
77 println!(" [+{:>4}ms] Request {}: 🚫 No tokens available", elapsed, i)
78 }
79 Err(e) => println!(" [+{:>4}ms] Request {}: Error - {:?}", elapsed, i, e),
80 }
81 }
82
83 println!("\n Phase 2: Waiting 500ms for token replenishment...");
85 tokio::time::sleep(Duration::from_millis(500)).await;
86 let elapsed = start.elapsed().as_millis();
87 println!(" [+{:>4}ms] → Tokens should be replenished now", elapsed);
88
89 println!("\n Phase 3: Sending requests after replenishment");
91 for i in 5..=8 {
92 let result: Result<String, DoOverError<String>> = limiter
93 .execute(|| async move { Ok(format!("Request {}", i)) })
94 .await;
95
96 let elapsed = start.elapsed().as_millis();
97 match result {
98 Ok(_) => println!(" [+{:>4}ms] Request {}: ✅ Token consumed", elapsed, i),
99 Err(DoOverError::BulkheadFull) => {
100 println!(" [+{:>4}ms] Request {}: 🚫 No tokens available", elapsed, i)
101 }
102 Err(e) => println!(" [+{:>4}ms] Request {}: Error - {:?}", elapsed, i, e),
103 }
104 }
105
106 println!(
107 "\n 💡 After 500ms, all 3 tokens were replenished and available for use"
108 );
109}