# PlugLimit
[![Hex Version](https://img.shields.io/hexpm/v/plug_limit)](https://hex.pm/packages/plug_limit)
[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen)](https://hexdocs.pm/plug_limit)
Rate limiting Plug module based on Redis Lua scripting.
## Summary
PlugLimit is using Redis Lua scripting to provide rate-limiting functionality for web applications
based on a Plug library. PlugLimit has a modular architecture: users can use their own Redis Lua
scripts implementing custom rate limiting algorithms.
Salient Redis Lua scripting feature is a race conditions resiliency which makes this library
a recommended solution for distributed systems (e.g.: Phoenix servers behind round robin load balancer).
PlugLimit provides two built-in rate limiters: `PlugLimit.FixedWindow` and `PlugLimit.TokenBucket`.
## Installation
Add `PlugLimit` library to your application dependencies:
```elixir
def deps do
[
{:plug_limit, "~> 0.1.0"}
]
end
```
## Usage
PlugLimit is Redis client agnostic. In a first step you must define Elixir function executing Redis
commands, depending on Redis client of your choice:
```elixir
# config/config.exs
config :plug_limit,
enabled?: true,
cmd: {MyApp.Redis, :command, []}
```
`MyApp.Redis.command/2` function must accept Redis command as a first argument and static MFA tuple
`arg` as a second.
In most cases `:cmd` function will be a `Redix.command/3` or `:eredis.q/2,3` wrapper.
Example naive [Redix](https://hex.pm/packages/redix) driver wrapper:
```elixir
#lib/my_app/redis.ex
def command(command, opts \\ [timeout: 500]) do
{:ok, pid} = Redix.start_link()
Redix.command(pid, command, opts)
:ok = Redix.stop(pid)
end
```
PlugLimit is tested with both [Redix](https://hex.pm/packages/redix) and
[eredis](https://hex.pm/packages/eredis) Redis clients.
Phoenix Framework endpoint can be protected with fixed window rate-limiter by placing a
`PlugLimit.FixedWindow` plug call in the request processing pipeline:
```elixir
#lib/my_app_web/router.ex
pipeline :high_cost_pipeline do
plug(PlugLimit.FixedWindow,
limit: 10,
ttl: 60,
key: {MyApp.RateLimiter, :user_key, [:high_cost_pipeline]}
)
# remaining pipeline plugs...
end
```
Rate limits for `:high_cost_pipeline` pipeline will be evaluated with Redis Lua script fixed window
algorithm. Client identified with `:key` will be allowed to issue 10 requests in 60 seconds time
window.
MFA tuple defined with `:key` option specifies user function which should provide Redis key
used to uniquely identify given rate-limiter bucket. Redis rate-limiter key name should be derived
from `Plug.Conn.t()` connection struct parameters.
Example function to create Redis key name for rate-limiter throttling requests for a given user
identified by a connection assigned `:user_id`:
```elixir
#lib/my_app/rate_limiter.ex
def user_key(%Plug.Conn{assigns: %{user_id: user_id}}, prefix),
do: {:ok, ["#{prefix}:#{user_id}"]}
```
Please refer to `PlugLimit` module documentation for detailed library configuration description and
"Redis Lua script rate limiters" in LIMITERS.md file for Redis Lua scripts implementation
guidelines.
## TODO
- [ ] Add leaky bucket rate limiting algorithm implementation.