Packages

A dead simple distributed rate limiter using Mnesia

Current section

Files

Jump to
minirate lib minirate.ex
Raw

lib/minirate.ex

defmodule Minirate do
@moduledoc """
A simple distributed rate limiter using Mnesia.
## Usage
case Minirate.check_limit("login", "user@example.com", 5) do
{:allow, count} -> :ok
{:block, :limit_exceeded} -> {:error, :rate_limited}
end
## Configuration
config :minirate,
mnesia_table: :rate_limiter,
expiry_ms: 60_000,
cleanup_period_ms: 300_000
"""
use Application
@doc false
def start(_type, _args) do
children = [
%{
id: Minirate.Worker,
start: {Minirate.Worker, :start_link, [mnesia_table(), expiry_ms(), cleanup_period_ms()]}
}
]
Supervisor.start_link(children, strategy: :one_for_one)
end
@doc """
Check if an action is within the rate limit.
Returns `{:allow, count}` if allowed or `{:block, :limit_exceeded}` if exceeded.
"""
def check_limit(action, id, limit) do
Minirate.Counter.check_limit(mnesia_table(), {action, id, limit, now()})
end
@doc """
Check rate limit with custom increment.
"""
def check_limit(action, id, limit, increment) do
Minirate.Counter.check_limit(mnesia_table(), {action, id, limit, now()}, increment)
end
defp expiry_ms, do: get_config(:expiry_ms)
defp mnesia_table, do: get_config(:mnesia_table)
defp cleanup_period_ms, do: get_config(:cleanup_period_ms)
defp get_config(key) do
case Application.get_env(:minirate, key) do
nil ->
Kernel.raise(
ArgumentError,
"the configuration parameter #{Kernel.inspect(key)} is not set"
)
value ->
value
end
end
defp now do
DateTime.utc_now() |> DateTime.to_unix(:millisecond)
end
end