Skip to main content

Temporal Workers

@cona/temporal-workers — the Node process that polls Temporal task queues and executes activities. Runs on Fly.io in fra. For queue routing and the workflows themselves see Temporal Orchestration; for VM sizing see Deployment Topology.

Bootstrap order

Startup order matters here: environment validation happens before any connection is opened, and worker-group selection happens before module load so Prisma pools size correctly. worker-group.ts is deliberately eight lines with a comment explaining why: worker dependencies initialise Prisma pools at module load, so WORKER_GROUP must be set before worker.js is imported, not inside run().

Worker factories

workers/index.ts:39-59 declares 19 production factories, each tagged with a group. getWorkerFactories() filters by WORKER_GROUP (all starts everything) and appends the test-document factory only when NODE_ENV !== "production". Three factories produce more than one worker: shopify (parent + child), amazon (settlements + VAT report), accounting and reconciliation (two lanes each), and mirakl (one template × 6 marketplaces).

Health and shutdown

The health server is an internal Fly Machine check on :8080/health — workers expose no public port and sit behind no proxy. Shutdown is ordered deliberately (worker.ts:261-293):
  1. On SIGTERM, close the health server first so Fly stops routing before workers drain.
  2. Call worker.run() shutdown; promises resolve when each worker reaches STOPPED.
  3. Promise.allSettled, not Promise.all — the comment at worker.ts:277 explains that all short-circuits on first rejection and would lose the per-worker failure log.
  4. Flush telemetry. OTLP export is best-effort; the telemetry package absorbs exporter failures so observability can never block shutdown (worker.ts:377-378).
Fly caps kill_timeout at 300s. The worker force deadline is 4m30s, leaving 30s for connection, logger, and telemetry cleanup.

Source layout

Dependencies

Only 2 direct @cona/core imports — business logic is reached through activities in @cona/temporal-workflows, which imports core 292 times. The layering holds.

Notes

The accounting queue gets special treatment. ensureAccountingQueueRunning (worker.ts:62) runs after worker creation to guarantee the long-lived syncAccountingQueueWorkflow is active. See Accounting Engine. Workflow bundles are cached. create-worker.ts exposes getWorkflowBundleCacheStats() and buildWorkflowBundle(); bundling all workflows per worker would otherwise dominate startup. No Sentry. Workers report to Axiom and OpenTelemetry only.