new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Temporal · Develop · all subjects

workers/basics

378 notes in this subject, read out of this brain and free to use. This is page 5 of 7.

Worker setup example in Rust

Example showing full Worker setup: ```rust use std::str::FromStr; use temporalio_client::{Client, ClientOptions, Connection, ConnectionOptions}; use temporalio_common::telemetry::TelemetryOptions; use temporalio_sdk::{Worker, WorkerOptions}; use temporalio_sdk_core::{CoreRuntime, RuntimeOptions, Url}; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let runtime = CoreRuntime::new_assume_tokio( RuntimeOptions::builder() .telemetry_options(TelemetryOptions::builder().build()) .build()?, )?; let connection_options = ConnectionOptions::new(Url::from_str("http://localhost:7233")?).build(); let connection = Connection::connect(connection_options).await?; let client = Client::new(connection, ClientOptions::new("default").build())?; let worker_options = WorkerOptions::new("my-task-queue") .register_workflow::<GreetingWorkflow>()? .register_activities(GreetingActivities) .build(); let mut worker = Worker::new(&runtime, client, worker_options)?; worker.run().await?; Ok(()) } ``` This example creates a CoreRuntime, connects to a local Temporal server on port 7233, registers a workflow and activities, and runs the worker.

Shut down a Worker in Rust SDK

Call shutdown_handle() on the Worker before calling run(), since run() borrows the Worker mutably. The shutdown handle returned initiates shutdown when called, stopping polling for new Tasks and allowing in-flight Tasks to finish. Use this pattern to perform graceful shutdown on signals like SIGINT.

Worker options configuration

WorkerOptions controls Task Queue, cache size, poller behavior, and slot allocation. Key options include max_cached_workflows, workflow_task_poller_behavior, tuner, and client_identity_override to set a more useful Worker identity than the default of {pid}@{hostname}. The defaults work for most cases; tune against real load using Worker performance and worker tuning reference documentation.

Task queue routing behavior and failure handling

Task queues do not route tasks by type. Any Worker polling a task queue can receive any Task on that queue. If a Worker receives a Task for a type it did not register, that Task fails.

Register Workflows and Activities on a Worker

Use register_workflow::<T>() for each Workflow type, which returns a Result requiring error handling with ?. Use register_activities() with an instance, which allows Activities to share state through instance fields. All Workers polling the same Task Queue must register the same Workflow and Activity types, since task queues do not route by type and any Worker can receive any task from the queue.

Create and run a Worker in Rust SDK

To create and run a Worker: (1) instantiate CoreRuntime with RuntimeOptions and TelemetryOptions, (2) create a Connection with ConnectionOptions pointing to the Temporal server address, (3) create a Client with the Connection and namespace, (4) build WorkerOptions with the task queue name, register workflows and activities, (5) create a Worker instance, and (6) call worker.run() which polls until shutdown. The run() method blocks and polls the task queue for work.

Standalone Activity Worker setup - TypeScript

Running a Worker for Standalone Activities is the same as for Workflow Activities. Create a Worker, register the Activity, and run the Worker. The Worker does not need to know whether the Activity will be invoked from a Workflow or as a Standalone Activity. Example: ```typescript import { NativeConnection, Worker } from '@temporalio/worker'; import * as activities from './activities'; import { loadClientConnectConfig } from '@temporalio/envconfig'; async function run() { const config = loadClientConnectConfig(); const connection = await NativeConnection.connect(config.connectionOptions); try { const worker = await Worker.create({ connection, namespace: 'default', taskQueue: 'hello-standalone-activities', activities, }); await worker.run(); } finally { await connection.close(); } } run().catch((err) => { console.error(err); process.exit(1); }); ```

TypeScript Client main topics

The primary client topics in TypeScript SDK documentation are: Temporal Client and Namespaces.

TypeScript SDK Client overview

The TypeScript SDK Client section provides documentation for implementing clients with the TypeScript SDK. Key topics include the Temporal Client and Namespaces.

Namespace operations available

Namespaces can be created, updated, depreciated, deleted, and managed. Details can be retrieved, configuration can be updated, and Namespaces can be deprecated or deleted.

Register Namespace before setting in Temporal Client

A Namespace must be registered with the Temporal Service before it can be set in the Temporal Client.

Namespace management on Temporal Cloud

On Temporal Cloud, Namespaces can be created and managed using the Temporal Cloud UI or tcld commands from the command-line interface.

Namespace management on self-hosted Temporal Service

On self-hosted Temporal Service, Namespaces can be registered and managed using the Temporal CLI (recommended) or programmatically using APIs. Note that these APIs and Temporal CLI commands will not work with Temporal Cloud.

