Docket.Postgres.Vehicle (docket v0.1.0-dev)

Copy Markdown View Source

Ephemeral execution shell for one claimed run.

Each vehicle loads and compiles the run's exact graph, validates every explicit node timeout against the host's finite attempt maximum, and commits one fenced runtime moment at a time. Host-policy incompatibility reschedules the claim without poisoning: the claim-attempt increment is reversed, the run stays valid, and the handback counts one claim abandon. The retry wake backs off exponentially with the consecutive abandon count — min(abandon_backoff_ms * 2^abandons, abandon_backoff_cap_ms) — so a fleet with no compatible host pushes the run back instead of spinning, and any committed progress resets the count. Stored-graph incompatibility shares the same abandon counter, so a persistently incompatible deployment still poisons through that path's budget. Deployments should still use homogeneous limits and audit stored graphs before rollout so compatible work is not needlessly delayed.

Node attempt deadlines are enforced by the runtime dispatcher. The cooperative drain budget is checked only at moment boundaries and includes configured headroom below orphan-TTL crash recovery. Token/sequence fencing remains the sole authority for commits after a crash or steal.

Vehicles never refresh claims. Attempt timeout and retry are replay boundaries: external effects and unlinked children cannot be retracted, so expected long work must park or detach durably.

Summary

Types

Result of one drain.

Functions

Synchronously drains one claim lease to its next park or stop.

Launches a vehicle under the configured :task_supervisor.

Builds the dispatcher's :launch callback from eagerly validated options.

Types

drain_budget()

@type drain_budget() :: [
  max_moments: pos_integer() | :infinity,
  max_elapsed_ms: pos_integer() | :infinity
]

option()

@type option() ::
  {:backend, {module(), Docket.Backend.ctx()}}
  | {:task_supervisor, Supervisor.supervisor()}
  | {:clock, (-> DateTime.t())}
  | {:monotonic_clock, (-> integer())}
  | {:drain_budget, drain_budget()}
  | {:max_attempt_elapsed_ms, pos_integer()}
  | {:jitter, (pos_integer() -> non_neg_integer())}
  | {:abandon_backoff_ms, pos_integer()}
  | {:abandon_backoff_cap_ms, pos_integer()}
  | {:max_claim_abandons, pos_integer()}
  | {:graph_cache, module() | false}
  | {:graph_cache_opts, keyword()}
  | {:compiler,
     (Docket.Graph.t(), keyword() ->
        {:ok, Docket.Runtime.Graph.t()} | {:error, Docket.Graph.t()})}
  | {:executor, module()}
  | {:executor_opts, keyword()}
  | {:max_supersteps, pos_integer()}
  | {:context, map()}
  | {:id_generator, (atom() -> String.t())}
  | {:checkpoint_observers, module() | [module()]}

outcome()

@type outcome() ::
  {:ok,
   {:parked, Docket.Runtime.Moment.park_kind()}
   | :fence_lost
   | {:discarded, term()}
   | {:host_incompatible, Docket.Error.t()}
   | {:abandoned, :rescheduled | :poisoned | :stale, term()}
   | {:deferred, :rescheduled | :poisoned | :stale}}

Result of one drain.

{:parked, kind} drained to a committed park. :fence_lost and {:discarded, reason} stopped without commit authority. {:host_incompatible, error} released without poisoning because this host's attempt maximum cannot execute the graph. {:abandoned, ...} is a stored-graph incompatibility; {:deferred, ...} has no due attempt yet.

Functions

drain(lease, opts)

Synchronously drains one claim lease to its next park or stop.

launch(lease, opts)

@spec launch(Docket.Backend.RunStore.claim_lease(), [option()]) ::
  {:ok, pid()} | {:error, term()}

Launches a vehicle under the configured :task_supervisor.

Shaped for the dispatcher's :launch callback: launch: &Docket.Postgres.Vehicle.launch(&1, opts). Prefer launcher/1, which validates the options once at assembly time.

launcher(opts)

@spec launcher([option()]) :: (Docket.Backend.RunStore.claim_lease() ->
                           {:ok, pid()} | {:error, term()})

Builds the dispatcher's :launch callback from eagerly validated options.

Validation happens in this call, so a malformed :drain_budget or :monotonic_clock raises ArgumentError where the supervision tree is built - before any vehicle launches or any claim is consumed - instead of failing per claim. This is the recommended wiring: launch: Docket.Postgres.Vehicle.launcher(opts).