A rate limiter implementation using Erlang's :atomics module for atomic counters.
This provides fast, atomic counter operations without the overhead of ETS or process messaging. Requires Erlang/OTP 21.2 or later.
defmodule MyApp.RateLimit do
use Hammer, backend: :atomic
end
MyApp.RateLimit.start_link(clean_period: :timer.minutes(1))
# Allow 10 requests per second
MyApp.RateLimit.hit("user_123", 1000, 10)Runtime configuration:
:clean_period- (in milliseconds) period to clean up expired entries, defaults to 1 minute:key_older_than- (in milliseconds) maximum age for entries before they are cleaned up, defaults to 24 hours:algorithm- the rate limiting algorithm to use, one of::fix_window,:leaky_bucket,:token_bucket. Defaults to:fix_window:before_clean- optional callback invoked with expired entries before they are deleted. Accepts a function(algorithm :: atom(), entries :: [map()]) -> any()or an MFA tuple{module, function, extra_args}. Each entry is a map with:key,:value, and:expired_at(ms). If the callback raises, entries are still deleted and a warning is logged.MyApp.RateLimit.start_link(
clean_period: :timer.minutes(1), key_older_than: :timer.hours(24), before_clean: fn algorithm, entries -> Enum.each(entries, fn entry -> MyApp.Telemetry.emit_expired(algorithm, entry) end) end)
The atomic backend supports the following algorithms:
:fix_window- Fixed window rate limiting (default) Simple counting within fixed time windows. See Hammer.Atomic.FixWindow for more details.:leaky_bucket- Leaky bucket rate limiting Smooth rate limiting with a fixed rate of tokens. See Hammer.Atomic.LeakyBucket for more details.:token_bucket- Token bucket rate limiting Flexible rate limiting with bursting capability. See Hammer.Atomic.TokenBucket for more details.
Summary
Functions
Returns a specification to start this module under a supervisor.
Returns the current time in milliseconds.
Starts the atomic rate limiter process.
Types
@type config() :: %{ table: atom(), table_opts: list(), clean_period: pos_integer(), key_older_than: pos_integer(), algorithm_module: module(), before_clean: Hammer.CleanUtils.before_clean() | nil }
@type start_option() :: {:clean_period, pos_integer()} | {:key_older_than, pos_integer()} | {:before_clean, Hammer.CleanUtils.before_clean()} | GenServer.option()
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec now() :: pos_integer()
Returns the current time in milliseconds.
@spec start_link([start_option()]) :: GenServer.on_start()
Starts the atomic rate limiter process.
Options:
:clean_period- How often to run cleanup (ms). Default 1 minute.:key_older_than- Max age for entries (ms). Default 24 hours.:before_clean- Optional callback invoked with expired entries before deletion. Accepts a function(algorithm :: atom(), entries :: [map()]) -> any()or an MFA tuple{module, function, extra_args}. Each entry is a map with:key,:value, and:expired_at(ms). If the callback raises, entries are still deleted and a warning is logged.