#rate-limiting #rate #limiter #limit

r8limit

Dead simple rate limiter for Rust

5 releases

0.2.2 Sep 8, 2021
0.2.1 Sep 7, 2021
0.2.0 Sep 7, 2021
0.1.1 Sep 6, 2021
0.1.0 Sep 6, 2021

#3 in #ratelimit


Used in prob-rate-limiter

MIT license

11KB
109 lines

r8limit

Crates.io docs.rs

A dead simple Rust library for rate limiting.

Usage

In your Cargo.toml:

[dependencies]
r8limit = "0.2"

In your code:

use std::time::Duration;

fn main() {
    // Allow 3 attempts every 5 seconds
    let mut limiter = r8limit::RateLimiter::new(3, Duration::from_secs(5));
    println!("{}", limiter.attempt()); // true
    println!("{}", limiter.attempt()); // true
    println!("{}", limiter.attempt()); // true
    println!("{}", limiter.attempt()); // false
}

Refill policies

The refill policy is what determines how the number of executions in an interval are refilled.

Full

By default, the refill policy is set to Full. This means that every time the difference between now and the start of the current window has reached or exceeded the specified interval, the number of executions is reset to the maximum number of executions allowed during the interval.

use std::time::Duration;
use std::thread::sleep;
use crate::RateLimiter;

fn main() {
    let mut limiter = RateLimiter::new(3, Duration::from_secs(1));
    limiter.attempt(); // returns true; executions remaining in window: 2
    limiter.attempt(); // returns true; executions remaining in window: 1
    limiter.attempt(); // returns true; executions remaining in window: 0
    limiter.attempt(); // returns false; executions remaining in window: 0
    // Remember that the interval is set to 1s
    sleep(Duration::from_millis(500)); // executions remaining in window: 0
    // As you can see, even though half of the interval has passed, there are still 0 executions available.
    assert_eq!(limiter.attempt(), false); // returns false; executions remaining for window: 0
    // That is what the default refill policy, RefillPolicy::Full, does.
    // We'll sleep for the remainder of the window, which means that the next attempt will reset the window
    sleep(Duration::from_millis(500)); // executions remaining in window: 3
    limiter.attempt(); // returns true; executions remaining in window: 2
    limiter.attempt(); // returns true; executions remaining in window: 1
    limiter.attempt(); // returns true; executions remaining in window: 0
    limiter.attempt(); // returns false; executions remaining in window: 0
}

Gradual

Unlike the Full refill policy, the Gradual refill policy does not wait until the interval has completely elapsed to refill the number of executions remaining.

use std::time::Duration;
use std::thread::sleep;
use crate::{RateLimiter, RefillPolicy};

fn main() {
    let mut limiter = RateLimiter::new(2, Duration::from_millis(500)).with_refill_policy(RefillPolicy::Gradual);
    limiter.attempt(); // returns true; executions remaining in window: 1
    limiter.attempt(); // returns true; executions remaining in window: 0
    limiter.attempt(); // returns false; executions remaining in window: 0
    // The Gradual refill policy calculates the percentage of the interval which has 
    // elapsed, and multiplies that by the number of executions allowed per interval.
    // This means that if we wait for half of the interval, half of the executions will become available again
    sleep(Duration::from_millis(250)); // executions remaining in window: 1
    limiter.attempt(); // returns true; executions remaining in window: 0
    limiter.attempt(); // returns false; executions remaining in window: 0
    sleep(Duration::from_millis(500)); // executions remaining in window: 2
    limiter.attempt(); // returns true; executions remaining in window: 1
    limiter.attempt(); // returns true; executions remaining in window: 0
    limiter.attempt(); // returns false; executions remaining in window: 0
}

No runtime deps