Skip to main content

rate_limiter/
rate_limiter.rs

1//! Rate Limiter Example
2//!
3//! This example demonstrates the RateLimiter policy using a token bucket algorithm.
4//! It shows how burst capacity works and how tokens are replenished over time.
5
6use 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    // Scenario 1: Burst of requests
14    burst_example().await;
15
16    println!("\n{}\n", "─".repeat(60));
17
18    // Scenario 2: Token replenishment
19    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    // Phase 1: Use up all tokens
67    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    // Phase 2: Wait for tokens to replenish
84    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    // Phase 3: Try again after replenishment
90    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}