# `Macaw.Plug.RateLimit`
[🔗](https://github.com/dcoai/macaw_plug/blob/v0.1.4/lib/macaw/plug/rate_limit.ex#L1)

Rate limiting by a generic cell rate algorithm (GCRA), in ETS.

Each key has one *theoretical arrival time* (TAT), advanced by one emission
interval per request with compare-and-swap, so concurrent requests on one
key can never both take the last slot. A limiter allows `count` requests
per `period` on average, and bursts of up to `burst`.

Start the table once, in the host's supervision tree:

    children = [Macaw.Plug.RateLimit.Table, ...]

Then limit with the plug, keyed by the client's address (after
`Macaw.Plug.ClientIp`) unless told otherwise:

    plug Macaw.Plug.RateLimit, id: :api, rate: {50, :second}, burst: 200

or call `check/3` directly. Over the limit, the plug answers `429` with a
`Retry-After` header and halts; `:on_limit`, a `{module, function}` taking
the conn and the seconds to wait, answers instead (a JSON API's error
shape, say).

Limiters share one table, each key stored as `{id, key}`. Keys idle long
enough to have a full burst again are the same as absent, and the table's
sweep removes them, so memory follows the active keys, not every key ever
seen.

# `t`

```elixir
@type t() :: %Macaw.Plug.RateLimit{
  id: term(),
  interval: pos_integer(),
  tolerance: non_neg_integer()
}
```

# `check`

```elixir
@spec check(t(), term(), integer()) :: :ok | {:error, pos_integer()}
```

Takes a slot for `key` at `now` (monotonic microseconds, default the
clock): `:ok`, or `{:error, seconds_to_wait}`, at least 1.

# `new`

```elixir
@spec new(term(), keyword()) :: t()
```

A limiter: `id` names it, `rate: {count, :second | :minute | :hour}` is its
average, and `burst` (default `count`) the most requests it takes at once.
Raises on a rate it can't honor, naming it.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
