-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
Updated by @maryamariyan:
Description:
We want to add a new opt-in option on HostOptions that reacts to failures on a background service.
It generally might not be always desirable to allow for a failure/crash on a background service to stop the application altogether and therefore logging the failure is a default behavior.
API Proposal:
API below would add the option to stop the host on background service exception.
namespace Microsoft.Extensions.Hosting
{
public class HostOptions
{
public bool StopOnBackgroundServiceException { get; set; }
}
}Original Description (click to view)
Description
According to the documentation of the BackgroundService.ExecuteAsync method that method should return a Task that represents the lifetime of the long running operation(s) being performed.
For me that implies that this Task is monitored by the generic host, so I would expect that when the BackgroundService.ExecuteAsync's Task instance is completed (successfully or due to an exception) the host itself would also stop. This is not the case. As a result, a Windows service where the BackgroundService.ExecuteAsync method has crashed or stopped will continue to appear running in the Services Management Console, but will no longer be performing any work., causing all monitoring systems to keep reporting that everything is ok.
Configuration
- .NET Core 3.1.402
- OS Windows 10.0.18363
- Architecture X64
Sample application
The following sample demonstrates the problem. You can either run it on the console or install it as windows service and start it. The program will continue to run after the background service has crashed.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace WorkerTest
{
public static class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
});
}
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private int _counter;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time} ({counter})", DateTimeOffset.Now, _counter++);
if (_counter > 10)
throw new Exception("Something happened!");
await Task.Delay(1000, stoppingToken).ConfigureAwait(false);
}
}
finally
{
if (!stoppingToken.IsCancellationRequested)
_logger.LogError("Worker stopped unexpectedly");
}
}
}
}