Crate ratelimit_meter

source ·
Expand description

Leaky Bucket Rate-Limiting (as a meter) in Rust

This crate implements the generic cell rate algorithm (GCRA) for rate-limiting and scheduling in Rust.

Interface

This crate implements two “serious” rate-limiting/traffic-shaping algorithms: GCRA and a Leaky Bucket. An “unserious” implementation is provided also: The Allower, which returns “Yes” to all rate-limiting queries.

The Generic Cell Rate Algorithm can be used by creating a builder from the GCRA struct:

use std::num::NonZeroU32;
use ratelimit_meter::{Decider, GCRA, per_second};

let mut lim = per_second::<GCRA>(NonZeroU32::new(50).unwrap()); // Allow 50 units per second
assert_eq!(Ok(()), lim.check());

The rate-limiter interface is intentionally geared towards only providing callers with the information they need to make decisions about what to do with each cell. Deciders return additional information about why a cell should be denied alongside the decision. This allows callers to e.g. provide better error messages to users.

As a consequence, the ratelimit_meter crate does not provide any facility to wait until a cell would be allowed - if you require this, you should use the NonConformance returned with negative decisions and have the program wait using the method best suited for this, e.g. an event loop.

Rate-limiting Algorithms

Design and implementation of GCRA

The GCRA limits the rate of cells by determining when the “next” cell is expected to arrive; any cells that arrive before that time are classified as non-conforming; the methods for checking cells also return an expected arrival time for these cells, so that callers can choose to wait (adding jitter), or reject the cell.

Since using the GCRA results in a much smoother usage pattern, it appears to be very useful for “outgoing” traffic behaviors, e.g. throttling API call rates, or emails sent to a person in a period of time.

Unlike token or leaky bucket algorithms, the GCRA assumes that all units of work are of the same “weight”, and so allows some optimizations which result in much more concise and fast code (it does not even use multiplication or division in the “hot” path).

See the documentation of the GCRA type for more details on its implementation and on trade-offs that apply to it.

Design and implementation of the leaky bucket

In contrast to the GCRA, the leaky bucket algorithm does not place any constraints on the next cell’s arrival time: Whenever there is capacity left in the bucket, it can be used. This means that the distribution of “yes” decisions from heavy usage on the leaky bucket rate-limiter will be clustered together. On average, the cell rates of both the GCRA and the leaky bucket will be the same, but in terms of observable behavior, the leaky bucket will appear to allow requests at a more predictable rate.

This kind of behavior is usually what people of online APIs expect these days, which makes the leaky bucket a very popular technique for rate-limiting on these kinds of services.

The leaky bucket algorithm implemented in this crate is fairly standard: It only updates the bucket fill gauge when a cell is checked, and supports checking “batches” of cells in a single call with no problems.

Thread-safe operation

The implementations in this crate use compare-and-set to keep state, and are safe to share across threads..

Example:

use std::thread;
use std::num::NonZeroU32;
use std::time::Duration;
use ratelimit_meter::{Decider, GCRA, per_second};

// Allow 50 units/second across all threads:
let mut lim = per_second::<GCRA>(NonZeroU32::new(50).unwrap());
let mut thread_lim = lim.clone();
thread::spawn(move || { assert_eq!(Ok(()), thread_lim.check());});
assert_eq!(Ok(()), lim.check());

Modules

Structs

An object that allows incrementally constructing Decider objects.
Implements the virtual scheduling description of the Generic Cell Rate Algorithm, attributed to ITU-T in recommendation I.371 Traffic control and congestion control in B-ISDN; from Wikipedia.
An error that is returned when initializing a Decider that is too small to let a single cell through.
Implements the industry-standard leaky bucket rate-limiting as-a-meter. The bucket keeps a “fill height”, pretending to drip steadily (which reduces the fill height), and increases the fill height with every cell that is found conforming. If cells would make the bucket overflow, they count as non-conforming.
Provides additional information about non-conforming cells, most importantly the earliest time until the next cell could be considered conforming.

Enums

Gives additional information about the negative outcome of a batch cell decision.

Traits

The main decision trait. It allows checking a single cell against the rate-limiter, either at the current time instant, or at a given instant in time, updating the Decider’s internal state if the cell is conforming.
The “batch” decision trait, allowing a Decider to make a decision about multiple cells at once.

Functions

Return a builder that can be used to construct a Decider using the parameters passed to the Builder.
Construct a new Decider that allows capacity cells per time unit through.
Construct a new Decider that allows capacity cells per second.