Table of Contents

Background server methods

Background server methods are a special type of server method that run in the background in a separate process. To indicate that a server method executes in background, check 'Background execution' in ' Advanced options / Background execution'.

As execution is deferred, a method of this type cannot return a result and must therefore return void or Task.

Task runner

To enable server methods to run in the background, a complementary project server{RootNamespace}.TaskRunner{RootNamespace}.TaskRunner.csproj needs to be generated. This is responsible for running tasks in the background.

In order not to unnecessarily burden a cluster that does not use background server methods, this project is not generated by default. To generate it, you need to add the line below to the cluster configuration:

TaskRunnerExecutionMode: IsolatedProcess

If you don't add it and there are background server methods in your cluster, an error will be issued by the generation asking you to add this line.

Note

In development mode with the neos run command, the task runner is not started automatically. It is launched only when a background server method is triggered for the first time.

There are two execution modes for the task runner:

  • BackgroundService: the task runner is implemented as a background service in the task runner process. This is the default mode.
  • DaprWorkflow: the task runner is implemented with Dapr workflows. This mode allows to benefit from Dapr workflow features such as retries, error handling. but it requires the Dapr placement control plane service to be running.

BackgroundService execution mode

This is the default execution mode for background server methods.

For the full documentation of this execution mode, including configuration settings and best practices, see BackgroundService execution mode.

DaprWorkflow execution mode

Note

To use DaprWorkflow execution mode, you need to set BackgroundTaskExecution:EnableDaprWorkflow to true in the dotnet configuration.
In production set the secret BackgroundTaskExecution__EnableDaprWorkflow to true and restart TaskRunner instances, in development set the environment variable BackgroundTaskExecution__EnableDaprWorkflow to true before the neos run command.

Warning

Dapr-specific features for background server methods, such as workflow-based retries and error handling, require DaprWorkflow mode and the Dapr placement service.

Dapr Placement control plane service

This service is required for Dapr workflow execution.
In development mode, it starts automatically before the first Task runner service. To function properly, ports 6050 and 8080 must be available on the machine.

If port 6050 is already in use, you can specify a different one by setting the environment variable NEOS_PLACEMENT_PORT with the desired port number.
If port 8080 is already in use, you can specify a different one by setting the environment variable NEOS_PLACEMENT_HEALTHZ_PORT with the desired port number.

Sequence diagram for triggering a background server method

The diagram below explains what happens when a background server method is called.

sequenceDiagram
  autonumber

  actor Client
  participant Backend as MyCluster backend
  participant TaskRunner as MyCluster task runner
  participant Dapr as Dapr sidecar

  rect rgb(240, 240, 240)
  Client->>Backend: Requests background server method execution
  Note right of Client: POST https://localhost/neos/MyCluster/webapi/MyLongLastingTask
  Backend->>Backend: Publishes event StartBackgroundServerMethod
  Note right of Backend: [{ "clusterName": "MyCluster", "serverMethodName": "MyLongLastingTask", "Arguments": {...} }]
  Backend->>Client: Responses NoContent 204
  end

  rect rgb(240, 240, 240)
  TaskRunner->>TaskRunner: Event StartBackgroundServerMethod received (pub/sub retry policies apply here)
  Note left of TaskRunner: Check the cluster name to ensure that the message concerns MyCluster
  TaskRunner->>Dapr: Schedules workflow MyLongLastingTaskWorkflow
  end

  rect rgb(240, 240, 240)
  Dapr->>TaskRunner: Runs workflow MyLongLastingTaskWorkflow
  Note right of TaskRunner: Server method retry policies apply here
  end

Retry policies

In ' Advanced options / Background execution', you can select a retry policy.

Note

To edit retry policies, display the list mode in Neos Studio on the left and click on Backend / Background server methods retry policies.

This retry policy applies to the background method code and is a wrapper around Dapr workflow retry policies.

This should not be confused with pub/sub retry policies. When a background server method is called, pub/sub retry policy applies to the StartBackgroundServerMethod subscription. Background server methods retry policy applies to the execution of background method code.

