X402.RateLimiter.ETS (X402 v0.9.0)

Copy Markdown View Source

Per-node fixed-window rate limiter backed by a lazily created ETS table.

Each key owns one counter per window: the first hit opens a window of window_ms and every hit until it closes increments the counter, so a key may make at most limit hits per window. Once the window closes the next hit opens a fresh one. Fixed windows admit up to 2 × limit hits across a window boundary; pick limit/window_ms with that in mind.

The table is created on first use and needs no process in your supervision tree. Stale windows are swept opportunistically (every 1000 hits) and can be swept explicitly with sweep/1.

Per-node only

Counters live on the local node. In a clustered deployment each node limits independently, so a wallet load-balanced across n nodes gets up to n × limit hits per window. Use a shared-store implementation of X402.RateLimiter when that matters.

Summary

Types

ETS table name holding the counters.

Functions

Records a hit for key in the fixed window of window_ms milliseconds.

Clears every counter in the table.

Removes windows that have already closed.

Types

table()

@type table() :: atom()

ETS table name holding the counters.

Functions

hit(table, key, limit, window_ms)

(since 0.9.0)

Records a hit for key in the fixed window of window_ms milliseconds.

table is the ETS table name; nil selects the default table.

Examples

iex> table = String.to_atom("rate_limiter_doctest_122916")
iex> X402.RateLimiter.ETS.hit(table, {:payer, "0xabc"}, 2, 60_000)
{:allow, 1}
iex> X402.RateLimiter.ETS.hit(table, {:payer, "0xabc"}, 2, 60_000)
{:allow, 0}
iex> {:deny, retry_after_ms} = X402.RateLimiter.ETS.hit(table, {:payer, "0xabc"}, 2, 60_000)
iex> retry_after_ms in 1..60_000
true

reset(table \\ nil)

(since 0.9.0)
@spec reset(table() | nil) :: :ok

Clears every counter in the table.

sweep(table \\ nil)

(since 0.9.0)
@spec sweep(table() | nil) :: non_neg_integer()

Removes windows that have already closed.

Returns the number of rows removed.