glimit
A simple, framework-agnostic, in-memory rate limiter for Gleam. π«
Features
- β¨ Simple and easy to use.
- π Rate limits based on any key (e.g. IP address, or user ID).
- πͺ£ Uses a distributed Token Bucket algorithm to rate limit requests.
- ποΈ No back-end service needed; stores rate limit stats in-memory.
Usage
A very minimalistic example of how to use glimit would be the following snippet:
import glimit
let limiter =
glimit.new()
|> glimit.per_second(2)
|> glimit.identifier(fn(x) { x })
|> glimit.on_limit_exceeded(fn(_) { "Stop!" })
let func =
fn(_) { "OK" }
|> glimit.apply(limiter)
func("π") // "OK"
func("π«") // "OK"
func("π«") // "OK"
func("π«") // "Stop!"
func("π") // "OK"
func("π") // "Stop!"
More practical examples can be found in the examples/ directory, such as Wisp and Mist server examples.
Constraints
While the in-memory rate limiter is simple and easy to use, it does have an important constraint: it is scoped to the BEAM VM cluster it runs in. This means that if your application is running across multiple BEAM VM clusters, the rate limiter will not be shared between them.
Performance
Each rate limiter registry is a single OTP actor. Operations like get_or_create, get_all, and remove are serialized through it, so the registry itself is the throughput bottleneck β individual rate limiter actors run concurrently.
- Memory: One actor per unique identifier. Idle identifiers (full token buckets) are automatically swept every 10 seconds and shut down.
- Sweep: Entries are processed in batches of 50 with a 10ms per-call timeout. Between batches, other registry messages (hits, lookups) can interleave, so sweeps donβt block the registry for large numbers of identifiers.
- Fail-open: If a rate limiter actor dies or times out, the request is allowed through rather than rejected.
Documentation
Further documentation can be found at https://hexdocs.pm/glimit/glimit.html.
Contributing
Contributions like PRβs, bug reports or suggestions are more than welcome! β₯οΈ