.@ Tony Finch – blog


Yesterday I read an article describing the GCRA rate limiting algorithm. I thought it was really interesting, but I wasn’t entirely satisfied with Brandur’s explanation, and the Wikipedia articles on leaky buckets and GCRA are terrible, so here’s my version.

what is GCRA?

GCRA is the “generic cell rate algorithm”, a rate-limiting algorithm that came from ATM. GCRA does the same job as the better-known leaky bucket algorithm, but using half the storage and with much less code.

GCRA only stores one time stamp per sender, whereas leaky buckets need to store a time stamp and a quota.

GCRA needs less arithmetic, and makes it trivial to tell a client how long it should wait before the next request is permitted.

rate limit parameters

Both algorithms have the same configuration parameters:

Each operation has a cost, e.g. the size of a packet if this is a network rate limiter, in which case the cost is measured in bytes and the rate is bytes per second. In simple cases we might fix the cost at 1 unit of work per request, so the rate is measured in requests per second.

dodgy metaphors

The leaky bucket algorithm is named after a questionable analogy.

Imagine you have a bucket that leaks at some rate.

Each request is represented as a jug of water; the amount of water is the cost. A request is permitted if you can pour the jug into the bucket without overflowing.

In a steady state you can pour water into the bucket at the same rate that it leaks out.

The capacity of the bucket determines the burst size: how much water you can chuck in quickly after waiting for the bucket to drain.

On its own terms this metaphor doesn’t make much sense. If the bucket is leaking, why is it bad for it to overflow? But I suppose it’s fairly closely analogous to a write buffer attached to a fixed-rate transmission line.

A better metaphor might be a battery that recharges at a fixed rate; the battery’s capacity determines the burst size, and the cost of of an operation says how much it drains the battery.

leaky bucket algorithm

eliminate the bucket

GCRA’s key trick is to change the units so that the client’s quota is measured in units of time. Calculating bucket leakage or battery recharge becomes trivial: it is just time passing.

Re-think the bucket capacity as a sliding window around the present time during which requests are allowed. The size of the window is burst / rate seconds.

A request nominally takes cost time, which is 1 / rate for fixed-cost requests.

GCRA stores the earliest permitted time of a request, which (with the change of units) is the present time minus the quota. Thus the two leaky bucket values get replaced with one.

A client is only allowed to make a request late (after the earliest permitted time) so the sliding window covers the recent past. We don’t let the window reach into the future to protect against clock resets.

When a client is bursting, its earliest permitted time increases faster than real time and will catch up with the present time.

When a client goes over quota, its earliest permitted time is in the future, so its requests get denied.

GCRA algorithm

discussion

Leaky bucket is a pretty simple algorithm, but GCRA is even simpler. It requires almost no calculation – a big advantage for something intended for implementation in hardware in the 1990s!

I have not tried to match my variable names to the standard GCRA terminology. To be honest, I find it too confusing and I would probably get it wrong. (Why do they have so many different Ts?!)

an old mystery solved

When I was working on rate limiting for Exim, I used an exponentially weighted moving average to calculate a client’s smoothed sending rate. That algorithm stores an update time and a measured rate for each client – the same amount of storage as traditional leaky bucket.

The nice thing about the moving average was that it allowed more flexible policy enforcement than leaky bucket, because the rate measurement was meaningful even when it didn’t apply back-pressure. For example, a dry run mode, or diverting messages to a quarantine.

But it was strange that moving averages packed more information into the same space as leaky buckets. Back then I didn’t really understand how the space was being wasted. Now I know!