Hammer is a rate-limiter for Elixir with pluggable storage backends. Hammer enables users to set limits on actions performed within specified time intervals, applying per-user or global limits on API requests, file uploads, and more.
Note
This README is for the unreleased master branch, please reference the official documentation on hexdocs for the latest stable release.
Hammer is available in Hex. Install by adding :hammer
to your list of dependencies in mix.exs
:
def deps do
[
{:hammer, "~> 7.0"}
]
end
Atomic backends are single-node rate limiting but will be the fastest option.
- Hammer.ETS (default, can be distributed)
- Hammer.Atomic
- Hammer.Redis
- Hammer.Mnesia
Each backend supports multiple algorithms. Not all of them are available for all backends. The following table shows which algorithms are available for which backends.
By default, Hammer backends use the fixed window counter to track actions within set time windows, resetting the count at the start of each new window. For example, with a limit of 10 uploads per minute, a user could upload up to 10 files between 12:00:00 and 12:00:59, and up to 10 more between 12:01:00 and 12:01:59. Notice that the user can upload 20 videos in a second if the uploads are timed at the window edges. If this is an issue, it can be worked around with a "bursty" counter which can be implemented with the current API by making two checks, one for the original interval with the total limit, and one for a shorter interval with a fraction of the limit. That would smooth out the number of requests allowed.
Here's a comparison of the different rate limiting algorithms to help you choose:
- Simplest implementation with lowest overhead
- Good for basic rate limiting with clear time boundaries
- Potential edge case: Up to 2x requests possible at window boundaries
- Best for: Basic API limits where occasional bursts are acceptable
- Provides smooth, consistent request rate
- Requests "leak" out at constant rate
- Good for traffic shaping and steady throughput
- Best for: Network traffic control, queue processing
- Allows controlled bursts while maintaining average rate
- Tokens regenerate at fixed rate
- More flexible than fixed windows
- Best for: APIs needing burst tolerance, gaming mechanics
- Most precise rate limiting
- No boundary conditions like fixed windows
- Higher overhead than other algorithms
- Best for: Strict rate enforcement, critical systems
Selection Guide:
- Need simple implementation? → Fixed Window
- Need smooth output rate? → Leaky Bucket
- Need burst tolerance? → Token Bucket
- Need precise limits? → Sliding Window
- Limit: Maximum number of actions allowed in a window.
- Scale: Duration of the time window (in milliseconds).
- Key: Unique identifier (e.g., user ID) to scope the rate limiting.
defmodule MyApp.RateLimit do
use Hammer, backend: :ets
end
MyApp.RateLimit.start_link()
user_id = 42
key = "upload_video:#{user_id}"
scale = :timer.minutes(1)
limit = 3
case MyApp.RateLimit.hit(key, scale, limit) do
{:allow, _count} ->
# upload the video
:ok
{:deny, retry_after} ->
# deny the request
{:error, :rate_limit, _message = "try again in #{retry_after}ms"}
end
See the BENCHMARKS.md for more details.
Hammer was originally inspired by the ExRated library, by grempe.
Copyright (c) 2023 June Kelly Copyright (c) 2023-2024 See CONTRIBUTORS.md
This library is MIT licensed. See the LICENSE for details.