Detects when a whole workflow has finished and announces it exactly once.
Two paths feed this module:
check/2— called byBaton.Workerevery time a step reaches a terminal state. When that step is the last one outstanding, the workflow has settled. This is the prompt path: the notification fires the instant the final step returns.announce/2— called byBaton.Plugin's sweep as a backstop, for workflows that settled without a clean worker return (e.g. a step that hard-crashed or was killed by Oban, socheck/2never ran for it).
Exactly-once
Both paths funnel through a single atomic claim: the first writer to insert
the workflow's row in workflow_completions (on-conflict-do-nothing) is the
one that broadcasts {:workflow_finished, _} and emits
[:baton, :workflow, :finished]. Everyone else — a retry, a simultaneous
sibling step, or the plugin arriving after the worker already announced — sees
the conflict and stays silent. So the terminal notification fires once per
workflow, regardless of which path detects it.
Why the just-finished step is passed to check/2
When a worker's perform/1 returns, Oban hasn't yet written the new terminal
state to oban_jobs — at that instant the current job still reads as
executing. So check/2 is told the step's resolved state explicitly and
overlays it on what the database reports. announce/2 has no such lag (the
plugin only sees states Oban has already written), so it reads straight from
the database.
Summary
Functions
Backstop path for Baton.Plugin: announce a workflow as finished if its
database state shows it fully settled and it hasn't been announced yet.
Given a job that just reached terminal_state, broadcast a finished event if
the workflow is now fully settled and not already announced. Returns :ok
regardless; failures here are logged and never propagated to the job.
Functions
Backstop path for Baton.Plugin: announce a workflow as finished if its
database state shows it fully settled and it hasn't been announced yet.
Returns {:finished, outcome} if this call made the announcement, or
:noop if the workflow is still running or was already announced.
@spec check(Oban.Job.t(), String.t()) :: :ok
Given a job that just reached terminal_state, broadcast a finished event if
the workflow is now fully settled and not already announced. Returns :ok
regardless; failures here are logged and never propagated to the job.