Hangfire's background job cancellation token is a mechanism that allows long-running background jobs to be stopped gracefully when a server is shutting down or when a job's state is explicitly changed (e.g., deleted or re-queued).
It is an essential feature for building resilient applications, as it prevents the abrupt termination of tasks, which could lead to inconsistent data or the appearance of ThreadAbortException.
The Hangfire cancellation process
Hangfire's cancellation process is cooperative, meaning the background job itself must actively check the cancellation token's status and respond accordingly. Here is how it works:
- Job method signature: You enable cancellation by adding a
CancellationTokenparameter to your background job method. For older versions of Hangfire, theIJobCancellationTokeninterface was used, but theCancellationTokenclass is now preferred for its asynchronous capabilities. - Hangfire token injection: When you enqueue a job, you can pass
CancellationToken.Noneas a placeholder. During execution, Hangfire automatically replaces this with a live, server-managed cancellation token. - Cancellation events: Hangfire's
BackgroundJobServermonitors for two primary cancellation events:- Server shutdown: When the server process is stopping (e.g., during an application restart), Hangfire signals the cancellation token for all currently executing jobs. This is key to a "graceful shutdown".
- State change: When a job's state is explicitly altered in the Hangfire dashboard or via the
BackgroundJobClient(e.g., a call toBackgroundJob.Delete()orBackgroundJob.Requeue()), Hangfire also cancels the job's token.
- Cooperative cancellation: Inside your job method, you must periodically check the
CancellationTokenfor a cancellation request. A common pattern is to calltoken.ThrowIfCancellationRequested()at key points, such as within a long-running loop or before a resource-intensive operation. - Handling cancellation: When cancellation is requested,
token.ThrowIfCancellationRequested()throws anOperationCanceledException. Hangfire's internal processing logic recognizes this exception as a graceful cancellation. It then updates the job's status in the storage.
Implementation and best practices
To properly use background job cancellation in Hangfire, follow these steps:
1. Modify your job methodAdd a CancellationToken parameter to the method that Hangfire will execute.
public async Task ProcessData(int someParameter, CancellationToken cancellationToken)
{
// ...
}
Use code with caution.
2. Enqueue the jobWhen enqueuing the job, pass CancellationToken.None as the argument for the token. Hangfire handles the rest automatically.
BackgroundJob.Enqueue<IMyService>(x => x.ProcessData(42, CancellationToken.None));
Use code with caution.
3. Implement the cancellation logicInside your job, check for cancellation at appropriate intervals. For asynchronous operations, you can pass the token directly to the awaited method.
public async Task ProcessData(int someParameter, CancellationToken cancellationToken)
{
for (int i = 0; i < 100; i++)
{
cancellationToken.ThrowIfCancellationRequested(); // Check for cancellation
Console.WriteLine($"Processing item {i}...");
await Task.Delay(1000, cancellationToken); // Pass token to async operation
}
}
Use code with caution.
4. Handle server configurationHangfire checks for cancellation periodically by default. You can adjust the frequency of these checks by configuring the CancellationCheckInterval in your server options.
services.AddHangfireServer(options =>
{
options.CancellationCheckInterval = TimeSpan.FromSeconds(1); // Set check interval
});
Use code with caution.
Benefits of using cancellation tokens
- Graceful shutdown: Allows jobs to finish their current work, perform cleanup tasks, and exit cleanly when a server restarts, reducing the risk of data corruption.
- Faster application restarts: Prevents the application from waiting for long-running jobs to complete before shutting down, leading to quicker deployment and scaling operations.
- Reliability: When a job is canceled due to a state change (like being deleted or re-queued), the token mechanism ensures the worker process stops working on the task immediately, avoiding wasted resources.
- Prevents
ThreadAbortException: This exception is generally considered harmful and difficult to handle correctly. Cooperative cancellation helps avoid this issue, as the job exits in a controlled manner.
Distinguishing cancellation token from job state
It's important to understand the difference between a job's state and its execution. Deleting or re-queuing a job changes its state in Hangfire's storage but does not automatically stop a job that is already running. The cancellation token is the vital link that bridges the job's state in storage with its active execution thread, enabling the running job to become aware of the state change and stop processing.
For a running job to be truly canceled, you must change the job's state (e.g., BackgroundJob.Delete()) and ensure the job method contains logic to check and respond to the cancellation token. Without a cooperative cancellation implementation, a running job will simply ignore the state change and continue executing until it naturally completes.