Skip to content

Add a timeout to the NuGet package downloads of the tests - #1308

Merged
meziantou merged 1 commit into
mainfrom
fix-nuget-download-hang
Aug 20, 2026
Merged

Add a timeout to the NuGet package downloads of the tests#1308
meziantou merged 1 commit into
mainfrom
fix-nuget-download-hang

Conversation

@meziantou

Copy link
Copy Markdown
Owner

GetNuGetReferences in tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.cs downloaded the reference packages with GetStreamAsync and read the resulting stream through a ZipArchive. HttpClient.Timeout does not cover reading an unbuffered response stream, and neither DownloadPackageWithRetries nor HttpRetryMessageHandler could recover from a stalled read: they only react to exceptions and 5xx responses. A stalled connection therefore hung the download forever, and because the result is shared through the NuGetPackagesCache Lazy<Task<string[]>>, every test waiting on it hung too.

This happened on CI in this run: the build_and_test (Meziantou.Analyzer.Test.roslyn5.6) job ran 3629 tests successfully, then RemoveEmptyStatementAnalyzerTests.ForStatement (a 0.6s test) was still running after 13 minutes and the hang dump policy killed the run with exit code 7.

Changes

  • The package is now read into a buffer under a CancellationTokenSource timeout covering both the request and the stream read, and only then handed to the ZipArchive. As the archive is opened over a buffer, the per-entry extraction can no longer stall on the network either.
  • The timeout is 60 seconds. With the existing 5-attempt retry loop, a fully stalled connection now fails after about 5 minutes instead of hanging forever, which is well inside the CI hang dump window.
  • IsTransientException now also matches OperationCanceledException and TimeoutException, so the timeout feeds the existing retry loop instead of propagating. There is no outer cancellation token in this path, so this cannot swallow a legitimate cancellation.

Testing

The local NuGet reference cache was moved aside so that the download path actually ran during the test runs.

  • dotnet test tests/Meziantou.Analyzer.Test/Meziantou.Analyzer.Test.roslyn5.9.csproj: 3663 passed, 0 failed, with a cold cache.
  • dotnet test tests/Meziantou.Analyzer.Test/Meziantou.Analyzer.Test.roslyn4.8.csproj: 3533 passed, 0 failed.
  • dotnet run --project src/DocumentationGenerator: exit code 0, no markdown change.

HttpClient.Timeout does not cover reading an unbuffered response stream,
so a stalled connection made GetNuGetReferences hang forever. The result
is shared by all the tests through the NuGetPackagesCache Lazy<Task>, so
every test waiting on it hung too, and neither the retry loop nor
HttpRetryMessageHandler could recover as they only react to exceptions
and 5xx responses.

Download the package to a buffer under a CancellationTokenSource timeout
before opening the ZipArchive, and treat OperationCanceledException and
TimeoutException as transient so the existing retry loop takes over.
@meziantou
meziantou merged commit 0e392b8 into main Aug 20, 2026
12 checks passed
@meziantou
meziantou deleted the fix-nuget-download-hang branch August 20, 2026 00:30
This was referenced Aug 20, 2026
This was referenced Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant