Hi everyone! I'd like to share rateguard, a crate for enforcing one global rate limit across a fleet of service instances, without a shared datastore.

use rateguard::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) // 1000 rps per key, across every instance .spawn()?; if guard.check("api:tenant-42").is_allowed() { serve(request) } else { too_many_requests() } 

Why

The usual way to share a limit between instances is a counter in Redis, which means a network round trip on every request and a datastore to run. rateguard splits the problem in two:

  • Enforcement is local: `check()` is a plain synchronous function (~25 ns, no I/O, no `.await`) running GCRA over this node's share of the limit.
  • Allocation is distributed: nodes gossip observed demand over UDP (SWIM for membership) and each takes a share proportional to its traffic, so an uneven load balancer doesn't leave one node rejecting while others idle.

If gossip stops entirely, every node keeps enforcing with the last share it knew.

Highlights

  • Only "hot" keys are coordinated; cold keys cost nothing on the network
  • Gossip bandwidth is independent of traffic: ~2 datagrams per node per 200 ms
  • Bounded memory, set by configuration
  • Explicit network-partition policies (`Optimistic`, `HoldDown`, `Quorum`) with documented overshoot bounds
  • Limit classes (tariffs) that can be changed live, dry-run mode, `stats()` and `metrics`
  • `tower` layer for axum, plus a sidecar with Python/Go/TypeScript clients
  • No `unsafe`; the protocol core is sans-I/O and tested in a deterministic simulator on virtual time (partitions, packet loss, GC pauses)

When not to use it

If a single extra admitted request costs real money (billing, prepaid quotas), use a centralized counter. If you run one instance, `governor` is simpler.

Feedback welcome

I'd especially appreciate failure scenarios the design doesn't survive, and review from anyone who has operated a gossip protocol in production.

1 post - 1 participant

Read full topic