The Oban job that propagates ONE change — the entry point for everything a host writes.
A write records what it changed and enqueues this. The job then walks the graph from that change, in its own transaction, until the cascade completes or reaches something that has to stop.
Why the write does not cascade inline
The design says a cascade runs in one transaction. It does — its own, not the writer's. A host write that transitively touches fourteen cells should not hold a user's request open for all of them, and running the walk inside the writer's transaction would put that transaction at risk of exactly the problem this redesign exists to remove: a long-held connection.
So the write's transaction contains one INSERT — this job — and commits. The consequence, worth stating because it surprises: a host reading a derived table immediately after its own write sees the old value. That was true of the drain too, and remains true here.
Uniqueness is a 60-second window, NOT :infinity
This is the trap in this file, and it is worth reading before copying the config from anywhere else.
DrainWorker used period: :infinity with empty args, and that was right
for it: every drain did identical global work, so a burst of writes wanted
exactly one pending drain.
These args name a SPECIFIC change. :infinity would mean "the same row
changing twice, ever, cascades once" — a durable correctness bug, silent,
and impossible to notice from the outside. Sixty seconds coalesces a burst
of writes to one row and nothing more.
Contrast ReactiveDag.ResumptionWorker, whose :infinity IS right, for a
reason that only applies there: its args are a stopping point rather than a
change.
Summary
Functions
Enqueue a cascade from one cell's changed keys.
Functions
@spec enqueue(String.t(), [String.t()], keyword()) :: {:ok, Oban.Job.t()} | {:error, term()}
Enqueue a cascade from one cell's changed keys.
Safe to call inside a transaction: it is one INSERT, and it commits with the write that caused it — so a rolled-back write leaves no cascade, and a committed one always leaves exactly one.
versions maps a changed key to the id of the version recording what the
change did. Omitting it is allowed and costly: a suspension downstream will
carry "*" and resume by recomputing the whole cell.