Getting Started
Architecture
Transports
Persistence
ServiceInsight
ServicePulse
ServiceControl
Monitoring
Modernization
Samples

Scheduling

Component: NServiceBus
NuGet Package: NServiceBus (7.x)
Standard support for version 7.x of NServiceBus has expired. For more information see our Support Policy.

The NServiceBus Scheduler is a lightweight, non-durable API for scheduling tasks to execute at specified intervals. To leverage NServiceBus features such as built-in retries and error queue forwarding, scheduled tasks should only Send or SendLocal a single message to perform the actual work. For example, you might query a database for orders requiring action and emit individual messages for each order found.

How the scheduler works

The scheduler maintains a non-durable, in-memory dictionary of scheduled tasks per endpoint instance.

When a new task is scheduled:

  • It receives a unique ID and is stored in the endpoint's in-memory dictionary.
  • The ID is sent in a message to the timeout manager, which defers the message for the specified interval.
  • When the interval elapses, the dispatcher returns the message to the endpoint, which uses the ID to fetch and execute the task.

Example usage

The following examples show how to schedule a task, with and without a name for logging purposes:

// To send a message every 5 minutes
await endpointInstance.ScheduleEvery(
        timeSpan: TimeSpan.FromMinutes(5),
        task: pipelineContext =>
        {
            // use the pipelineContext parameter to send messages
            var message = new CallLegacySystem();
            return pipelineContext.Send(message);
        });

// Name a schedule task and invoke it every 5 minutes
await endpointInstance.ScheduleEvery(
        timeSpan: TimeSpan.FromMinutes(5),
        name: "MyCustomTask",
        task: pipelineContext =>
        {
            log.Info("Custom Task executed");
            return Task.CompletedTask;
        });

When not to use the Scheduler

Consider alternatives if:

  • The task includes branching or business logic (use a saga and saga timeouts).
  • Polling can be replaced by publishing an event when a state transition occurs.
  • You need features not supported by the Scheduler API, such as scaling out, canceling, or deleting scheduled tasks, or side-by-side deployments.

Current limitations

  • The scheduler is non-durable. After a process restart, all scheduled tasks are recreated with new IDs. Tasks scheduled before the restart that arrive at the queue will not be found, though a log entry is written.
  • Scheduled tasks cannot be canceled or modified after creation.
  • The scheduler supports specifying a repeat interval, but not a specific execution time.
  • Tasks are executed as part of the incoming message pipeline, with a maximum duration limited by the receive transaction timeout.
  • If a task's execution time exceeds the configured interval, the same action may run concurrently. Non-thread-safe actions must handle synchronization, for example, using a semaphore.
  • The scheduler's reliance on the queuing mechanism means there can be slight delays in task execution, especially in high-load systems or when using transports without native delayed delivery.
  • The Scheduler API does not support scaling out the endpoint or side-by-side deployments. In scenarios with multiple endpoint instances (e.g., on the same machine with MSMQ or RabbitMQ), all instances share the same input queue. Since each endpoint maintains its own tasks in memory, a message dequeued by an endpoint instance that did not create the task will result in the task not being found.

Exception handling

When an exception is thrown inside a schedule callback, the exception will be logged as an error and the endpoint will not shutdown.

Samples