Token bucket vs windows
Three throttles with the same long-run rate see the same bursty client. The fixed window lets nearly twice its limit through across a window edge, the bucketed sliding window stays within a few percent of it, and the token bucket never exceeds b + r·T. Switch to naive refill to watch rounding starve the bucket.
| Limiter | Accepted | Rejected | Max in any 1 s | Max in any 100 ms |
|---|
All three limiters run on time in integer nanoseconds and allow the same long-run rate: the windows allow N = r × 1 s per window. The token bucket starts full and refills continuously; over any interval T it passes at most b + r·T. With exact refill credit is kept in scaled units (1 token = 10⁹ units), so elapsed_ns × r is added exactly and no fraction is ever lost. Naive refill computes elapsed_ns × r / 10⁹ in integers and sets last = now, so every call closer than 1/r to the previous one earns zero. The fixed window resets its counter at each second. The sliding window is the drill's bucketed version (K = 10 buckets of 100 ms); it holds N in any interval shorter than 900 ms. Rejected messages are never counted. Bursty client: a seeded Poisson background (45/s) plus three 70 ms bursts, one straddling the edge at 2 s. Edge attack: an 8/s background plus N messages in the 50 ms before the edge at 2 s and N more in the 50 ms after it.