Grabofus
Grabofus

Reputation: 2034

Graceful shutdown of IHostedService

I am trying to develop a simple API in .NET Core that allows asynchronous processing of requests.

  1. Request sent to Controller
  2. Work scheduled on background service (IHostedService)
  3. Controller returns 202
  4. Background service performs long running operation

As the application will be running on IIS, a pool recycle can happen after the controller has returned a response. I would like to implement a way to allow the application to do a graceful shutdown - finishing it's currently executing tasks, but not accepting any new.

I am having difficulties having the graceful shutdown run to end. For testing purposes I've simplified the StopAsync() method, so now it only contains an async 20 second wait:

public async Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Hosted service is stopping...");

        try
        {
            // Signal cancellation to the executing method
            _stoppingTokenSource.Cancel();

            _logger.LogInformation("Delay by 20s");
            await Task.Delay(20000, cancellationToken);
            _logger.LogInformation("Delay over");
        }
        finally
        {
            // Await all pending download requests
            await Task.WhenAny(Task.WhenAll(_pendingTasks), Task.Delay(Timeout.Infinite, cancellationToken));

            _logger.LogInformation("Hosted service has been stopped successfully.");
        }
    }

The logs only show show:

Looking at EventViewer I can see 2 events raised for the shutdown with exactly 10 seconds in between them:

I have already tried:

Settings up shutdown timeout on Program.cs

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .UseShutdownTimeout(TimeSpan.FromMinutes(1));

Check Advanced Settings of application pool:

enter image description here

If you have done something similar, could you please let me know if I'm doing something wrong / point me in the right direction?

Thank you!

EDIT

While debugging the application, shutting down with CTRL + C produces the expected behaviour.

Upvotes: 5

Views: 10342

Answers (2)

Grabofus
Grabofus

Reputation: 2034

I've found a solution after having another look at the issue again.

It seems like ASP.NET Core Module in IIS have a separate shutdown time limit, which can be configured in the web.config file as:

    <configuration>
      <system.webServer>
        <handlers>
          <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
        </handlers>
        <aspNetCore
            shutdownTimeLimit="30"    - set timeout here in seconds
            processPath="dotnet"
            arguments=".\GracefulShutdown.dll"
            stdoutLogEnabled="false"
            stdoutLogFile=".\logs\stdout" />
      </system.webServer>
    </configuration>

Reference to settings at: https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/aspnet-core-module?view=aspnetcore-2.1

Ideally this should be available through code, which is still something I'm trying to resolve. If you find a way to do it, please share in comment.

Upvotes: 8

Sukhminder Sandhu
Sukhminder Sandhu

Reputation: 875

In Configure(IApplicationBuilder app, IHostEnvironment env) function Register to ApplicationStopping in IHostApplicationLifetime ( Triggered when the application host is performing a graceful shutdown. Shutdown will block until this event completes)

  var applicationLifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>();
  applicationLifetime.ApplicationStopping.Register(OnShutdown);

private void OnShutdown() { Write your code here that you want before shutdown, you can delay shutdown by Thread.Sleep(); }

Upvotes: 2

Related Questions