Skip to content

Host: StartupTimeout not enforced for hosted services and can break application state #106227

Open
@mus65

Description

@mus65

Description

The host implementation doesn't seem to check StartupTimeout itself, but relies on the service implementations checking the cancellationToken.

We had a situation where this lead to a broken application state. In the reproduction example, the ABackgroundService is cancelled because of the startup timeout, but the application starts successfully anyway. So now the application is running without the background service.

Even without the ABackgroundService, I wouldn't expect the application to start successfully here, because the StartupTimeout was 5s, but the ASlowStartService took 10s to start up.

I realize that the try/catch in ABackgroundService should not be needed. We did this as a workaround for #98935. Either way I could see other applications doing this unintentionally.

Reproduction Steps

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder();
builder.Services.AddHostedService<ABackgroundService>();
builder.Services.AddHostedService<ASlowStartService>();
builder.Logging.AddConsole();

builder.Services.Configure<HostOptions>(o =>
{
  o.StartupTimeout = TimeSpan.FromSeconds(5);
});

var host = builder.Build();
await host.RunAsync();

internal class ABackgroundService(ILogger<ABackgroundService> logger) : BackgroundService
{
  protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  {
    try
    {
      await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
    }
    catch (OperationCanceledException)
    {
      logger.LogInformation("cancelled");
    }
  }
}

internal class ASlowStartService : IHostedService
{
  public async Task StartAsync(CancellationToken cancellationToken)
  {
    await Task.Delay(TimeSpan.FromSeconds(10), CancellationToken.None);
  }

  public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

Expected behavior

Host fails to start.

Actual behavior

Host starts successfully, but BackgroundService is aborted.

info: ABackgroundService[0]
      cancelled
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.

Regression?

No response

Known Workarounds

We worked around this adding cancellationToken.ThrowIfCancellationRequested() in StartedAsync.

Configuration

.NET 8.0.7
Windows x64

Other information

No response

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions