PhoenixKit.Jobs.Run (phoenix_kit v2.55.1)

Copy Markdown View Source

A job run: one logical piece of long background work (V207).

Oban executes it batch by batch; this row is what an admin watches and controls. See PhoenixKit.Jobs for the API and dev_docs/plans/2026-10-03-job-runs.md for the design. Go through PhoenixKit.Jobs rather than writing rows: every transition is one transaction that also dispatches the next batch and writes the history.

States

  • queued — started (or resumed, or restarted), waiting for a batch;
  • running — a batch is executing, or the next one is waiting for its turn;
  • pausing / cancelling — a pause or cancel was asked while a batch holds the run; the batch finishes, then the run becomes paused / cancelled;
  • paused, completed, failed, cancelled.

The first five are active: at most one run per kind and scope may be in one of them (a partial unique index), so a retry cannot start while the old run is still draining.

The execution protocol

generation counts dispatches: every Oban job carries the generation it was made for, and one of an older generation is inert. claim_token / claimed_at say a batch holds the run now. restart_seq counts triggers that asked for a fresh pass and restart_ack the last one a batch has taken in. rescues / last_rescued_at are the sweeper's durable budget, and oban_job_id the current dispatch.

Summary

Functions

Whether the run has not finished.

The states of an unfinished run: at most one per kind and scope.

Whether a batch holds the run right now.

Whether a script (run_inline/3) holds the run, not an Oban batch.

Seconds a script's claim stands without a heartbeat.

Whether a script owns the run between its batches: it holds no batch claim but has not given the run up either (it is waiting out a delay).

The changeset a new run is inserted with.

Whether the lease of a script's claim has run out at now. The lease runs from the latest sign of life — the claim, a heartbeat, or the time the script asked its next batch to wait for (wake_at) — so a script waiting out a long delay between batches holds its run for the delay and a lease after it.

The ways a run can have been started.

A whole number between 0 and 100, or nil when the total is not known.

The run's scope as {type, uuid}, or :site when it is about nothing in particular.

Seconds until wake_at, never negative; 0 when the run has no delay pending.

Every state a run can be in.

Whether the run has finished, one way or another.

The states a run never leaves.

Types

t()

@type t() :: %PhoenixKit.Jobs.Run{
  __meta__: term(),
  args: term(),
  cancelled_at: term(),
  cancelled_by_uuid: term(),
  claim_owner: term(),
  claim_token: term(),
  claimed_at: term(),
  cursor: term(),
  done: term(),
  error: term(),
  failed_count: term(),
  finished_at: term(),
  generation: term(),
  heartbeat_at: term(),
  inserted_at: term(),
  interruptions: term(),
  kind: term(),
  last_rescued_at: term(),
  mode: term(),
  module: term(),
  oban_job_id: term(),
  owner_token: term(),
  paused_at: term(),
  paused_by_uuid: term(),
  rescues: term(),
  restart_ack: term(),
  restart_seq: term(),
  result: term(),
  scope_type: term(),
  scope_uuid: term(),
  started_at: term(),
  started_by_uuid: term(),
  state: term(),
  title: term(),
  total: term(),
  updated_at: term(),
  uuid: term(),
  wake_at: term()
}

Functions

active?(state)

@spec active?(t() | String.t()) :: boolean()

Whether the run has not finished.

active_states()

The states of an unfinished run: at most one per kind and scope.

claimed?(run)

@spec claimed?(t()) :: boolean()

Whether a batch holds the run right now.

inline_claim?(run)

@spec inline_claim?(t()) :: boolean()

Whether a script (run_inline/3) holds the run, not an Oban batch.

inline_lease_seconds()

Seconds a script's claim stands without a heartbeat.

inline_waiting?(run)

@spec inline_waiting?(t()) :: boolean()

Whether a script owns the run between its batches: it holds no batch claim but has not given the run up either (it is waiting out a delay).

insert_changeset(attrs)

The changeset a new run is inserted with.

lease_expired?(run, now)

@spec lease_expired?(t(), DateTime.t()) :: boolean()

Whether the lease of a script's claim has run out at now. The lease runs from the latest sign of life — the claim, a heartbeat, or the time the script asked its next batch to wait for (wake_at) — so a script waiting out a long delay between batches holds its run for the delay and a lease after it.

modes()

The ways a run can have been started.

percent(run)

@spec percent(t()) :: non_neg_integer() | nil

A whole number between 0 and 100, or nil when the total is not known.

scope(run)

@spec scope(t()) :: :site | {String.t(), String.t()}

The run's scope as {type, uuid}, or :site when it is about nothing in particular.

seconds_until_wake(run, now)

@spec seconds_until_wake(t(), DateTime.t()) :: non_neg_integer()

Seconds until wake_at, never negative; 0 when the run has no delay pending.

states()

Every state a run can be in.

terminal?(state)

@spec terminal?(t() | String.t()) :: boolean()

Whether the run has finished, one way or another.

terminal_states()

The states a run never leaves.