dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.48k stars 10.03k forks source link

Ihosted service Task hosted in IIS process is not triggering StopAsync while performing IISreset #57248

Open vsfeedback opened 3 months ago

vsfeedback commented 3 months ago

This issue has been moved from a ticket on Developer Community.


[severity:It bothers me. A fix would be nice] I had create a small Ihosted service and deployed into server as IIS. and when I perform the apppool stop or iis reset, I am expecting the stop async to be called but its not happening. Please find below the code snippet and the Server specification.

Server Spec :  Windows Server 2016 Standard 
IIS :
version 10
Site PreLoadEnabled to true
Recycling.periodicRestart.time  = 0
App Pool start up mode to AlwaysRunning
namespace TaskRunIhosted
{
    public class QueueProcessingService : IHostedLifecycleService
    {
        private readonly ITaskLogger _logger;
        private Task _executingTask;

private CancellationTokenSource _stoppingCts = new CancellationTokenSource();

public QueueProcessingService(ITaskLogger messageQueueClient)
        {
            _logger = messageQueueClient;
        }

public async Task StartingAsync(CancellationToken cancellationToken)
        {
            _logger. LogInformation($"StartingAsync .... Performed...");
            await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
        }

public async Task StartAsync(CancellationToken cancellationToken)
        {
            _logger. LogInformation($"StartAsync .... Performed...");
            await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
            // Triggered when the application host is ready to start the service.
            _executingTask = ExecuteAsync(_stoppingCts.Token);

// If the task is completed, return it, otherwise it's still running
            await (_executingTask.IsCompleted ? _executingTask : Task.CompletedTask);
        }

protected async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            // The main background processing logic goes here
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger. LogInformation($"ExecuteAsync .... Performed...");

await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);

//var message = await _logger. ReceiveAsync(stoppingToken);
                //if (message != null)
                //{
                //    ProcessMessage(message);
                //}
            }
            _logger. LogInformation($"IsCancellationRequested .... Performed...");
        }

public Task StartedAsync(CancellationToken cancellationToken)
        {
            _logger. LogInformation($"StartedAsync .... Performed...");

// Logic after StartAsync, e.g., post-initialization logging or actions
            // This is where you might log that the service has successfully started
            return Task.CompletedTask;
        }

public Task StoppingAsync(CancellationToken cancellationToken)
        {
            _logger. LogInformation($"StoppingAsync .... Performed...");

// Logic before StopAsync, e.g., pre-cleanup actions
            // This is where you might log that the service is stopping
            return Task.CompletedTask;
        }

public async Task StopAsync(CancellationToken cancellationToken)
        {
            _logger. LogInformation($"StopAsync .... Performed...");
            await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
            // Triggered when the application host is performing a graceful shutdown.
            _stoppingCts.Cancel();

// Wait until the task completes or the stop token triggers
            await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
        }

public async Task StoppedAsync(CancellationToken cancellationToken)
        {
            _logger. LogInformation($"StoppedAsync .... Performed...");
            await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
            // Logic after StopAsync, e.g., cleaning up the queue
            //await _logger. CleanupAsync(cancellationToken);
        }

//private void ProcessMessage(QueueMessage message)
        //{
        //    // Process the message, e.g., enriching data, sending notifications, etc.
        //}
    }
}

namespace TaskRunIhosted;

public class TaskLogger : ITaskLogger
{
    private readonly ILogger _logger;

public TaskLogger(ILogger<TaskLogger> logger)
    {
        _logger = logger;
    }

public void LogInformation(string message)
    {
        _logger. LogInformation(message);
    }
}

program.cs

using Serilog;
using TaskRunIhosted;

var builder = WebApplication.CreateBuilder();
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
builder. Configuration
    . SetBasePath(AppContext.BaseDirectory)
    . AddJsonFile($"appsettings.{ env}.json", false, true);

builder. Services.AddSingleton<ITaskLogger, TaskLogger>();

builder. Services.AddHostedService<QueueProcessingService>();

Log.Logger = new LoggerConfiguration()
    . WriteTo.Console(). WriteTo
    . File("Logs/logs.txt", rollingInterval: RollingInterval.Day)
    . CreateLogger();

builder. Host.UseSerilog();
var app = builder. Build();

app. Run(); 

Original Comments

Feedback Bot on 8/7/2024, 01:57 AM:

(private comment, text removed)

Babin Bharathan on 8/9/2024, 00:39 AM:

(private comment, text removed)


Original Solutions

(no solutions)

amcasey commented 3 months ago

Possibly related: https://github.com/dotnet/aspnetcore/issues/22831, https://github.com/dotnet/aspnetcore/issues/53894

Babin-AB commented 2 months ago

Any more inputs required for the issue?

Babin A B