Skip to content

Vyral.Execution.Temporal.Hosting

This package hosts the single generic Vyral.RunCoordinator.v1 workflow and its activities. The workflow contains only deterministic coordination: activity scheduling, retry delays, native timers, event-id signals, and portable wait projection. Consumer handler code is invoked by the activity executor and may perform normal asynchronous I/O there.

Worker hosts own Temporal client construction, TLS/auth material, task queue selection, registered plugins, and worker identity. None of those settings can be selected by a portable run request.

For a .NET Generic Host, register the host-owned ITemporalClient, one ITemporalExecutionRuntimeStore, and the intended IObjectStore, then use the supported composition:

var temporal = services
.AddVyralTemporalExecution(executionOptions)
.AddPlugin<MyPortablePlugin>();
temporal.AddHostedWorker(new TemporalExecutionWorkerHostOptions
{
WorkerId = workerIdentity
});

AddVyralTemporalExecution creates one shared TemporalExecutionRuntimeAdapter, coordinator client, aggregate outbox reconciler, and preflight surface. An API-only host omits AddHostedWorker and does not load handler assemblies. A worker host adds the official Temporal Generic Host lifecycle, the fixed Vyral workflow/activities, and a cancellation-aware background pump for start, signal, and cancellation outboxes. The pump logs counts and fixed messages only; durable row identities and provider exception text are not logged. Reconciliation is safe across multiple worker replicas because the PostgreSQL store claims bounded batches. The disposable and live qualification suites also start this exact Generic Host composition, submit a portable handler through it, and require the hosted outbox pump to deliver the run before the receipt records generic_host_composition.

TemporalExecutionWorker registers exactly one versioned workflow and three activities: execute a Vyral attempt, project a wait resolution, and project cancellation. Activities delegate to the host-provided ITemporalExecutionActivityExecutor. For normal hosts, the overload accepting an ITemporalExecutionHandlerResolver, ITemporalExecutionRuntimeStore, options, and a provider-owned worker id composes the standard TemporalExecutionAttemptHandler. That handler supplies the portable IExecutionRunContext: progress, traces, artifacts, checkpoints, fenced leases, timers, external events, and durable wait suspension/resumption are projected without making the database responsible for invoking consumer code. The lower-level attempt-handler overload remains available for custom host composition.

Activities heartbeat every five seconds under a thirty-second heartbeat timeout and have bounded Temporal transport retries under a fixed ten-minute per-try start-to-close timeout. Every transport retry carries the same Vyral attempt number; the projection replays an already persisted terminal, retry, or suspension outcome instead of invoking the handler again. If the prior worker stopped before persisting an outcome, the handler may re-execute under the documented at-least-once side effect policy. Portable retry classification, count, and delay remain Vyral-owned.

The coordinator uses only replay-safe workflow APIs for delays, signals, queries, and cancellation. It never opens a database connection, performs network I/O, reads a secret, invokes consumer code, or places payload/artifact bodies in workflow history.

The coordinator rolls over after 32 coordination transitions or an earlier Temporal server suggestion. Continue-as-new occurs only between activities and waits, carries the portable attempt offset, cumulative transition count, and deduplicated event id/revision signals, and keeps the same Vyral run id and coordinator generation. A Temporal patch marker protects replay of workflows that started before rollover support. Temporal run ids may change across the chain and remain diagnostic metadata only. The dependency-backed qualification gate drives 37 transitions, follows each exact Temporal run in the chain, and rejects any run above 512 history events or 1 MiB of encoded protobuf history. It also caps that scenario at eight chained runs. These are conservative Vyral release guardrails, not Temporal service limits; a coordinator-shape change must re-run the measurement gate before the threshold is retained. The normal test suite also replays a checked-in successful coordinator history captured before the continue-as-new patch marker existed. This offline fixture makes removal of the required legacy patch branch a deterministic test failure.

The dependency-backed history-boundary case writes distinct synthetic sentinels through a run payload, external-event body, checkpoint, artifact body, trace, status details, and terminal result. It verifies each body in the Vyral projection and then scans the exact serialized Temporal protobuf history, failing if any sentinel appears there. Hashes and opaque ids may cross the coordinator boundary; consumer-owned bodies may not.

Waits resume by re-executing the handler, not by restoring a CLR stack frame. Plugins must use checkpoints and idempotent external side effects. Artifacts without an existing URI remain inline up to MaxArtifactInlineBytes; with an IObjectStore supplied to the standard handler or worker constructor, larger bodies are written beneath a run-owned prefix and projection metadata carries only a vyral-object:// reference, hash, size, and non-secret storage markers. Without that composition, oversized writes fail before projection rather than silently entering PostgreSQL.

This remains a prototype host surface until its checked-in process-restart gate has produced a retained passing receipt. Its Generic Host composition deliberately uses Temporal’s official hosting integration, but it does not yet enable Temporal Worker Versioning routing. Workers advertise non-routing deployment/build metadata, and preflight classifies current, other unversioned, unattributed, and unexpectedly versioned pollers without returning raw worker identities or deployment labels. The disposable Temporal/PostgreSQL gate exercises shared conformance, native timers/signals, object-store offload, bounded-history rollover, and the real worker-process recovery drill. The opt-in live gate runs the same behavioral and process-boundary coverage against operator-provisioned TLS/authenticated Temporal and PostgreSQL endpoints with random identities and a cleanup audit. The neutral console host in samples/Vyral.Execution.TemporalSample is operable example composition, not qualification evidence by itself.

Use the operator material linked from the repository’s Execution Runtime guide for production topology, replay-compatible rollout, credential rotation, backup/restore, and incident recovery.

TemporalExecutionPreflight validates the redacted topology, checks Temporal workflow-service health and namespace access, reports fresh workflow/activity poller and aggregate build-compatibility counts from a read-only task queue inspection, reads projection schema/backlog state, and verifies a write/read/delete object-store probe under a random isolated key. It never starts a workflow and returns fixed blocker/warning codes instead of provider exception text. Missing or uninspectable worker pollers remain declared warnings rather than inferred successes. For delivered, nonterminal runs it takes a bounded projection snapshot and compares each included coordinator with the latest Temporal workflow status. Missing or closed coordinators and partial/failed inspection are warnings; only counts and coverage are returned, never workflow ids, worker identities, or observed build labels.


Canonical source: src/Vyral.Execution.Temporal.Hosting/README.md at cb04cf7