Cancellation token and APIs

Cancellation token and APIs

Why you should start using them in your backend services

kndb's photo
kndb

Published on Nov 20, 2021

3 min read

Ever encountered a situation where you got frustrated with an unresponsive website/app and you hit the refresh button. abort Of course you have. Its 2021 and everyone wants instant gratification.

On the Web, the end user always has the cancel/refresh button at their disposal, and they wont hesitate to use it. But what about the process that was initiated on the server side? How do we as backend devs take care of that, we don't want to keep on processing a cancelled operation and exhaust our resources, do we?

In this post we will take a look at cancellation tokens in .NET and how we can leverage them for situations just like these.

Cancellation tokens

Cancellation tokens or the cancellation model in general belong to the Task Parallel Library. The library provides us a Type called CancellationTokenSource which encapsulates a CancellationToken object within it. CancellationTokenSource also provides us with other extensions to manage a cancelled operation.

On a high level, to make use of the cancellation model we need to do the following

  • Run our logic inside a Task, this would accept a cancellation token
  • We keep on monitoring for cancellation requests by taking a look at the cancellation token and handle them gracefully
  • From outside our logic, some other operation informs our task that a cancellation is requested

Now since we are dealing with APIs in this post, there is no need for us to manage CancellationTokenSource as it is inherently injected by the framework as part of each request. All we have to do is manage the cancellation token.

image.png

Lets take a look at some snippets where we will pass CancellationToken as a parameter to our API endpoint. There is no need to build or manage the CancellationTokenSource.

āš”Make use of framework methods which accept cancellation tokens

[HttpPost("~/longoperation")]
public async Task<ActionResult> AnnoyingLongRunningOperation(CancellationToken token)
{
     try
     {
            _logger.LogInformation("Calling long running operation");
            await Task.Delay(1000000000, token); // our annoying long running task
            _logger.LogInformation("Done with long running operation");
     }
     catch (OperationCanceledException ex)
     {
          // handle ex
          _logger.LogError("Long running operation was cancelled");
     }
     return new OkObjectResult("Done!");
}

After I initiated the request using Postman, I immediately hit cancel and this is how the application behaves. image.png As you can see, Task.Delay() throws an OperationCanceledException as soon as it encounters a cancellation request (it has its own logic to dispose objects etc), and we can handle that exception gracefully in our logic.

Likewise, we can leverage alot of framework methods which already accept cancellation tokens.

āš”Write your own method which accepts a cancellation token

[HttpPost("~/longoperation")]
public async Task<ActionResult> AnnoyingLongRunningOperation(CancellationToken token)
{
    try
    {
        _logger.LogInformation("Calling long running operation");
        await ActualAnnoyingLongRunningOperationAsync(token);
        _logger.LogInformation("Done with long running operation");
    }
    catch (OperationCanceledException ex)
    {
        // handle ex
        _logger.LogError("Long running operation was cancelled");
    }
    return new OkObjectResult("Done!");
}
private async Task ActualAnnoyingLongRunningOperationAsync(CancellationToken token)
{
    for(int i=0;i<100;i+=2)
    {
        _logger.LogInformation($"Loop counter : {i}");
        await Task.Delay(100); // 100 important operations
    }
    token.ThrowIfCancellationRequested();
    // 100 other important operations
}

Again, after I initiated the request using Postman, I immediately hit cancel and this is how the application behaves now.

image.png This time you can see that we have managed cancellation on our own terms, we have made sure the loop runs completely(could represent some necessary backend job etc) before cancellation is processed.

In both the cases, the task that was consuming needless server resources, was disposed from the pool.

Cancellation model is an easy and neat feature we should always look out for implementing in our projects and I hope this post gave you some insights.

Thank you for reading. Cheers!

Take a look at my other post Cancellation token multifold šŸ˜‡ which talks about different mix and matches of cancellation token usage.