Skip to main content

Crate rateguard

Crate rateguard 

Source
Expand description

Distributed rate limiting for Rust services: one global limit across a fleet, no datastore, no network call on the request path.

Every instance of the service runs a Guard. The nodes find each other over UDP and agree, every 200 ms, on how the limit is shared out; each request is then decided locally, with no I/O and no lock shared with the network.

use rateguard::{Decision, Guard};

let guard = Guard::builder()
    .bind("0.0.0.0:7946")
    .advertise("10.0.0.3:7946")
    .seeds(["10.0.0.1:7946", "10.0.0.2:7946"])
    .limit(1_000) // per key, across the whole fleet, per second
    .spawn()?;

match guard.check("api:tenant-42") {
    Decision::Allow => { /* serve */ }
    Decision::Deny { retry_after } => { /* 429, Retry-After */ }
}

The gossip is not encrypted, and by default not authenticated either: run the nodes on a network you trust, or have them sign it with a shared secret, see Builder::gossip_auth.

Structs§

Builder
Configures and starts a node; see Guard::builder.
Checked
A decision and what the key’s shadow limit would have decided: see Guard::check_in_traced.
DeniedKey
A key denied lately; see Stats::most_denied.
Guard
The handle every request goes through: cheap to clone, Send + Sync. The node runs as long as one clone is alive.
KeyStats
What this node knows of one key; see Guard::key_stats.
Limit
A limit for each key of a class: a count of requests per second, minute or hour across the whole fleet, and the burst one instance may admit back to back on top of its share. See Builder::class and Guard::set_class.
RejectedDatagrams
Datagrams refused since the node started; see Stats::rejected_datagrams.
Secret
A secret shared by every node of a cluster, at least 32 bytes. Never shown: its Debug prints Secret(..).
Stats
A node’s state, as of the last tick of its background task: a quarter of the protocol period ago at most, 50 ms by default. See Guard::stats.

Enums§

AuthError
Why secrets or a mode were refused. Never carries secret bytes.
Decision
The answer to a request. Two answers and no more, ever: matching both is safe across versions.
Error
Why a node could not start, or a class could not be set.
GossipAuth
How a node signs what it sends and checks what it receives.
KeyLimit
The limit of one key, set with Guard::set_key_limit: its own numbers, or those of a class.
PartitionPolicy
What a node does when its cluster shrinks: the CAP trade-off as a setting. When the network splits into k groups, each sees only itself.

Functions§

key_hash
The 64-bit hash a key travels and is stored under. xxh3: fixed by its specification, the same on every node and every Rust version. The key table reserves 0 (an empty slot) and u64::MAX (a slot being reset), so a key hashing to either is moved next door.
key_hash_in
The hash of key in the class class: xxh3 with the class’s hash as its seed, so that one key in two classes is two keys. The default class, "", has seed 0, and its hashes are key_hash’s. Public so that the hashes of Stats::most_denied can be matched to keys. Without a salt only: a node given a Builder::key_salt hashes as Guard::key_hash_in does.