Temporal SDK packages composition

The Temporal TypeScript SDK comprises five main packages: @temporalio/client for communicating with the Temporal Service, @temporalio/worker for Worker Process management, @temporalio/workflow for workflow development, @temporalio/activity for activity authoring, and @temporalio/common for shared utilities.

TypeScript SDK installation with npm

To install the Temporal TypeScript SDK, run 'npm install @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity @temporalio/common'. The project requires Node.js 18 or later.

TypeScript SDK API reference location

The Temporal TypeScript SDK API reference is published at typescript.temporal.io.

Worker.runUntil() for finite workflow execution

Worker.runUntil() executes the provided async function and stops the Worker when it completes. This is useful for scripts that need to execute a specific workflow and then shut down, in contrast to Worker.run() which runs indefinitely.

Connect to Temporal server from TypeScript client

Use Connection.connect({ address: 'localhost:7233' }) to create a connection to a Temporal server. Then create a Client from the connection with the desired namespace parameter.

TypeScript SDK platform documentation sections

The TypeScript SDK platform documentation covers two main areas: Observability and Enriching the UI.

Hello World Client example

Example Client implementation: import { Client, Connection } from '@temporalio/client'; import { nanoid } from 'nanoid'; import { example } from './workflows'; async function run() { const connection = await Connection.connect({ address: 'localhost:7233' }); const client = new Client({ connection, }); const handle = await client.workflow.start(example, { taskQueue: 'hello-world', args: ['Temporal'], workflowId: 'workflow-' + nanoid(), }); console.log(`Started workflow ${handle.workflowId}`); console.log(await handle.result()); } run().catch((err) => { console.error(err); process.exit(1); }); This demonstrates connecting to the Temporal Server, starting a workflow execution, and waiting for the result.

TypeScript SDK Node.js version requirement

The Temporal TypeScript SDK requires Node.js 20 or later.

Install TypeScript SDK packages

To add Temporal to an existing project, install the required packages with: npm install @temporalio/client @temporalio/worker @temporalio/workflow

Create new Temporal TypeScript project

Create a new project with the Temporal SDK using: npx @temporalio/create@latest ./my-app. When prompted to select a sample, choose the hello-world sample.

Start local Temporal Service with CLI

Start a local Temporal Service development server using: temporal server start-dev. This starts the Web UI, creates the default Namespace, uses an in-memory database, and makes the Temporal Service available on localhost:7233. The Temporal Web UI is available at http://localhost:8233.

Change Temporal Web UI port

To change the port for the Web UI when starting the server, use the --ui-port option: temporal server start-dev --ui-port 8080. The Temporal Web UI will then be available at http://localhost:8080.

Worker creation and configuration

A Worker is created using Worker.create() with configuration including: connection (NativeConnection instance), namespace, taskQueue, workflowsPath (path to workflow definitions), and activities (activity implementations). The Worker then calls await worker.run() to start accepting tasks from the queue. Worker logs are written via the Runtime logger to STDERR at INFO level by default.

Hello World Worker example

Example Worker implementation: import { NativeConnection, Worker } from '@temporalio/worker'; import * as activities from './activities'; async function run() { const connection = await NativeConnection.connect({ address: 'localhost:7233', }); try { const worker = await Worker.create({ connection, namespace: 'default', taskQueue: 'hello-world', workflowsPath: require.resolve('./workflows'), activities, }); await worker.run(); } finally { await connection.close(); } } run().catch((err) => { console.error(err); process.exit(1); }); This demonstrates establishing a NativeConnection, registering workflows and activities, and starting the worker to poll the task queue.

Worker polls Task Queue for work

A Worker polls a Task Queue for work to do. Once the Worker dequeues a Workflow or Activity task from the Task Queue, it executes that task. Workers continue running until they encounter an unexpected error or the process receives a shutdown signal.

Client connection configuration

A Temporal Client is created by first establishing a Connection using Connection.connect() with an address (default is localhost:7233). In production, configure TLS and other settings by passing options including address and tls. For TLS with default settings, pass tls: true. For custom TLS configuration, pass a TLSConfig object.

Temporal Client lifecycle

Temporal Clients are not explicitly closed.

Worker versioning configuration in TypeScript

To enable Worker Versioning in TypeScript, pass a Build ID and set useVersioning to true when creating a Worker. The buildId parameter should be assigned from an environment variable or similar configuration. Example: const worker = await Worker.create({ taskQueue: 'your_task_queue_name', buildId: buildId, useVersioning: true, ... });

TypeScript SDK workers documentation structure

