Baton.Completion (Baton v0.1.0)

Copy Markdown View Source

Detects when a whole workflow has finished and announces it exactly once.

Two paths feed this module:

  • check/2 — called by Baton.Worker every 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 by Baton.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, so check/2 never 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

announce(workflow_id, label)

@spec announce(String.t(), String.t() | nil) ::
  {:finished, :completed | :failed} | :noop

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.

check(job, terminal_state)

@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.