Skip to content

Commit

Permalink
fix docs (#121)
Browse files Browse the repository at this point in the history
  • Loading branch information
epinault authored Jan 22, 2025
1 parent 074b1c9 commit 8e1e417
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 25 deletions.
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,13 @@ Each backend supports multiple algorithms. Not all of them are available for all
| [Hammer.ETS.FixWindow](https://hexdocs.pm/hammer/Hammer.ETS.FixWindow.html) | [Hammer.ETS](https://hexdocs.pm/hammer/Hammer.ETS.html) |
| [Hammer.ETS.LeakyBucket](https://hexdocs.pm/hammer/Hammer.ETS.LeakyBucket.html) | [Hammer.ETS](https://hexdocs.pm/hammer/Hammer.ETS.html) |
| [Hammer.ETS.TokenBucket](https://hexdocs.pm/hammer/Hammer.ETS.TokenBucket.html) | [Hammer.ETS](https://hexdocs.pm/hammer/Hammer.ETS.html) |
| [Hammer.ETS.SlidingWindow](https://hexdocs.pm/hammer/Hammer.ETS.SlidingWindow.html) | [Hammer.Redis](https://hexdocs.pm/hammer/Hammer.ETS.html) |
| [Hammer.Redis.FixedWindow](https://hexdocs.pm/hammer/Hammer.Redis.FixedWindow.html) | [Hammer.Redis](https://hexdocs.pm/hammer/Hammer.Redis.html) |
| [Hammer.ETS.SlidingWindow](https://hexdocs.pm/hammer/Hammer.ETS.SlidingWindow.html) | [Hammer.ETS](https://hexdocs.pm/hammer/Hammer.ETS.html) |
| [Hammer.Redis.FixedWindow](https://hexdocs.pm/hammer_backend_redis/Hammer.Redis.FixedWindow.html) | [Hammer.Redis](https://hexdocs.pm/hammer_backend_redis/Hammer.Redis.html) |
| [Hammer.Redis.LeakyBucket](https://hexdocs.pm/hammer_backend_redis/Hammer.Redis.LeakyBucket.html) | [Hammer.Redis](https://hexdocs.pm/hammer_backend_redis/Hammer.Redis.html) |
| [Hammer.Redis.TokenBucket](https://hexdocs.pm/hammer_backend_redis/Hammer.Redis.TokenBucket.html) | [Hammer.Redis](https://hexdocs.pm/hammer_backend_redis/Hammer.Redis.html) |
| [Hammer.Mnesia.FixedWindow](https://hexdocs.pm/hammer_backend_mnesia/Hammer.Mnesia.FixedWindow.html) | [Hammer.Mnesia](https://hexdocs.pm/hammer_backend_mnesia/Hammer.Mnesia.html) |
| [Hammer.Mnesia.LeakyBucket](https://hexdocs.pm/hammer_backend_mnesia/Hammer.Mnesia.LeakyBucket.html) | [Hammer.Mnesia](https://hexdocs.pm/hammer_backend_mnesia/Hammer.Mnesia.html) |
| [Hammer.Mnesia.TokenBucket](https://hexdocs.pm/hammer_backend_mnesia/Hammer.Mnesia.TokenBucket.html) | [Hammer.Mnesia](https://hexdocs.pm/hammer_backend_mnesia/Hammer.Mnesia.html) |

## Default Algorithm

Expand Down Expand Up @@ -90,7 +95,12 @@ Selection Guide:
- Need burst tolerance? → Token Bucket
- Need precise limits? → Sliding Window

## Creating a Rate Limiter
## How to use Hammer

- Basic usage is covered in the [Tutorial](https://hexdocs.pm/hammer/tutorial.html).
- Distributed usage is covered in the [Distributed ETS](https://hexdocs.pm/hammer/distributed-ets.html) guide.

## The quick start

- **Limit:** Maximum number of actions allowed in a window.
- **Scale:** Duration of the time window (in milliseconds).
Expand Down
11 changes: 11 additions & 0 deletions lib/hammer/atomic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ defmodule Hammer.Atomic do
This provides fast, atomic counter operations without the overhead of ETS or process messaging.
Requires Erlang/OTP 21.2 or later.
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.
- `: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.
"""

use GenServer
Expand Down
18 changes: 15 additions & 3 deletions lib/hammer/atomic/fix_window.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule Hammer.Atomic.FixWindow do
- Window 2: 60-120 seconds
- And so on...
The algorithm:
## The algorithm:
1. When a request comes in, we:
- Calculate which window it belongs to based on current time
- Increment the counter for that window
Expand All @@ -31,7 +31,7 @@ defmodule Hammer.Atomic.FixWindow do
- You want efficient implementation with minimal storage overhead
- Your use case can tolerate potential bursts at window boundaries
Common use cases include:
## Common use cases include:
- Basic API rate limiting where occasional bursts are acceptable
- Protecting backend services from excessive load
Expand All @@ -51,14 +51,26 @@ defmodule Hammer.Atomic.FixWindow do
- `:clean_period` - How often to run the cleanup process (in milliseconds)
Defaults to 1 minute. The cleanup process removes expired window entries.
## Example
Example configuration:
### Example configuration:
MyApp.RateLimit.start_link(
clean_period: :timer.minutes(5),
)
This would run cleanup every 5 minutes and clean up old windows.
### Example usage:
defmodule MyApp.RateLimit do
use Hammer, backend: :atomic, algorithm: :fix_window
end
MyApp.RateLimit.start_link(clean_period: :timer.minutes(1))
# Allow 10 requests per second
MyApp.RateLimit.hit("user_123", 1000, 10)
"""
alias Hammer.Atomic
@doc false
Expand Down
18 changes: 15 additions & 3 deletions lib/hammer/atomic/leaky_bucket.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defmodule Hammer.Atomic.LeakyBucket do
- If bucket reaches capacity (100), new requests are denied
- Once bucket level drops, new requests are allowed again
The algorithm:
## The algorithm:
1. When a request comes in, we:
- Calculate how much has leaked since last request
- Subtract leaked amount from current bucket level
Expand All @@ -32,7 +32,7 @@ defmodule Hammer.Atomic.LeakyBucket do
- Need to smooth out traffic spikes
- Want to prevent resource exhaustion
Common use cases include:
## Common use cases include:
- API rate limiting needing consistent throughput
- Network traffic shaping
Expand Down Expand Up @@ -64,14 +64,26 @@ defmodule Hammer.Atomic.LeakyBucket do
If set, entries older than this will be removed during cleanup.
This helps prevent memory growth from abandoned buckets.
Example configuration:
## Example
### Example configuration:
MyApp.RateLimit.start_link(
clean_period: :timer.minutes(5),
key_older_than: :timer.hours(24)
)
This would run cleanup every 5 minutes and remove buckets not used in 24 hours.
### Example usage:
defmodule MyApp.RateLimit do
use Hammer, backend: :atomic, algorithm: :leaky_bucket
end
MyApp.RateLimit.start_link(clean_period: :timer.minutes(1))
# Allow 100 requests/sec leak rate with max capacity of 500
MyApp.RateLimit.hit("user_123", 100, 500, 1)
"""

@doc false
Expand Down
23 changes: 19 additions & 4 deletions lib/hammer/atomic/token_bucket.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ defmodule Hammer.Atomic.TokenBucket do
- If bucket has enough tokens, request allowed and tokens consumed
- If not enough tokens, request denied until bucket refills
The algorithm:
## The algorithm:
1. When a request comes in, we:
- Calculate tokens added since last request based on time elapsed
- Add new tokens to bucket (up to max capacity)
Expand All @@ -33,7 +33,7 @@ defmodule Hammer.Atomic.TokenBucket do
- Need to support different costs for different operations
- Want to avoid the sharp edges of fixed windows
Common use cases include:
## Common use cases include:
- API rate limiting with burst tolerance
- Network traffic shaping
Expand Down Expand Up @@ -67,14 +67,27 @@ defmodule Hammer.Atomic.TokenBucket do
If set, entries older than this will be removed during cleanup.
This helps prevent memory growth from abandoned buckets.
Example configuration:
## Example
### Example configuration:
MyApp.RateLimit.start_link(
clean_period: :timer.minutes(5),
key_older_than: :timer.hours(24)
)
This would run cleanup every 5 minutes and remove buckets not used in 24 hours.
### Example usage:
defmodule MyApp.RateLimit do
use Hammer, backend: :atomic, algorithm: :token_bucket
end
MyApp.RateLimit.start_link(clean_period: :timer.minutes(1))
# Allow 10 tokens per second with max capacity of 100
MyApp.RateLimit.hit("user_123", 10, 100, 1)
"""

@doc false
Expand All @@ -90,7 +103,9 @@ defmodule Hammer.Atomic.TokenBucket do
]
end

@doc false
@doc """
Checks if a key is allowed to perform an action, and consume the bucket by the given amount.
"""
@spec hit(
table :: atom(),
key :: String.t(),
Expand Down
13 changes: 13 additions & 0 deletions lib/hammer/ets.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,23 @@ defmodule Hammer.ETS do
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 1 hour
- `:algorithm` - the rate limiting algorithm to use, one of: `:fix_window`, `:sliding_window`, `:leaky_bucket`, `:token_bucket`. Defaults to `:fix_window`
The ETS backend supports the following algorithms:
- `:fix_window` - Fixed window rate limiting (default)
Simple counting within fixed time windows. See [Hammer.ETS.FixWindow](Hammer.ETS.FixWindow.html) for more details.
- `:leaky_bucket` - Leaky bucket rate limiting
Smooth rate limiting with a fixed rate of tokens. See [Hammer.ETS.LeakyBucket](Hammer.ETS.LeakyBucket.html) for more details.
- `:token_bucket` - Token bucket rate limiting
Flexible rate limiting with bursting capability. See [Hammer.ETS.TokenBucket](Hammer.ETS.TokenBucket.html) for more details.
"""

use GenServer
Expand Down
18 changes: 15 additions & 3 deletions lib/hammer/ets/fix_window.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule Hammer.ETS.FixWindow do
- Window 2: 60-120 seconds
- And so on...
The algorithm:
## The algorithm:
1. When a request comes in, we:
- Calculate which window it belongs to based on current time
- Increment the counter for that window
Expand All @@ -31,7 +31,7 @@ defmodule Hammer.ETS.FixWindow do
- You want efficient implementation with minimal storage overhead
- Your use case can tolerate potential bursts at window boundaries
Common use cases include:
## Common use cases include:
- Basic API rate limiting where occasional bursts are acceptable
- Protecting backend services from excessive load
Expand All @@ -52,13 +52,25 @@ defmodule Hammer.ETS.FixWindow do
Defaults to 1 minute. The cleanup process removes expired window entries.
Example configuration:
## Example
### Example configuration:
MyApp.RateLimit.start_link(
clean_period: :timer.minutes(5),
)
This would run cleanup every 5 minutes and clean up old windows.
### Example usage:
defmodule MyApp.RateLimit do
use Hammer, backend: :ets, algorithm: :fix_window
end
MyApp.RateLimit.start_link(clean_period: :timer.minutes(1))
# Allow 10 requests per second
MyApp.RateLimit.hit("user_123", 1000, 10)
"""
alias Hammer.ETS
@doc false
Expand Down
18 changes: 15 additions & 3 deletions lib/hammer/ets/leaky_bucket.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defmodule Hammer.ETS.LeakyBucket do
- If bucket reaches capacity (100), new requests are denied
- Once bucket level drops, new requests are allowed again
The algorithm:
## The algorithm:
1. When a request comes in, we:
- Calculate how much has leaked since last request
- Subtract leaked amount from current bucket level
Expand All @@ -32,7 +32,7 @@ defmodule Hammer.ETS.LeakyBucket do
- Need to smooth out traffic spikes
- Want to prevent resource exhaustion
Common use cases include:
## Common use cases include:
- API rate limiting needing consistent throughput
- Network traffic shaping
Expand Down Expand Up @@ -64,14 +64,26 @@ defmodule Hammer.ETS.LeakyBucket do
If set, entries older than this will be removed during cleanup.
This helps prevent memory growth from abandoned buckets.
Example configuration:
## Example
### Example configuration:
MyApp.RateLimit.start_link(
clean_period: :timer.minutes(5),
key_older_than: :timer.hours(24)
)
This would run cleanup every 5 minutes and remove buckets not used in 24 hours.
### Example usage:
defmodule MyApp.RateLimit do
use Hammer, backend: :ets, algorithm: :leaky_bucket
end
MyApp.RateLimit.start_link(clean_period: :timer.minutes(1))
# Allow 100 requests/sec leak rate with max capacity of 500
MyApp.RateLimit.hit("user_123", 100, 500, 1)
"""
alias Hammer.ETS

Expand Down
18 changes: 15 additions & 3 deletions lib/hammer/ets/sliding_window.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ defmodule Hammer.ETS.SlidingWindow do
- At time t+1, we look back 60 seconds from t+1, dropping any requests older than that
- This creates a "sliding" effect where the window gradually moves forward in time
The algorithm:
## The algorithm:
1. When a request comes in, we store it with the current timestamp
2. To check if rate limit is exceeded, we:
- Count all requests within the last <scale> seconds
Expand All @@ -30,7 +30,7 @@ defmodule Hammer.ETS.SlidingWindow do
- You can accept slightly higher storage overhead compared to fixed windows
- You want to avoid sudden changes in allowed request rates
Common use cases include:
## Common use cases include:
- API rate limiting where consistent request rates are important
- Financial transaction rate limiting
Expand Down Expand Up @@ -58,13 +58,25 @@ defmodule Hammer.ETS.SlidingWindow do
- `:clean_period` - How often to run the cleanup process (in milliseconds)
Defaults to 1 minute. The cleanup process removes expired window entries.
Example configuration:
## Example
### Example configuration:
MyApp.RateLimit.start_link(
clean_period: :timer.minutes(5),
)
This would run cleanup every 5 minutes and clean up old windows.
### Example usage:
defmodule MyApp.RateLimit do
use Hammer, backend: :ets, algorithm: :sliding_window
end
MyApp.RateLimit.start_link(clean_period: :timer.minutes(1))
# Allow 10 requests in any 1 second window
MyApp.RateLimit.hit("user_123", 1000, 10)
"""
alias Hammer.ETS.SlidingWindow

Expand Down
Loading

0 comments on commit 8e1e417

Please sign in to comment.