The TypeScript SDK workers section includes documentation on worker processes and interceptors. Worker processes explain how to run workers, and interceptors explain how to hook into the worker lifecycle.

Activity interceptor registration

Activity interceptors are registered on Worker creation by passing an array of ActivityInboundCallsInterceptor factory functions through WorkerOptions.interceptors.

Client interceptor registration

Client interceptors are registered on Client construction by passing an array of WorkflowClientInterceptor via ClientOptions.interceptors.

Install ca-certificates with node:slim Docker images

When using node:slim Docker images, install ca-certificates package as they are not included by default. The TypeScript SDK requires root TLS certificates even when connecting to a local Temporal Service or when not using TLS. Failure to install results in a '[TransportError: transport error]' runtime error.

Example: Multi-step Dockerfile for distroless/nodejs

```dockerfile # -- BUILD STEP -- FROM node:20-bullseye AS builder COPY . /app WORKDIR /app RUN npm install --only=production \ && npm run build # -- RESULTING IMAGE -- FROM gcr.io/distroless/nodejs20-debian11 COPY --from=builder /app /app WORKDIR /app CMD ["node", "build/worker.js"] ``` This example demonstrates using distroless/nodejs for smaller Docker images by building in one stage and copying artifacts to a minimal image.

Example: Dockerfile for node:slim with ca-certificates

```dockerfile FROM node:20-bullseye-slim RUN apt-get update \ && apt-get install -y ca-certificates \ && rm -rf /var/lib/apt/lists/* COPY . /app WORKDIR /app RUN npm install --only=production \ && npm run build CMD ["npm", "start"] ``` This example shows how to install ca-certificates when using node:slim Docker images.

Example: Dockerfile for TypeScript SDK Worker with node:20-bullseye

```dockerfile FROM node:20-bullseye COPY . /app WORKDIR /app RUN npm install --only=production \ && npm run build CMD ["npm", "start"] ``` This example shows a basic Dockerfile for deploying a TypeScript SDK Worker using the bullseye image.

Example: Build workflow bundle script

```ts import { bundleWorkflowCode } from '@temporalio/worker'; import { writeFile } from 'fs/promises'; import path from 'path'; async function bundle() { const { code } = await bundleWorkflowCode({ workflowsPath: require.resolve('../workflows'), }); const codePath = path.join(__dirname, '../../workflow-bundle.js'); await writeFile(codePath, code); console.log(`Bundle written to ${codePath}`); } ``` This example demonstrates pre-bundling workflow code during the build step for faster Worker startup in production.

Example: Production Worker with pre-bundled workflows

```ts const workflowOption = () => process.env.NODE_ENV === 'production' ? { workflowBundle: { codePath: require.resolve('../workflow-bundle.js'), }, } : { workflowsPath: require.resolve('./workflows') }; async function run() { const worker = await Worker.create({ ...workflowOption(), activities, taskQueue: 'production-sample', }); await worker.run(); } ``` This example shows conditional workflow loading: using pre-bundled code in production and runtime bundling in development.

Example: Graceful Worker shutdown with shutdownGraceTime

```ts const worker = await Worker.create({ connection, taskQueue: 'my-task-queue', workflowsPath: require.resolve('./workflows'), shutdownGraceTime: '30s', }); await worker.run(); ``` This example demonstrates setting a 30-second grace period for in-progress Activities to complete before Worker shutdown.

Example: Basic Worker setup with NativeConnection

```ts import { NativeConnection, Worker } from '@temporalio/worker'; import * as activities from './activities'; async function run() { const connection = await NativeConnection.connect({ address: 'localhost:7233', }); try { const worker = await Worker.create({ connection, namespace: 'default', taskQueue: 'sleep-for-days', workflowsPath: require.resolve('./workflows'), activities, }); await worker.run(); } finally { await connection.close(); } } run().catch((err) => { console.error(err); process.exit(1); }); ``` This example demonstrates connecting to Temporal, creating a Worker with workflows and activities, and running it with proper connection cleanup.

Do not use Alpine Linux for TypeScript SDK Workers

Alpine replaces glibc with musl, which is incompatible with the Rust core of the TypeScript SDK. Errors like 'Error loading shared library ld-linux-x86-64.so.2' or 'symbol not found for __register_atfork' indicate a musl-based image is being used. Use glibc-based images instead.

Configure Node.js memory limit in Docker with NODE_OPTIONS

Set the --max-old-space-size Node.js argument explicitly through the NODE_OPTIONS environment variable when running in Docker. By default, Node.js sets maximum old-gen memory to 25% of the host's physical memory (up to 4 GB) rather than the container's limit. Set it to roughly 80% of the memory in megabytes you want the process to use.

