StatifierOban.Invoke.Worker (StatifierOban v0.1.0)

Copy Markdown View Source

The Oban worker a base-handler invocation becomes.

Uniqueness is the whole point of this module, exactly as it is for StatifierOban.Timer.Worker: jobs are unique on the {scope, invoke_id} pair, read off the args at the top level. Re-executing the same drive after a crash rebuilds a byte-identical pair - invoke_id is a deterministic %MachineState{} counter (st-ADR-0008 as amended) - so the duplicate insert conflicts with the stored job and becomes a no-op. That conflict, not any check in host code, is what makes the at-least-once perform/2 contract (st-ADR-0051 decision 4) safe for the enqueue itself.

The unique window is every state over an infinite period: an invoke whose job already completed, was cancelled, or was discarded must still swallow a replayed insert, because the replay is the same scheduling decision, not a new one. The unique fields exclude :queue and the meta the delivery module rides on, for the same reasons the timer worker's do.

perform/1 decodes the stored effect, resolves the handler module the args carry, calls its run/1 - the host's actual work, at least once, idempotent on invoke_id by that module's own contract - and hands the result to the job's StatifierOban.Invoke.Delivery module (from the meta written at enqueue time; absent meta falls back to the documented default, StatifierOban.Invoke.Delivery.Session), which owes the run-liveness check before any completion is fed back. The outcomes map onto Oban states so each is observable on the job row:

  • work done and delivered -> the job completes (:ok);
  • the run is not live -> the job cancels with {:discarded, reason} recorded - a completed invoke against a dead or halted run is discarded the same way a fired timer is;
  • run/1 returns {:error, reason} -> the job retries with {:run_failed, reason} recorded - the work is idempotent on invoke_id by contract, so retrying is what at-least-once means; a raise or exit out of run/1 (or the delivery module) retries the same way;
  • an undecodable row cancels with {:undecodable, reason} - no number of retries makes a corrupt row decodable;
  • a handler or delivery module that cannot be resolved returns {:error, {:invalid_handler, _}} / {:error, {:invalid_delivery, _}} and retries - environment facts about the host's code, fixable by a deploy, unlike the row facts above.