Skip to main content

RateLimiter

Struct RateLimiter 

Source
pub struct RateLimiter { /* private fields */ }
Expand description

A token bucket rate limiter.

The rate limiter maintains a bucket of tokens. Each operation consumes one token. When tokens are depleted, operations are rejected until the bucket refills.

§Examples

use do_over::{policy::Policy, rate_limit::RateLimiter, error::DoOverError};
use std::time::Duration;

// 10 requests per second
let limiter = RateLimiter::new(10, Duration::from_secs(1));

// 1000 requests per minute
let limiter = RateLimiter::new(1000, Duration::from_secs(60));

Implementations§

Source§

impl RateLimiter

Source

pub fn new(capacity: u64, interval: Duration) -> Self

Create a new rate limiter.

§Arguments
  • capacity - Number of tokens (requests) allowed per interval
  • interval - Duration after which tokens refill
§Examples
use do_over::rate_limit::RateLimiter;
use std::time::Duration;

// Allow 100 requests per second
let limiter = RateLimiter::new(100, Duration::from_secs(1));

// Allow 5 requests per 100ms (burst limiting)
let limiter = RateLimiter::new(5, Duration::from_millis(100));
Examples found in repository?
examples/comprehensive.rs (line 55)
53fn create_inventory_policy() -> InventoryPolicy {
54    Wrap::new(
55        RateLimiter::new(3, Duration::from_secs(1)), // 3 requests per second
56        Wrap::new(
57            RetryPolicy::fixed(2, Duration::from_millis(100)),
58            TimeoutPolicy::new(Duration::from_millis(500)),
59        ),
60    )
61}
More examples
Hide additional examples
examples/rate_limiter.rs (line 27)
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}

Trait Implementations§

Source§

impl Clone for RateLimiter

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<E> Policy<DoOverError<E>> for RateLimiter
where E: Send + Sync,

Source§

fn execute<'life0, 'async_trait, F, Fut, T>( &'life0 self, f: F, ) -> Pin<Box<dyn Future<Output = Result<T, DoOverError<E>>> + Send + 'async_trait>>
where F: Fn() -> Fut + Send + Sync + 'async_trait, Fut: Future<Output = Result<T, DoOverError<E>>> + Send + 'async_trait, T: Send + 'async_trait, Self: 'async_trait, 'life0: 'async_trait,

Execute an async operation with this policy’s resilience behavior. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.