Use multi-step Dockerfile for distroless/nodejs images

When building TypeScript SDK Workers with distroless/nodejs images, use a multi-step Dockerfile: build the application in a node image first, then copy the built artifacts into the distroless image. Build tools like npm are not in distroless/nodejs images.

Docker image requirements for TypeScript SDK Workers

Use LTS Node.js releases (18, 20, 22, or 24) in Docker. Both amd64 and arm64 architectures are supported. A glibc-based image is required; musl-based images (like Alpine) are not supported due to incompatibility with the Rust core of the TypeScript SDK.

Worker states and lifecycle

A Worker progresses through seven states: INITIALIZED (after Worker.create() succeeds), RUNNING (after worker.run() is called), FAILED (unrecoverable error), STOPPING (shutdown signal received), DRAINING (all Workflow Tasks drained, waiting for Activities and cached Workflows eviction), DRAINED (all Activities and Workflows completed), STOPPED (shutdown complete, worker.run() resolves). Query Worker state with Worker.getState().

Programmatically shutdown a Worker

Call Worker.shutdown() to shut down a Worker programmatically. This is useful in integration tests or when automating a fleet of Workers.

shutdownForceTime for guaranteed Worker shutdown

Set shutdownForceTime on Worker.create() to guarantee the Worker eventually shuts down if you must ensure termination by a deadline.

shutdownGraceTime for graceful Worker shutdown

Set shutdownGraceTime (as a string like '30s') on Worker.create() to give in-progress Activities time to finish before Worker shutdown. As soon as a shutdown signal is received, the Worker stops polling for new Tasks and allows in-flight Tasks to complete until shutdownGraceTime is reached. Any Activities still running after this time are rescheduled by the Temporal Service.

Worker shutdown signals

Workers shut down when the process receives operating system signals: SIGINT, SIGTERM, SIGQUIT, or SIGUSR2. In development, use Ctrl+C (SIGINT) or nodemon (SIGUSR2). Shutdown signals are configurable through the shutdownSignals RuntimeOptions parameter.

Use bundleWorkflowCode for production startup optimization

In production, improve Worker startup time by pre-bundling workflow code. Call bundleWorkflowCode() with workflowsPath during your build step to generate a bundle file. Then pass the bundle to Worker.create() using the workflowBundle option with a codePath parameter.

Use workflowsPath in development

In development, use the workflowsPath option in Worker.create() to specify the location of workflow files. The Worker will bundle the workflow code at runtime.

All Workers on same Task Queue must register same Workflow and Activity Types

All Workers polling the same Task Queue must register the same Workflow Types and Activity Types. A Task Queue does not route by type, so any Worker polling it can receive any Task on that queue. A Worker that receives a Task for a type it did not register fails that Task.

Workflows must be registered by path, not by value

Workflows are registered by path rather than by value because they run in a separate JavaScript context. Use the workflowsPath option in Worker.create() to specify the path to the workflows file.

Create and run a Worker with Worker.create()

Create a Worker using Worker.create(), passing a connection, namespace, task queue name, workflows path or bundle, and activities object. Call await worker.run() to start polling the task queue. The connection should be created with NativeConnection.connect().

AWS Lambda support for Serverless Workers

AWS Lambda support for Serverless Workers is in Public Preview. The TypeScript SDK provides the @temporalio/lambda-worker package to run a Worker as a Lambda function, covering setup, configuration, Lambda-tuned defaults, and observability.

Serverless Workers definition and behavior

Serverless Workers run on ephemeral, on-demand compute rather than long-lived processes. Temporal invokes the Worker when Tasks arrive, and the Worker shuts down when the work is done.

Pre-bundle Workflow code for Lambda

Use workflowBundle with pre-bundled code instead of workflowsPath to avoid webpack bundling overhead on every Lambda cold start. Build the bundle as a separate build step using bundleWorkflowCode({ workflowsPath: require.resolve('./workflows') }) and write it to a file. Then reference it in the handler with workflowBundle: { codePath: require.resolve('./workflow-bundle.js') }.

shutdownDeadlineBufferMs for Lambda Worker

shutdownDeadlineBufferMs is specific to the @temporalio/lambda-worker package and controls how much time before the Lambda deadline the Worker begins graceful shutdown. The default is shutdownGraceTime (5s) plus 2s, totaling 7000ms. If your Worker handles long-running Activities, increase shutdownGraceTime, shutdownDeadlineBufferMs, and the Lambda invocation deadline (--timeout) together.

Give your agent this brain