Behaviour describing the cluster-facing operations Hourglass code issues
through Hourglass.Client.
Two implementations:
Hourglass.Client.Real— production. Wraps the Bridge NIFs and round-trips real Temporal Server.Hourglass.Client.Mock— Mox-defined intest/support/client_mock.ex. Returns canned responses for behavioral tests so the default suite runs in-process with no cluster.
The active backend is resolved at call time via
Application.fetch_env!(:hourglass, :client_backend). Production
config sets it to Real; config/test.exs sets it to Mock.
Callback shape
No client ref leaks through the contract — each backend manages its own connection internally. Production opens the bridge client on demand inside each callback (matching the per-call connect pattern the codebase already uses); the mock has nothing to connect to.
Callbacks return Hourglass-typed values + decoded proto messages, never raw bytes — the Bridge's bytes-in/bytes-out contract is a NIF implementation detail, not part of this surface. That makes the mock cheap (no protobuf round-trip) and the production-impl boundary explicit (decode once at the boundary).
What the backend does NOT expose
connect— connection management is per-impl, not part of the contract.await_workflow—Hourglass.await/2is a polling loop onstatus/2; it doesn't need a separate cluster hook. Mock- backeddescribe_workflow_executioncovers it.query_workflow,terminate_workflow— not used by Hourglass today. Add when needed.
Summary
Callbacks
Requests cancellation of the identified workflow execution.
Returns a snapshot of the workflow's current execution state. Maps to
Temporal's DescribeWorkflowExecution RPC; the caller (Hourglass.status/2)
decodes the response into a WorkflowStatus struct.
Ensures the named namespace exists in the cluster. Idempotent: if the
namespace already exists, returns :ok without re-registering.
Returns the workflow's full event history. Maps to
GetWorkflowExecutionHistory (with wait_new_event: false semantics).
Used by Hourglass.status/2 (when failures: :include) and by
Hourglass.await/2's test-only completed-result extraction.
Sends a signal to the identified workflow execution.
Starts a new workflow execution. Returns a WorkflowHandle containing
the cluster-assigned run_id.
Functions
Resolves the active backend module.
Sets a per-process backend override. Returns the previous value (or
nil if there was none) so callers can restore it.
Callbacks
@callback cancel_workflow(handle :: Hourglass.WorkflowHandle.t(), reason :: String.t()) :: :ok | {:error, Hourglass.Error.t()}
Requests cancellation of the identified workflow execution.
@callback describe_workflow_execution(handle :: Hourglass.WorkflowHandle.t()) :: {:ok, Temporal.Api.Workflowservice.V1.DescribeWorkflowExecutionResponse.t()} | {:error, Hourglass.Error.t()}
Returns a snapshot of the workflow's current execution state. Maps to
Temporal's DescribeWorkflowExecution RPC; the caller (Hourglass.status/2)
decodes the response into a WorkflowStatus struct.
@callback ensure_namespace(name :: String.t()) :: :ok | {:error, Hourglass.Error.t()}
Ensures the named namespace exists in the cluster. Idempotent: if the
namespace already exists, returns :ok without re-registering.
@callback fetch_history(handle :: Hourglass.WorkflowHandle.t()) :: {:ok, Temporal.Api.History.V1.History.t()} | {:error, Hourglass.Error.t()}
Returns the workflow's full event history. Maps to
GetWorkflowExecutionHistory (with wait_new_event: false semantics).
Used by Hourglass.status/2 (when failures: :include) and by
Hourglass.await/2's test-only completed-result extraction.
@callback signal_workflow( handle :: Hourglass.WorkflowHandle.t(), name :: String.t(), payload :: term() ) :: :ok | {:error, Hourglass.Error.t()}
Sends a signal to the identified workflow execution.
@callback start_workflow(workflow_module :: module(), args :: term(), opts :: keyword()) :: {:ok, Hourglass.WorkflowHandle.t()} | {:error, Hourglass.Error.t()}
Starts a new workflow execution. Returns a WorkflowHandle containing
the cluster-assigned run_id.
Required opts:
:workflow_id— caller-supplied stable id.
Optional:
:task_queue— defaults to"default".:namespace— defaults toHourglass.Client.default_namespace/0.
Functions
@spec impl() :: module()
Resolves the active backend module.
Lookup order:
- Process dictionary (
set_impl/1) — per-process override, used by test setup to flip to the Mox mock for a single test without affecting other concurrent tests. - Application config (
:client_backend) — process-wide default. Production sets it toReal; tests useMock.
Production callers call this on every cluster operation. The cost is two map lookups — negligible compared to the cluster round-trip the call performs.
Sets a per-process backend override. Returns the previous value (or
nil if there was none) so callers can restore it.
Used by the test case template; production code should not call this.