Hourglass.Worker.WorkflowTypeResolver (hourglass v0.4.0)

Copy Markdown View Source

Maps a Temporal Core WorkflowActivation to the Elixir workflow module that should evaluate it.

Two-tier lookup, in order:

  1. Structural resolution from the activation's initialize_workflow job. Every fresh start, and every replay-from-history delivery to a worker that doesn't yet have the workflow cached, includes an InitializeWorkflow job whose workflow_type field is the module's stringified atom (e.g. "Elixir.Hourglass.Workflows.IngestSource"). We recover the module atom via String.to_existing_atom/1 and verify it is a loaded Hourglass workflow by checking for the __workflow_input_type__/0 marker injected by use Hourglass.Workflow. No registration or module list is required — the type name IS the module.

  2. From the cached Workflow.State. Subsequent incremental activations (e.g. just a resolve_activity job) don't repeat the workflow type. By the time we receive one, the prior activation has already populated state.workflow_module via Hourglass.Workflow.Evaluator.evaluate/3. We look up WorkflowStateCache.fetch(task_queue, run_id) and return the stored module.

When both tiers are exhausted the function returns {:error, :sticky_cache_miss} rather than raising. That case is a recoverable Core/cache desync: an incremental (sticky) activation arrived for a run the worker never cached (or evicted). It is NOT a bug in the activation — a non-sticky full-history redelivery of the same run always carries initialize_workflow (tier 1), so the caller recovers by failing the sticky workflow task to force that redelivery. See Hourglass.Worker.WorkflowPollLoop for the recovery.

A workflow-type name that IS present but cannot be turned into a loaded Hourglass workflow (bad format / unknown atom / not a workflow module) still raises: replaying won't change the type name, so that is a genuine deploy/coding error, not a transient desync.

Summary

Types

Result of resolve/2: the resolved module, or the recoverable sticky-cache-miss signal the poll loop turns into a workflow-task failure (forcing a non-sticky, full-history redelivery).

Functions

Resolve the workflow module for an activation.

Types

result()

@type result() :: {:ok, module()} | {:error, :sticky_cache_miss}

Result of resolve/2: the resolved module, or the recoverable sticky-cache-miss signal the poll loop turns into a workflow-task failure (forcing a non-sticky, full-history redelivery).

Functions

resolve(activation, task_queue)

@spec resolve(Coresdk.WorkflowActivation.WorkflowActivation.t(), String.t()) ::
  result()

Resolve the workflow module for an activation.

See moduledoc for lookup order. Returns {:ok, module} on success and {:error, :sticky_cache_miss} when neither tier yields a module — the recoverable "incremental activation for an uncached run" case. Raises only when a workflow-type name is present but unresolvable (unknown or unloaded module), which no redelivery can fix.