Legion. RateLimiter. Postgres
(Legion v0.5.0)
View Source
A Postgres-backed Legion.RateLimiter extending Legion.Store.Postgres.
This adapter depends on an existing Legion.Store.Postgres configuration.
It stores rate-limit metadata in that store's table and reads persisted usage from it.
Configure the store first, then define a rate limiter using the same Ecto repo and table:
defmodule MyApp.AgentStore do
use Legion.Store.Postgres, repo: MyApp.Repo
end
defmodule MyApp.RateLimiter do
use Legion.RateLimiter.Postgres, repo: MyApp.Repo
endThe Store migration creates the rate-limit metadata column and its GIN index. No separate rate-limiter migration is required:
defmodule MyApp.Repo.Migrations.AddLegionAgents do
use Ecto.Migration
def up, do: Legion.Store.Postgres.Migration.up()
def down, do: Legion.Store.Postgres.Migration.down()
endCall the generated limiter before running work, passing every rule that applies to the agent:
policy = %Legion.RateLimiter.Policy{
window_ms: :timer.minutes(1),
max_agents: 10,
max_tokens: 100_000
}
:ok =
MyApp.RateLimiter.enforce!(agent_id, [
%Legion.RateLimiter.Rule{identity: %{"ip" => "203.0.113.42"}, policy: policy}
])All rules are evaluated inside one transaction: the adapter locks every distinct identity (in a global order, so agents naming the same identities in different orders cannot deadlock), records the agent, then checks the rules in the order given. The first violated rule raises and rolls the whole call back, leaving metadata only for allowed calls.
Limits are evaluated when a turn starts; a turn that is already running is
never interrupted, so one turn can carry the recorded total past
:max_tokens before the next call is denied.
Identity matching
An agent's rule identities are merged into one JSON metadata document and
matched with Postgres JSON containment. Each rule is evaluated against the
agents whose metadata contains its identity, so an agent recorded under
%{"ip" => "203.0.113.42"} and %{"email" => "someone@example.com"} counts
towards both groups, and a broader identity such as
%{"ip" => "203.0.113.42"} also matches an agent recorded with a "tenant"
field. Rules passed directly to enforce!/2 must agree on shared fields;
Legion.start_link/2 validates this, and the adapter does not. Calling
enforce!/2 again for the same agent replaces its stored metadata while
retaining its original start time.
Limit evaluation
The adapter includes the agent being checked when it evaluates
:max_agents, so it raises only when that call would make the matching count
exceed the configured maximum. Agents are counted by started_at.
:max_tokens is evaluated from recorded "total_tokens" usage whose "at"
timestamp falls inside the policy's window; once that total reaches the
configured maximum, later calls raise.
Token limits require Legion usage tracking, which is enabled by default. If
your application configures config :legion, :track_usage, false, leave
:max_tokens as nil; agent limits do not require usage tracking.
Options
:repo(required) - the Ecto repo used by the configured store.:table- the shared Store table name, defaulting to"legion_agents". It must also be passed to the Store migration.