quota 0.1.3

High-performance Rate-limiter
Documentation

quota

A high-performance in-memory rate limiter for Rust, completely lock-free.

It uses a mix of Leaky Token Bucket & GCRA.

Scales with a compact 16 bytes to 29 bytes of memory per quota

  • Quota size: 8 bytes (*)
  • Last Access Time: 8 bytes (*)
  • Last Refill Time: 8 bytes (Enabled by default for an efficient per-quota clock)
  • Generation & Occupied: 5 bytes (Enabled by default for preventing dangling references from PoolQuota::quota())

The library uses SoA architecture, so the numbers above are accurate, no byte-alignment involved, which can make it efficient for SIMD processing in the future.

We provide 3 essential clean primitives: Quota, QuotaPolicy, and finally QuotaPool that combines both of them.

Example use of the simple Quota (A simple 8-byte number in memory):

use quota::Quota;

fn main() {
    let quota = Quota::with_initial_tokens(10);

    let mut results = vec![];
    for _ in 0..100 {
        results.push(quota.consume(1));
    }

    assert_eq!(results.iter().filter(|r| r.is_ok()).count(), 10);
    assert_eq!(results.iter().filter(|r| r.is_err()).count(), 90);
}

Example use of applying QuotaPolicy with a maximum capacity and RefillRate:

use quota::{Quota, QuotaPolicy, RefillRate};

fn main() {
    let policy = QuotaPolicy::new()
        .set_capacity(10.0) // Maximum Capacity to apply to a Quota per tick
        .set_refill_rate(RefillRate::per_micro(100.0)); // Refill Rate to apply to a Quota per tick

    let mut quota = Quota::with_initial_tokens(10);

    let mut results = vec![];
    for _ in 0..100 {
        policy.tick(1, &mut quota);
        results.push(quota.consume(1));
    }

    assert_eq!(results.iter().filter(|r| r.is_ok()).count(), 19);
    assert_eq!(results.iter().filter(|r| r.is_err()).count(), 81);
}

And now the main QuotaPool:

use quota::{RefillRate, QuotaPolicy, QuotaPool};

fn main() {
    let policy = QuotaPolicy::new()
        .set_capacity(10.0)
        .set_refill_rate(RefillRate::per_sec(3)); // TODO: Add a custom time tick in refill rate rather than ticking all the time

    let pool = QuotaPool::new(policy, 10);

    let mut results = vec![];
    for _ in 0..100 {
        results.push(pool.consume("testing", 1));
    }

    assert_eq!(results.iter().filter(|r| r.is_ok()).count(), 10);
    assert_eq!(results.iter().filter(|r| r.is_err()).count(), 90);
}