TweakCode
Worked Systems lessons

Rate Limiter

One counter across many app servers

Before you start

  • Caching

    The read path sits behind cache-aside.

    Open lesson

Why it matters

One edge gateway protects routes with different costs and runs as several processes. Per-process counts multiply every route’s quota, while one hot client can consume gateway CPU and shared-store bandwidth before the real check runs. The gateway must enforce read = 3 and export = 1 across all processes, while shedding an obvious local flood early.

Example

Limit the gateway

One edge gateway sits in front of many services. Some routes allow more traffic than others. Clients are untrusted.

Input
requests = [(0, "u", "read"), (10, "u", "read"), (20, "u", "read"), (30, "u", "read"), (31, "u", "export"), (32, "u", "export"), (65, "u", "read")], limits = {"read": 3, "export": 1}, window = 60
Output
[True, True, True, False, True, False, True]

Approach 2 · Resolve the bottleneck

The idea

Keep one example in view

Limit the gateway

requests = [(0, "u", "read"), (10, "u", "read"), (20, "u", "read"), (30, "u", "read"), (31, "u", "export"), (32, "u", "export"), (65, "u", "read")], limits = {"read": 3, "export": 1}, window = 60→[True, True, True, False, True, False, True]

The baseline, A counter per server, is N × the limit · per server. The better model below keeps this exact input and removes that bottleneck.

A per-process counter lets u multiply each route’s allowance across gateways. Apply a loose local prefilter, then atomically increment (u, route, minute): read 4 is denied, export 2 is denied, and the read at 65 starts a new window.

A route table picks the limit, then every gateway atomically increments the same (user, route, minute) key. For u, read requests at 0, 10, and 20 seconds pass; the read at 30 is denied at count 4. Export has its own key and limit 1, so 31 passes and 32 fails.

A loose in-process prefilter can reject an obvious hot client before spending a remote round trip, but it never grants the service-wide quota. At 65 seconds the read key starts a new minute. Expire old keys, and define fail-open or fail-closed behavior for store outages.

Visual anchor

Limit the gateway

INCR u:read:0Gateway 1Gateway 2Gateway 3Shared counteru:read:0 = 4
Gateway 1 sees 4 read requests against a route limit of 3. Denied, even though traffic crossed processes.

Pro

The rest of this is on Pro

The idea above is free. The walkthrough, the cost, and the practice are on Pro.