Hammer.Atomic (hammer v7.3.0)

Copy Markdown View Source

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

config()

@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
}

start_option()

@type start_option() ::
  {:clean_period, pos_integer()}
  | {:key_older_than, pos_integer()}
  | {:before_clean, Hammer.CleanUtils.before_clean()}
  | GenServer.option()

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

now()

@spec now() :: pos_integer()

Returns the current time in milliseconds.

start_link(opts)

@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.