Error handling method

If you don't set a retry policy, you can handle errors with a try/catch in the code of your background server method. If you do set one, however, you won't be able to know that the current try is the last one to perform a particular action.

If you want to perform a particular handling when the last attempt fails, you need to enter in ' Advanced options / Background execution / Error handling method name' the name of a server method that will be called when the last attempt fails.

Tip

For a complete example showing how to coordinate retry policies, error handling methods, and persistent state management in a real-world workflow, see Build a complete background processing workflow.

The accepted parameters for this method are :

  • errorType (type string or string?)
  • errorMessage (type string or string?)
  • stackTrace (type string or string?)
  • All parameters of the background server method with their exact name and type

None of these parameters are mandatory and they can be in any order.

Let's take a background server method MyLongLastingTask defined as follows:

public interface IMyLongLastingTask
{
    Task ExecuteAsync(string paramA, int paramB, DateTime paramC);
}

The Task ExecuteAsync(string paramA, int paramB, DateTime paramC, string errorMessage) method is a valid error handling method because:

  • paramA, paramB and paramC are parameters of MyLongLastingTask.
  • errorMessage is a valid additional parameter containing the error text.

The Task ExecuteAsync(string errorMessage, string paramA) method is also a valid error handling method because:

  • errorMessage is a valid additional parameter containing the error text.
  • paramA is a parameter of MyLongLastingTask.

The Task ExecuteAsync(string errorMessage, char paramA) method is an invalid error handling method because:

  • paramA is a parameter of MyLongLastingTask but its type is not correct.

The Task ExecuteAsync(string errorMessage, string userId) method is an invalid error handling method because:

  • userId is not a MyLongLastingTask parameter.

Calling background server method by code

If a background server method MyLongLastingTask is defined like this:

namespace MyCluster.Application.Abstractions.Methods
{
    public interface IMyLongLastingTask
    {
        Task ExecuteAsync(string paramA, int paramB, DateTime paramC);
    }
}

You will not be able to inject the MyCluster.Application.Abstractions.Methods.IMyLongLastingTask interface into your business code classes executed by the backend.

If you try to do this, you will get the following error at runtime:

System.NotSupportedException: Calling background server methods is not allowed in this context. You should use MyCluster.Application.Abstractions.MethodRunners.IMyLongLastingTaskRunner.

This prohibition exists to prevent a background server method from being executed directly and not in the background.

If you want to call a background server method from your business code (and have it run in background), you need to inject MyCluster.Application.Abstractions.MethodRunners.IMyLongLastingTaskRunner into your business code class.

IMyLongLastingTaskRunner is used like IMyLongLastingTask except that the method to call is named StartAsync instead of Execute[Async].

Sample call:

await _myLongLastingTaskRunner.StartAsync(paramA: "ABC", paramB: 10, paramC: DateTime.UTCNow);

An optional parameter serverMethodStartOptions (of type GroupeIsa.Neos.Application.MethodRunners.ServerMethodStartOptions) is also available with the following properties:

  • Identifier: this identifier when set is used as the instance identifier for the Dapr workflow and as a key in the emitted events (see below). This identifier must be unique and RFC 1123 subdomain compatible. It is recommended that the identifier contains a guid (examples: 9910ee3d-3c72-4d06-8fcf-3fce6001006a, MyLongRunningTask.9910ee3d-3c72-4d06-8fcf-3fce6001006a).
  • StartupTimeout: if this duration is specified and it is reached before the server method is started by the task runner, the launch is cancelled.
  • Timeout: if this duration is specified and it is reached before the end of the server method execution, the launch is cancelled. To cancel a server method that has already started, it must have a correctly used cancellationToken parameter.
Warning

Identifier must be a lowercase RFC 1123 subdomain. It consists of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)

Sample call with options:

await _myLongLastingTaskRunner.StartAsync(paramA: "ABC", paramB: 10, paramC: DateTime.UTCNow, new ServerMethodStartOptions(identifier: "9910ee3d-3c72-4d06-8fcf-3fce6001006a"));
Note

