Current section
Files
Jump to
Current section
Files
lib/hammer.ex
defmodule Hammer do
@moduledoc """
Hammer is a rate-limiting library for Elixir.
It provides a simple way for creating rate limiters, and comes with a built-in ETS backend.
defmodule MyApp.RateLimit do
use Hammer, backend: :ets
end
# Start the rate limiter, in case of ETS it will create the ETS table and schedule cleanups
MyApp.RateLimit.start_link(clean_period: :timer.minutes(10))
# Check the rate limit allowing 10 requests per second
MyApp.RateLimit.hit("some-key", _scale = :timer.seconds(1), _limit = 10)
"""
@type key :: term
@type scale :: pos_integer
@type limit :: pos_integer
@type count :: pos_integer
@type increment :: non_neg_integer
@doc """
Checks if a key is allowed to perform an action, and increment the counter.
Same as `hit/4` with `increment` set to 1.
**Note on key types**: While this library accepts any term as a key, some
backends may have restrictions. The Redis backend requires keys to be strings
or binary types, as it needs to serialize them for storage. The ETS and Atomic
backends support any term type.
"""
@callback hit(key, scale, limit) :: {:allow, count} | {:deny, timeout}
@doc """
Optional callback to check if a key is allowed to perform an action, and increment the counter.
Returns `{:allow, count}` if the action is allowed, or `{:deny, timeout}` if the action is denied.
This is the only required callback.
"""
@callback hit(key, scale, limit, increment) :: {:allow, count} | {:deny, timeout}
@doc """
Same as `inc/3` with `increment` set to 1.
"""
@callback inc(key, scale) :: count
@doc """
Optional callback for incrementing a counter value for a key without performing limit check.
Returns the new counter value.
"""
@callback inc(key, scale, increment) :: count
@doc """
Optional callback for setting the counter value for a key.
Returns the new counter value.
"""
@callback set(key, scale, count) :: count
@doc """
Optional callback for getting the counter value for a key.
Returns the current counter value.
"""
@callback get(key, scale) :: count
@optional_callbacks hit: 4, inc: 2, inc: 3, set: 3, get: 2
@doc """
Use the Hammer library in a module to create a rate limiter.
defmodule MyApp.RateLimit do
use Hammer, backend: :ets
end
"""
defmacro __using__(opts) do
quote bind_quoted: [opts: opts] do
@behaviour Hammer
@hammer_opts opts
backend =
Keyword.get(opts, :backend) ||
raise ArgumentError, """
Hammer requires a backend to be specified. Example:
use Hammer, backend: :ets
"""
# this allows :ets to be aliased to Hammer.ETS
backend =
case backend do
:ets -> Hammer.ETS
:atomic -> Hammer.Atomic
backend -> backend
end
@before_compile backend
end
end
end