Postgrex-backed Raxol.Agent.Cache adapter.
Persists cache entries in a PostgreSQL table so agents on
separate processes (or separate BEAM nodes) can share cache
state. Values are stored as bytea via
:erlang.term_to_binary/1, preserving arbitrary Erlang terms.
Optional dependency
Postgrex is declared optional: true in the umbrella's
mix.exs. Consumers using this adapter must add :postgrex to
their own deps and start a Postgrex connection (typically under
their own supervision tree). The adapter does not start or own
the connection; it expects :conn in the configuration to point
at a live process.
Config
%{
conn: MyApp.Postgrex, # registered name or pid
table: "raxol_agent_cache" # optional, default below
}:table defaults to "raxol_agent_cache". The table name is
validated against a safe identifier pattern so config-level
injection is not possible.
Schema
Run the SQL returned by create_table_sql/1 once (e.g. via Ecto
migration or psql) before the first call:
iex> Raxol.Agent.Cache.Postgrex.create_table_sql()
"""
CREATE TABLE IF NOT EXISTS raxol_agent_cache (
key bytea NOT NULL,
value bytea NOT NULL,
expires_at timestamptz,
PRIMARY KEY (key)
);
CREATE INDEX IF NOT EXISTS raxol_agent_cache_expires_at_idx
ON raxol_agent_cache (expires_at)
WHERE expires_at IS NOT NULL;
"""expires_at is nullable: rows with NULL never expire (matches
the ttl_ms == 0 contract). The partial index makes the
occasional eager-sweep query (DELETE WHERE expires_at <= now())
cheap; this adapter does not run that sweep automatically, but a
consumer's supervised periodic job can.
Upsert semantics
put/4 uses ON CONFLICT (key) DO UPDATE so a second write to
the same key replaces the previous value and expiry. Last-write-
wins is the documented cache semantic; the append-only contract
of Raxol.Workflow.Checkpoint.Saver does not apply here.
Lazy expiry
get/2 filters with WHERE key = $1 AND (expires_at IS NULL OR expires_at > now()) — a stale row stays in the table but is
invisible to get/2. The caller can periodically delete expired
rows via DELETE FROM <table> WHERE expires_at <= now() for
retention; the partial index keeps that cheap.
Summary
Functions
Returns the CREATE TABLE IF NOT EXISTS + partial-index SQL for
the cache table. Run once as part of the consumer's migration step.
Functions
Returns the CREATE TABLE IF NOT EXISTS + partial-index SQL for
the cache table. Run once as part of the consumer's migration step.
Pass a custom table name to match the value used in the cache
config; defaults to "raxol_agent_cache".