# Limitex
A pure Elixir distributed rate limiter based on the
[Token Bucket](https://en.wikipedia.org/wiki/Token_bucket) algorithm.
**Limitex** uses sharded ETS tables with write and read concurrency enabled, node
clustering is not handled, something like **libcluster** is recomended.
## Installation
You can add this package to your project dependencies:
```elixir
# mix.exs
def deps do
[
{:limitex, git: "https://github.com/pggalaviz/limitex.git"}
]
end
```
## Usage
Limitex exports a single function: `check_rate/3` and `check_rate/4`.
* `check_rate(id, bucket_time, limit)`
* `check_rate(id, bucket_time, limit, increment)`
which return an `{:ok, count}` or `{:error, :rate_limited}` tuples.
#### Parameters
* `id` is a string to identify who's requesting the action, it can be
composed of a unique part such as an IP address or a user ID, plus some action
identifier (see example below).
* `bucket_time` is the amount of time you want to rate limit the action for the ID, must
be given in milliseconds.
* `limit` is an integer which determines how many times we're allowed to perform
the action in the given time.
* `increment` each time you call the function, the count to reach the limit is
increased by 1 as a default, you can alternatively pass an arbitrary integer
to increase the limit.
#### Examples
Inside your app you can call it inside any function:
```elixir
defmodule MyApp.Upload do
def upload(video_data, user_id) do
case Limitex.check_rate("upload:#{user_id}", 60_000, 5) do
{:ok, _count} ->
# upload the video, somehow
{:error, :rate_limited} ->
# deny the request
end
end
end
```
So in the above example we'll limit user's uploads to 5 every 60 seconds.
#### Configuration
**Limitex** will perform scheduled cleanups to remove expired buckets, to handle
this, we should provide a cleanup interval and an expiry:
```elixir
# inside config.exs
config :limitex,
cleanup_interval: 60_000,
expiry: 300_000
```
Table cleanups will be scheduled every minute in this example (defaults to 5
minutes), and will delete buckets where expiry has already passed, it's
important to give a value greater than the biggest bucket we create (the
bucket_time param in `check_rate` function).
So if we're creating a bucket of 1 hour: `Limitex.check_rate("some_id",
3_600_000, 20)` we should give a bigger number in our config to prevent cleanup
to delete buckets which are not yet expired:
```elixir
# inside config.exs
config :limitex,
expiry: 3_800_000
```
`expiry` defaults to 15 minutes.
#### Benchmarks
Basic benchmarks can be run with `mix bench`.