# `Hammer.Atomic`
[🔗](https://github.com/ExHammer/hammer/blob/v7.5.0/lib/hammer/atomic.ex#L1)

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`, `:fix_window_per_key`, `: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](Hammer.Atomic.FixWindow.html) for more details.

- `:fix_window_per_key` - Per-key fixed window rate limiting
  Like `:fix_window`, but each key's window is anchored to its first hit rather than a
  globally-aligned wall-clock boundary. See [Hammer.Atomic.FixWindowPerKey](Hammer.Atomic.FixWindowPerKey.html) for more details.

- `:leaky_bucket` - Leaky bucket rate limiting
  Smooth rate limiting with a fixed rate of tokens. See [Hammer.Atomic.LeakyBucket](Hammer.Atomic.LeakyBucket.html) for more details.

- `:token_bucket` - Token bucket rate limiting
  Flexible rate limiting with bursting capability. See [Hammer.Atomic.TokenBucket](Hammer.Atomic.TokenBucket.html) for more details.

# `config`

```elixir
@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`

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

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `now`

```elixir
@spec now() :: pos_integer()
```

Returns the current time in milliseconds.

# `start_link`

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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
