Skip to content

Fix race condition in BackgroundServiceExceptionTests#125570

Open
lewing wants to merge 2 commits intodotnet:mainfrom
lewing:fix/backgroundservice-test-race
Open

Fix race condition in BackgroundServiceExceptionTests#125570
lewing wants to merge 2 commits intodotnet:mainfrom
lewing:fix/backgroundservice-test-race

Conversation

@lewing
Copy link
Member

@lewing lewing commented Mar 15, 2026

Problem

BackgroundService_AsynchronousException_StopAsync_ThrowsException is flaky (3 hits in 24 hours, #125550).

The test starts a BackgroundService that delays 100ms then throws, waits Task.Delay(200ms), and expects StopAsync to surface the exception. On loaded CI agents the 200ms isn't always enough for the exception to propagate through the host, so Assert.Throws sees no exception.

Assert.Throws() Failure: No exception was thrown
Expected: typeof(System.InvalidOperationException)

Fix

Replace the racy Task.Delay(200ms) with deterministic synchronization:

For StopHost behavior tests (StopAsync_ThrowsException, StopTwiceAsync_ThrowsException): Wait for IHostApplicationLifetime.ApplicationStopping, which fires after the host has captured the faulted service exception and called StopApplication(). This is the correct signal because it means the host has processed the exception and is ready to surface it on StopAsync.

For Ignore behavior test (IgnoreException_StopAsync_DoesNotThrow): Use a SignalingFailureService that signals a TaskCompletionSource before throwing. Since this test verifies StopAsync does not throw, we just need to know the service has faulted — no host-level processing is needed.

Both use a 10-second timeout as a safety net for CI.

Fixes #125550

The StopAsync and StopTwiceAsync tests used Task.Delay(200ms) to wait
for a background service (which delays 100ms then throws) to fault.
On loaded CI agents, 200ms is not always enough for the exception to
propagate through the host, causing Assert.Throws to see no exception.

For StopHost behavior tests: wait for ApplicationStopping, which fires
after the host has captured the exception and called StopApplication().

For Ignore behavior test: use a SignalingFailureService that signals a
TaskCompletionSource before throwing, so the test knows the service
has faulted without relying on timing.

Fixes dotnet#125550

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 15, 2026 01:58
@lewing lewing requested a review from svick March 15, 2026 02:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves reliability of Microsoft.Extensions.Hosting unit tests by replacing timing-based delays with deterministic synchronization points when validating background service exception handling.

Changes:

  • Replaced racy Task.Delay(...) waits with IHostApplicationLifetime.ApplicationStopping signaling for StopHost behavior tests.
  • Updated the Ignore behavior test to use a signaling background service (SignalingFailureService) to deterministically coordinate when the failure path is reached.
  • Added 10s WaitAsync timeouts as CI safety nets.

You can also share your feedback on Copilot code review. Take the survey.

@lewing lewing assigned svick and unassigned lewing Mar 15, 2026
@lewing lewing requested a review from cincuranet March 15, 2026 02:03
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-extensions-hosting
See info in area-owners.md if you want to be subscribed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Microsoft.Extensions.Hosting.Tests.BackgroundServiceExceptionTests.BackgroundService_AsynchronousException_StopAsync_ThrowsException failed

3 participants