When a background server method is called by API, it is systematically executed in background via a call to the runner.

Triggering background server methods via Pub/Sub subscriptions

Background server methods can be triggered via Pub/Sub subscriptions, allowing event-driven execution of long-running tasks.

To configure a background server method to be triggered by a Pub/Sub event:

  1. In Neos Studio, open your background server method
  2. Click on "Advanced options" button in the toolbar
  3. Select the "Subscription" tab
  4. Turn the "Subscribe to an event" switch on
  5. Enter the name of the event in "Subscribed event name"

When the specified event is published, the task runner will automatically start the background server method in the background.

Execution flow

The diagram below shows what happens when a Pub/Sub event triggers a background server method:

sequenceDiagram
  autonumber

  participant Publisher as Publisher (any cluster)
  participant Dapr as Dapr sidecar
  participant TaskRunner as MyCluster task runner
  participant DaprWorkflow as Dapr workflow engine

  rect rgb(240, 240, 240)
  Publisher->>Dapr: Publishes event MyEvent
  Note right of Publisher: await eventPublication.PublishEventAsync("MyEvent", data)
  end

  rect rgb(240, 240, 240)
  Dapr->>TaskRunner: Event MyEvent received (pub/sub retry policies apply here)
  TaskRunner->>DaprWorkflow: Schedules workflow MyBackgroundMethodWorkflow
  TaskRunner->>Dapr: Returns NoContent 204
  end

  rect rgb(240, 240, 240)
  DaprWorkflow->>TaskRunner: Runs workflow MyBackgroundMethodWorkflow
  Note right of TaskRunner: Server method retry policies apply here
  end

Multiple background methods subscribing to the same event

Multiple background server methods can subscribe to the same Pub/Sub event. When this happens, they are started sequentially in a specific order:

  1. First, by module hierarchy (as defined by module dependencies)
  2. Then, by server method name (alphabetically)

This execution order ensures predictable behavior when multiple background tasks need to be started in response to the same event.

Important

When multiple background server methods subscribe to the same event, they must have consistent properties:

  • All must have the same value for Publish message without envelop (PubSubRawPayload)

If inconsistent properties are detected, a generation error will occur.

Note

Unlike regular server methods (which execute synchronously), each background server method is started sequentially, but they then run in parallel as separate Dapr workflows.

Retry policies

When a background server method is triggered via Pub/Sub:

  • Pub/Sub retry policy applies to the event reception and workflow scheduling
  • Background server method retry policy applies to the execution of the background method code

For more information about retry policies, see the Retry policies section and Pub/Sub retries.

Automatic start-up of task runner in development mode

The diagram below explains the task runner startup flow in development mode. The Neos Studio backend is subscribed to the StartBackgroundServerMethod subscription and on receiving it issues an Api call to the proxy server to start the task runner.

sequenceDiagram
  autonumber

  actor Client
  participant Backend as MyCluster backend
  participant DesignerBackend as Neos Studio backend
  participant ServerProxy as Server proxy
  participant TaskRunner as MyCluster task runner

  rect rgb(240, 240, 240)
  Client->>Backend: Requests background server method execution
  Note right of Client: POST https://localhost/neos/MyCluster/webapi/MyLongLastingTask
  Backend->>Backend: Publishes event StartBackgroundServerMethod
  Note right of Backend: [{ "clusterName": "MyCluster", "serverMethodName": "MyLongLastingTask", "Arguments": {...} }]
  Backend->>Client: Response OK 200
  end

  rect rgb(240, 240, 240)
  DesignerBackend->>DesignerBackend: Event StartBackgroundServerMethod received
  Note left of DesignerBackend: Check the cluster name to ensure that the message concerns the edited cluster MyCluster
  DesignerBackend->>ServerProxy: Instructs the proxy to start TaskRunner
  Note right of DesignerBackend: POST admin/v1/backend/StartNewTaskRunner
  end

  rect rgb(240, 240, 240)
  ServerProxy->>TaskRunner: Starts process
  end

