Oban-independent batched retention sweep for inbound tables (IOPS-03,
D-49-25..30). This is the workhorse: mix mailglass.inbound.prune and the
optional MailglassInbound.Prune.Worker cron both call prune/0.
Mirrors Mailglass.Webhook.Pruner's STRUCTURE (:infinity disables a class,
per-table telemetry) but UPGRADES the unbounded delete_all to a batched idiom
(D-49-27): each table deletes LIMIT 1000 rows at a time
(FOR UPDATE SKIP LOCKED), looping until a batch deletes < 1000. The whole
sweep is serialized by a session pg_try_advisory_lock — a concurrent second
run returns {:ok, :locked_out} and deletes nothing.
Window split (D-49-25) — three physical tables, four windows
mailglass_inbound_replay_runsWHEREsource = :replayAND age > replay_runs_days (30d)mailglass_inbound_replay_runsWHEREsource = :freshAND age > execution_runs_days (90d)mailglass_inbound_evidenceWHERE age > evidence_days (90d)mailglass_inbound_recordsWHERE age > records_days (90d)
Deletes run child-first (D-49-26): replay_runs (both source filters) -> evidence
-> records. FKs are on_delete: :nothing, so a mis-ordered delete fails loudly
on the FK (the designed safety net — do NOT switch to CASCADE).
Because the FKs are :nothing, a parent window can never be shorter than a child
that references it, or the child-first sweep would leave a surviving child whose
parent the next delete tries to remove — tripping a foreign_key_violation
(CR-02). MailglassInbound.Config.retention/0 enforces this by clamping
evidence_days >= max(execution_runs_days, replay_runs_days) and
records_days >= evidence_days, so the default windows (evidence 90d, not the
former 30d) never invert against :fresh runs aged 30-90 days.
source is filtered via ExecutionRun (which maps the :source column);
NEVER ReplayRun (no :source field — Pitfall 4).
Summary
Functions
The session advisory-lock key the prune sweep acquires. Exposed so tests can acquire it from a separate connection to exercise the single-run guard.
Run the retention sweep. Returns {:ok, counts} where counts carries the
per-table deletion counts (+ per-table batch iteration counts) and :status,
or {:ok, :locked_out} when another sweep holds the advisory lock.
Functions
@spec lock_key() :: integer()
The session advisory-lock key the prune sweep acquires. Exposed so tests can acquire it from a separate connection to exercise the single-run guard.
Run the retention sweep. Returns {:ok, counts} where counts carries the
per-table deletion counts (+ per-table batch iteration counts) and :status,
or {:ok, :locked_out} when another sweep holds the advisory lock.