BlogCancelling CMS Scheduled Jobs

Cancelling CMS Scheduled Jobs

OptimizelyCMSScheduled Jobs
16 July 2025

From flags to tokens: making Optimizely CMS scheduled jobs more elegant with .NET cancellation tokens.

A finger is about to press the "Cancel" button.

Cancelling a scheduled job in Optimizely CMS is easy. At least that is what I have always been assuming. Over the years, I’ve written numerous scheduled jobs, many of which support cancellation. Yet, I never gave much thought to the mechanics of cancelling these jobs. Typically, my solution was to simply pass a boolean flag indicating whether a user had triggered a cancellation, wrapped in an action that returned the current flag state. While this approach works, I recently realized it might not be the most optimal option.

The solution was always there, but I never thought of using it - cancellation tokens. I had encountered them before, mostly when dealing with API endpoints, where they’re automatically injected into every asynchronous endpoint. I’d pass them through the layers, ending with sending them to another external API. But I have never created a single cancellation token myself. But it turns out it might be used to more gracefully cancel a scheduled job.

How to create a cancellation token?

private CancellationTokenSource? _cancellationTokenSource;

public override string Execute()
{
    _cancellationTokenSource = new CancellationTokenSource();

    try
    {
        var cancellationToken = _cancellationTokenSource?.Token ?? CancellationToken.None;
        var result = AsyncHelper.RunSync(() => 
            _jobService.DoJobTaskAsync(OnStatusChanged, cancellationToken));

        return result.ToString();
    }
    catch (Exception e)
    {
        _logger.LogError(e, "Our fantastic scheduled job failed due to an unexpected error.");
        throw;
    }
    finally
    {
        _cancellationTokenSource?.Dispose();
        _cancellationTokenSource = null;
    }
}

The token is not created directly but with the CancellationTokenSource class, which acts as a manager and handles the lifecycle of the token. The important aspect is to make sure the cancellation is actually triggered when a user requests it, by setting the cancellation token’s cancellation state in the job’s Stop method. Here, the CancellationTokenSource also comes in handy:

public override void Stop()
{
    _cancellationTokenSource?.Cancel();
}

How to use a cancellation token?

The main idea is to pass the cancellation token through all the layers that should respond to cancellation. When processing large amounts of data, for example, it’s possible to either:

  • check if the token has been canceled

if (cancellationToken.IsCancellationRequested)
{
    return result;
} 

  • Or execute a method ThrowIfCancellationRequested

cancellationToken.ThrowIfCancellationRequested(); 

and catch the produced exception at the top level

catch (OperationCanceledException ex)
{
    _logger.LogWarning(ex, "Our fantastic scheduled job has been canceled");
} 

Which way is better depends on the actual case. So far, I’ve been preferring the IsCancellationRequested approach, as it gives more control and allows me to return the result with partially processed data statuses. However, I can see the second option being useful when the result report is not that important, or it might be constructed at the top level when catching the exception.

In summary, using .NET cancellation tokens provides a good way to handle cancellations in Optimizely CMS scheduled jobs. This not only makes the code cleaner but also ensures a more graceful shutdown when users demand it.

More articles

Real life color picker
OptimizelyContent GraphHeadless

Exposing Color Picker to Content Graph

A guide on how to consume custom CMS property backing types in a headless architecture, using a color picker as an example.

Rubik's Cube as metaphor for hard problems to solve.
OptimizelyHeadlessError

Getting 404 when expecting 401

A short story about the mysterious behavior of an API in headless architecture.

Bunch of decimal numbers
OptimizelyGraphCommerce.NET

Decimal numbers in Optimizely Graph

Storing prices as decimal numbers on a commerce website and planning to expose them through Optimizely Graph? It might not be as straightforward as it seems.