Every database statement, one function each, as raw SQL.
All functions take the repo explicitly. Step outcomes commit through the
batched flush/1 (group commit — see GenDurable.Flusher): one guarded
UPDATE … FROM unnest(...) for the whole batch plus the consume / parent-join /
await-recheck / children-insert riders, in one transaction.
concurrency_key serialization is enforced by the database — the UNIQUE
partial index gen_durable_concurrency_active makes a second executing row
per key uncommittable, so the pick's claim IS the lock (held exactly for the
step window, released by any outcome or the reaper); no advisory locks, no
pinned connections.
Every statement has a static SQL text and goes through the connection-level
prepared-statement cache (cache_statement:), so Postgres parses and plans it
once per connection instead of on every call — bulk inserts pass rows as
parallel arrays via unnest to keep the text static (and the parameter count
fixed, clear of the 65535-parameter protocol cap). The one exception is
upsert_rate_configs (dynamic VALUES, boot-time only). Hosts behind a
transaction-pooling proxy set prepare: :unnamed on the repo to bypass the
cache.
JSON values (state, result, signal payloads) arrive here as encoded JSON text
and are bound as TEXT parameters cast server-side ($n::text::jsonb). A bare
$n::jsonb parameter would make the driver JSON-encode the already-encoded
string, storing a double-encoded jsonb scalar instead of an object —
invisible to ->>/jsonb indexes. Rows written by versions that did exactly
that still decode fine: the read paths accept both formats.