Published events

Executing a method in the background triggers an event named BackgroundServerMethodProgress. The event data is a ProgressMessage.

ExecutionIdentifier is the identifier provided in the serverMethodStartOptions parameter when launching the background server method. If no value was provided, it will contain the identifier auto-generated by Dapr for the workflow (a guid).

The table below lists the different states with details on when they are triggered:

State Trigger
Created An event with the state Created is triggered when the task runner has scheduled the workflow in Dapr
Started An event with the state Started is triggered when the Dapr workflow starts
InProgress An event with the state InProgress is triggered only by the business code
Succeeded An event with the state Succeeded is triggered when the Dapr workflow completes in successfully
Failed An event with the state Failed is triggered when workflow scheduling has failed or when the Dapr workflow completes in error

How to trigger an event with the state InProgress

You can emit additional events in the business code during the execution of the method using the IServerMethodProgress service. These events will have the status InProgress and can contain a message and/or a percentage of progress.

Sample call:

public class MyServerMethod : IMyServerMethod
{
    private readonly IServerMethodProgress _progress;

    public MyServerMethod(IServerMethodProgress serverMethodProgress)
    {
        _progress = progress;
    }

    public async Task ExecuteAsync()
    {
        await _progress.ReportAsync(message: "Task is starting...");

        for (int i = 0; i < 10; i++)
        {
            await Task.Delay(1000);
            await _progress.ReportAsync(percentProgress: i * 10, message: "Task in progress...");
        }
    }
}

How to get more information about the failure when the state is Failed

On failure messages you can find the error type and stack trace in the additional data:

string errorType = message.AdditionalData.GetValueOrDefault("ErrorType", string.Empty);
string stackTrace = message.AdditionalData.GetValueOrDefault("StackTrace", string.Empty);

Purge

Each background server method execution saves its state in the configured state store (more information). Workflow actor state will remain in the state store even after a workflow has completed.

This state can be read with IBackgroundServerMethodManager.GetStateAsync.

It is possible to manually purge the state on an execution by calling IBackgroundServerMethodManager.RequestPurgeAsync.

Note

IBackgroundServerMethodManager.GetStateAsync returns a response with Exists = false when the state of a purged instance is requested.

Warning

Creating a large number of workflows could result in unbounded storage usage. To prevent this, you can manually call IBackgroundServerMethodManager.RequestPurgeAsync but it is not easy to find the right event to call it and remember to do it systematically. For this reason, the Task Scheduler cluster (when deployed) will automatically schedules the purge 10 minutes after the end of the execution of a background server method (even if it was triggered manually).

Idempotency

Every background method must be written to be safely re-executed.

A background method can run more than once for reasons outside your control: automatic retry after a failure, a user manually relaunching a stuck operation, or a TaskRunner process that restarted mid-execution and replayed the workflow. In any of these cases, part or all of the work may have already been completed in a previous attempt.

If the method is not idempotent, the second run can silently create duplicates, corrupt aggregates, or repeat side effects (emails sent twice, accounting entries doubled, external API calls replayed) with severe and hard-to-reverse business consequences.

Warning

Non-idempotent background methods are a data integrity risk. The impact may not be visible immediately and can surface much later, making it difficult to diagnose and repair.

Common techniques:

  • Check-before-write: before inserting a record, verify it does not already exist; skip or update instead of inserting again.
  • Upsert: replace blind inserts with upserts keyed on a stable business identifier.
  • Idempotency key on external calls: pass a stable key to external APIs so they can deduplicate on their side.
  • Conditional state guards: write only when the target state matches the expected precondition, so a second run that arrives late does not execute.

For a detailed treatment with implementation patterns, see Build a complete background processing workflow.

Deployment

To enable background execution of server methods in production, you will need to:

  1. Provide a Docker image of the task-runner.
  2. Configure it in Helm chart yaml configuration file.

See also