I've been reading through the MS documentation regarding the deployment of a Windows service to run a Worker App.
The MS code sample talks about the need to add an Environment.Exit(1)
inside the exception handler so that the Windows Service Management can leverage the configured recovery options.
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
while (!stoppingToken.IsCancellationRequested)
{
string joke = _jokeService.GetJoke();
_logger.LogWarning("{Joke}", joke);
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
// Terminates this process and returns an exit code to the operating system.
// This is required to avoid the 'BackgroundServiceExceptionBehavior', which
// performs one of two scenarios:
// 1. When set to "Ignore": will do nothing at all, errors cause zombie services.
// 2. When set to "StopHost": will cleanly stop the host, and log errors.
//
// In order for the Windows Service Management system to leverage configured
// recovery options, we need to terminate the process with a non-zero exit code.
Environment.Exit(1);
}
}
There are a few concepts that are not clear to me and hoping someone can advise:
In my own project, my background services include various classes and operations such as connection managemenet for Azure IOT Hubs Device Client. In some cases, i simply do no want to force the environment i.e. the whole app to exit on every catch/exception scenario, but the docs are not clear on whether we are expceted to do this? I mean why catch an exception if we're going to simply wipe out the running of the application every time? doesnt make sense to me...
The next point ref the following statement "To correctly allow the service to be restarted, you can call Environment.Exit with a non-zero exit code" but then earlier in the article, it also talks about the two options available for 'BackgroundServiceExceptionBehavior':
An unhandled exception in my mind means the app has gone fowl on something that hasnt been appropriately caught in the right place i.e. where no try/catch block exists. So how does one provision an 'Environment.Exit(1)' to something they havent yet accounted for? And what happens in thie scenario?
The way the article reads to me suggests that the only way we can ensure the Windows Service will manage the re-starting of the app succesfully is from any exception that we knowingly caught, but that equally that doesnt tie up with what the general article is suggesting will happen.
Totally confused :(
As described in the article pre .NET 6 an unhandled exception in background service has not affected application in any way - for example if you had an app which only job was to process some queue in background service and this service would fail then the app has continued running as if nothing happened, which obviously is wrong in such cases. That's why it was fixed in .NET 6.
As service recovery options and .NET BackgroundService
instances paragraph states new default behavior for unhandled exceptions is StopHost
which behaves like as if application (Windows service) would exit normally (with exit code 0):
But it stops cleanly, meaning that the Windows Service management system will not restart the service
If you want your app to be restarted automatically by the Windows Service management system (if it is setup to do so) you need to "handle" the exception and end the application with non-zero exit code which indicates failure.
Obviously if some concrete exception is recoverable you need to just recover:
while (!stoppingToken.IsCancellationRequested)
{
try
{
// some job
}
catch (SomeRecoverableException e)
{
// handle it
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
Environment.Exit(1);
}
}
The Ignore
option exists for backward compatibility so it's possible to revert to the previous behaviour (can't think off the top of my head why it is possibly can be needed but still).