Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 94 additions & 70 deletions docs/develop/dotnet/workers/run-worker-process.mdx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
id: run-worker-process
title: Worker processes - .NET SDK
description: Shows how to run Worker processes with the .NET SDK
sidebar_label: Worker processes
title: Run a Worker - .NET SDK
description: Create and run a Temporal Worker using the .NET SDK.
sidebar_label: Run a Worker
slug: /develop/dotnet/workers/run-worker-process
toc_max_heading_level: 3
tags:
Expand All @@ -11,91 +11,115 @@ tags:
- Worker
---

## Run Worker Process
This page covers long-lived Workers that you host and run as persistent processes.
For Workers that run on serverless compute like AWS Lambda, see [Serverless Workers](/develop/dotnet/workers/serverless-workers).

**How to create and run a Worker Process using the Temporal .NET SDK**
## Create and run a Worker {/* #run-worker-process */}

The [Worker Process](/workers#worker-process) is where Workflow Functions and Activity Functions are executed.
Create a `TemporalWorker` with a Temporal Client and a `TemporalWorkerOptions` that names the Task Queue to poll.
Add the Workflows and Activities the Worker can execute, then call `ExecuteAsync()` to start polling.

- Each [Worker Entity](/workers#worker-entity) in the Worker Process must register the exact Workflow Types and Activity Types it may execute.
- Each Worker Entity must also associate itself with exactly one [Task Queue](/task-queue).
- Each Worker Entity polling the same Task Queue must be registered with the same Workflow Types and Activity Types.
<!--SNIPSTART dotnet-create-worker-->
[features/snippets/worker/worker.cs](https://github.com/temporalio/features/blob/main/features/snippets/worker/worker.cs)
```cs
var options = new TemporalWorkerOptions("my-task-queue");
options.AddWorkflow<GreetingWorkflow>();
options.AddAllActivities(typeof(GreetingActivities), null);

A [Worker Entity](/workers#worker-entity) is the component within a Worker Process that listens to a specific Task Queue.
using var worker = new TemporalWorker(client, options);
await worker.ExecuteAsync(CancellationToken.None);
```
<!--SNIPEND-->

`ExecuteAsync()` takes a `CancellationToken` and polls until that token is cancelled.
The snippet passes `CancellationToken.None`, which never cancels, so the Worker runs until the process exits.
To stop a Worker on demand, pass a token you control instead. See [Shut down a Worker](#shut-down-a-worker).

Although multiple Worker Entities can be in a single Worker Process, a single Worker Entity Worker Process may be perfectly sufficient.
For more information, see the [Worker tuning guide](/develop/worker-performance).
`TemporalWorker` implements `IDisposable`, so declare it with `using` to release its resources when the process exits.

A Worker Entity contains a Workflow Worker and/or an Activity Worker, which makes progress on Workflow Executions and Activity Executions, respectively.
## Register Workflows and Activities {/* #register-types */}

To develop a Worker, create a new `Temporalio.Worker.TemporalWorker` providing the Client and worker options which include Task Queue, Workflows, and Activities and more.
The following code example creates a Worker that polls for tasks from the Task Queue and executes the Workflow.
When a Worker is created, it accepts a list of Workflows, a list of Activities, or both.
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.

Add Workflows with `AddWorkflow<T>()` and Activities with `AddActivity()` or `AddAllActivities()`:

```csharp
// Create a client to localhost on default namespace
var client = await TemporalClient.ConnectAsync(new("localhost:7233")
{
LoggerFactory = LoggerFactory.Create(builder =>
builder.
AddSimpleConsole(options => options.TimestampFormat = "[HH:mm:ss] ").
SetMinimumLevel(LogLevel.Information)),
});
var options = new TemporalWorkerOptions("my-task-queue");
options.AddWorkflow<GreetingWorkflow>();
options.AddWorkflow<OrderWorkflow>();
options.AddAllActivities(new MyActivities(databaseClient));
```

// Cancellation token cancelled on ctrl+c
using var tokenSource = new CancellationTokenSource();
Console.CancelKeyPress += (_, eventArgs) =>
`AddAllActivities()` registers every method marked with `[Activity]`. Pass an instance to register instance methods, which lets Activities share state such as a database client. For a class of static Activity methods, pass the type and `null` instead.

## Connect to Temporal Cloud {/* #connect-to-temporal-cloud */}

To run a Worker against Temporal Cloud, configure the Client connection with your Namespace address and authentication credentials.
See [Connect to Temporal Cloud](/develop/dotnet/client/temporal-client#connect-to-temporal-cloud) for setup instructions.

## Configure Worker options {/* #worker-options */}

`TemporalWorkerOptions` controls concurrency limits, pollers, timeouts, and caching, including `MaxConcurrentActivities`, `MaxConcurrentWorkflowTasks`, and `MaxCachedWorkflows`.
The defaults work for most cases.

To tune these values against real load, see [Worker performance](/develop/worker-performance) and the [Worker tuning reference](/develop/worker-tuning-reference).

## Run a versioned Worker {/* #versioned-worker */}

Set a Worker Deployment Version and enable versioning in `DeploymentOptions`, then set a versioning behavior on each Workflow.

<!--SNIPSTART dotnet-versioned-worker-->
[features/snippets/worker/worker.cs](https://github.com/temporalio/features/blob/main/features/snippets/worker/worker.cs)
```cs
var options = new TemporalWorkerOptions("my-task-queue")
{
tokenSource.Cancel();
eventArgs.Cancel = true;
DeploymentOptions = new WorkerDeploymentOptions(
new WorkerDeploymentVersion("my-app", "1.0"),
useWorkerVersioning: true),
};
options.AddWorkflow<VersionedGreetingWorkflow>();
options.AddAllActivities(typeof(GreetingActivities), null);

// Create an activity instance with some state
var activities = new MyActivities();

// Run worker until cancelled
Console.WriteLine("Running worker");
using var worker = new TemporalWorker(
client,
new TemporalWorkerOptions(taskQueue: "my-task-queue").
AddAllActivities(activities).
AddWorkflow<MyWorkflow>());
try
{
await worker.ExecuteAsync(tokenSource.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Worker cancelled");
}
using var worker = new TemporalWorker(client, options);
```
<!--SNIPEND-->

All Workers listening to the same Task Queue name must be registered to handle the exact same Workflows Types and Activity Types.
Set the behavior per Workflow with `[Workflow(VersioningBehavior = VersioningBehavior.Pinned)]`, or set a default for the whole Worker with `DefaultVersioningBehavior` on `WorkerDeploymentOptions`.
`VersioningBehavior` comes from the `Temporalio.Common` namespace.

If a Worker polls a Task for a Workflow Type or Activity Type it does not know about, it fails that Task.
However, the failure of the Task does not cause the associated Workflow Execution to fail.
A versioning behavior applies only to a Worker that has versioning enabled.
If a Workflow declares one and its Worker does not enable versioning, the server rejects the Workflow Task and the Task retries instead of failing outright.

### Worker Processes with host builder and dependency injection
See [Worker Versioning](/worker-versioning) for the available versioning behaviors and how new versions roll out.

The [Temporalio.Extensions.Hosting](https://github.com/temporalio/sdk-dotnet/tree/main/src/Temporalio.Extensions.Hosting) extension exists for .NET developers to support HostBuilder and Dependency Injection approaches.
## Shut down a Worker {/* #shut-down-a-worker */}

To create the same worker as before using this approach:
To stop a Worker on demand, start it with a token you can cancel yourself instead of `CancellationToken.None`.
Create a `CancellationTokenSource`, pass its `Token` to `ExecuteAsync()`, then cancel the source when the Worker should stop, such as from a `Console.CancelKeyPress` handler.
The Worker stops polling for new Tasks and waits for in-flight Tasks to finish, up to `GracefulShutdownTimeout`.

```csharp
var host = Host.CreateDefaultBuilder(args)
.ConfigureLogging(ctx => ctx.AddSimpleConsole().SetMinimumLevel(LogLevel.Information))
.ConfigureServices(ctx =>
ctx.
// Add the database client at the scoped level
AddScoped<IMyDatabaseClient, MyDatabaseClient>().
// Add the worker
AddHostedTemporalWorker(
clientTargetHost: "localhost:7233",
clientNamespace: "default",
taskQueue: "my-task-queue").
// Add the activities class at the scoped level
AddScopedActivities<MyActivities>().
AddWorkflow<MyWorkflow>())
.Build();
await host.RunAsync();
<!--SNIPSTART dotnet-worker-graceful-shutdown-->
[features/snippets/worker/worker.cs](https://github.com/temporalio/features/blob/main/features/snippets/worker/worker.cs)
```cs
using var tokenSource = new CancellationTokenSource();
Console.CancelKeyPress += (_, eventArgs) =>
{
tokenSource.Cancel();
eventArgs.Cancel = true;
};

var options = new TemporalWorkerOptions("my-task-queue")
{
GracefulShutdownTimeout = TimeSpan.FromSeconds(30),
};
options.AddWorkflow<GreetingWorkflow>();

using var worker = new TemporalWorker(client, options);
await worker.ExecuteAsync(tokenSource.Token);
```
<!--SNIPEND-->

`ExecuteAsync()` throws `OperationCanceledException` once the Worker stops, so catch it where you want the process to exit cleanly.
See [Worker shutdown](/encyclopedia/workers/worker-shutdown) for what happens to in-flight Workflow Tasks and Activities.
12 changes: 9 additions & 3 deletions docs/develop/go/workers/run-worker-process.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@ gow run worker/main.go

## Register Workflows and Activities {/* #register-types */}

All Workers listening to the same Task Queue must be registered to handle the same Workflow Types and Activity Types.
If a Worker polls a Task for a type it does not know about, the Task fails. The Workflow Execution itself does not fail.
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.

Use `RegisterWorkflow()` and `RegisterActivity()` to register types.
To register an Activity struct with multiple methods, pass the struct. The Worker gets access to all exported methods.
Expand Down Expand Up @@ -127,11 +128,16 @@ w.RegisterWorkflowWithOptions(HelloWorkflow, workflow.RegisterOptions{
```
<!--SNIPEND-->

Set the behavior per Workflow with `RegisterWorkflowWithOptions()`, or set a default for every Workflow on the Worker with `DefaultVersioningBehavior` in `worker.DeploymentOptions`.

A versioning behavior applies only to a Worker that has versioning enabled, and setting `DefaultVersioningBehavior` without `UseVersioning` is an error.
With versioning enabled and no default set, a Workflow that does not set its own behavior fails at registration time.

See [Worker Versioning](/worker-versioning) for the available versioning behaviors and how new versions roll out.

## Shut down a Worker {/* #shut-down-a-worker */}

A Worker started with `Run(worker.InterruptCh())` shuts down when the process receives `SIGINT` or `SIGTERM`.
It stops polling for new Tasks and waits for in-flight Tasks to finish, up to the `WorkerStopTimeout` set in `worker.Options`.
The Worker stops polling for new Tasks and waits for in-flight Tasks to finish, up to the `WorkerStopTimeout` set in `worker.Options`.

See [Worker shutdown](/encyclopedia/workers/worker-shutdown) for what happens to in-flight Workflow Tasks and Activities.
Loading