Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Service Fabric RunAsync(CancellationToken cancellationToken) does not get cancelled

I figured the RunAsync method takes a CancellationToken as an argument which is great. Unfortunately to my observations I never gets cancelled.

Of course cancelling the RunAsync method and calling OnCloseAsync would be kind of redundant. Still I wonder when (if) the cancellation actually happens.

Am I supposed to write some additional code to provide a working Stop() method in my client? I would have prefered the cancellationToken in RunAsync would actually be cancelled ;-)

My Service Fabric Service code:

/// <summary>
/// This is the main entry point for your service instance.
/// </summary>
/// <param name="cancellationToken">Canceled when Service Fabric needs to shut down this service instance.</param>
protected override async Task RunAsync(CancellationToken cancellationToken)
{
    // TODO: Replace the following sample code with your own logic 
    //       or remove this RunAsync override if it's not needed in your service.

    long iterations = 0;

    while (!cancellationToken.IsCancellationRequested)
    {
        // I commented this out because I want my client to handle the cancellation 
        // event gracefully without throwing an OperationCanceled exception.
        //cancellationToken.ThrowIfCancellationRequested();

        // I never found these messages in any logs. Nor in the diagnostics events window in Visual Studio.
        ServiceEventSource.Current.ServiceMessage(this, "Working-{0}", ++iterations);

        await _client.Start(cancellationToken);

        await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
    }
}

My sample client implementation:

public class Client
{
    private static readonly Logger _logger = LogManager.GetCurrentClassLogger();

    public async Task Start(CancellationToken cancellationToken = default(CancellationToken))
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            _logger.Info("Saying hello from Main Operation.");
            await Task.Delay(3000, cancellationToken);
        }

        _logger.Info("Cancellation requested. Shutting down MainOperation().");
    }

    public void Stop()
    {
        _logger.Info("Stop requested. But I have no means to stop. Not implemented.");
    }
}
like image 727
lapsus Avatar asked Sep 21 '16 07:09

lapsus


1 Answers

Yes, the cancellation token is actually cancelled. It is guaranteed. I can assure you, after years of testing and production use, this is not an oversight.

However there is an oversight in your code.

If you're expecting to see this trace output from your client:

 _logger.Info("Cancellation requested. Shutting down MainOperation().");

You won't, rather it is extremely unlikely you'll ever see it. Why? Because this line before it:

await Task.Delay(3000, cancellationToken);

will throw OperationCanceledException when the cancellation token is signaled during the delay. That will kick you out of the loop and out of RunAsync, so your logging line will not execute.

Since you spend 3 seconds in that delay, and nanoseconds outside of it in the loop, you can see why it is extremely unlikely that cancellation would happen when you're not inside the delay.

like image 88
Vaclav Turecek Avatar answered Sep 23 '22 14:09

Vaclav Turecek