[Prometheus.HttpListener] Fix shutdown hang - #7587
Conversation
Avoid disposal race in `PrometheusHttpListener` that can cause shutdown to hang indefinitely.
Pull request dashboard statusMerged · refreshed 2026-08-04 21:27 UTC Status above doesn't look right?
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7587 +/- ##
==========================================
- Coverage 90.51% 90.49% -0.02%
==========================================
Files 290 290
Lines 16029 16039 +10
==========================================
+ Hits 14509 14515 +6
- Misses 1520 1524 +4
Flags with carried forward coverage won't be shown. Click here to find out more.
|
|
6 runs with no Prometheus related failures. There was one failure here on attempt 5, but that has no specific cause logged but appears to be within OpenTelemetry.Tests. |
There was a problem hiding this comment.
Pull request overview
This PR addresses a shutdown hang in the Prometheus HttpListener exporter by bounding shutdown waits and ensuring in-flight scrapes fail fast (HTTP 503) once disposal begins, with accompanying test hardening to avoid race-driven flakiness.
Changes:
- Add bounded timeouts to listener shutdown/drain logic and log when the processing loop fails to stop promptly.
- Ensure scrapes that are still collecting when disposal starts return HTTP 503 instead of completing with a successful response.
- Harden integration/unit tests by adding explicit scrape/promtool timeouts and avoiding thread-pool scheduling races during disposal.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PromToolFixture.cs | Adds scrape and overall promtool execution timeouts to prevent hangs in container-based validation. |
| test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs | Makes the disposal-vs-scrape race test deterministic (thread-pool availability + dedicated disposal thread). |
| src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs | Implements bounded shutdown waits and returns 503 when disposal begins mid-collection. |
| src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md | Adds Unreleased entries describing the behavioral changes (needs PR link fixups). |
Suppressed comments (1)
src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md:24
- This CHANGELOG entry still uses a placeholder PR reference (#TODO). Please update it to the real PR link before merging.
([#TODO](https://github.com/open-telemetry/opentelemetry-dotnet/pull/TODO))
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Add PR number.
….0 (#1316) Updated [OpenTelemetry.Exporter.OpenTelemetryProtocol](https://github.com/open-telemetry/opentelemetry-dotnet) from 1.17.0 to 1.18.0. <details> <summary>Release notes</summary> _Sourced from [OpenTelemetry.Exporter.OpenTelemetryProtocol's releases](https://github.com/open-telemetry/opentelemetry-dotnet/releases)._ ## 1.18.0 For highlights and announcements pertaining to this release see: [Release Notes > 1.18.0](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/RELEASENOTES.md#1180). The following changes are from the previous release [1.17.0](https://github.com/open-telemetry/opentelemetry-dotnet/releases/tag/core-1.17.0). * NuGet: [OpenTelemetry v1.18.0](https://www.nuget.org/packages/OpenTelemetry/1.18.0) * Fixed self-diagnostics log lines being silently dropped when an event message or parameter contained enough 3-byte UTF-8 characters to overflow the internal buffer estimate. Such content is now truncated. ([#7543](open-telemetry/opentelemetry-dotnet#7543)) * Fixed activity creation throwing when multiple tracer providers return a sampler attribute with the same key. ([#7558](open-telemetry/opentelemetry-dotnet#7558)) * Added the `otel.sdk.processor.log.processed` SDK self-observability metric. ([#7486](open-telemetry/opentelemetry-dotnet#7486)) * Added the `otel.sdk.processor.span.processed` SDK self-observability metric. ([#7598](open-telemetry/opentelemetry-dotnet#7598)) * `BatchActivityExportProcessor` and `SimpleActivityExportProcessor` no longer forward spans to the exporter once `Shutdown` has been called, and `BatchActivityExportProcessor.Shutdown` now waits for in-flight `OnEnd` calls to finish enqueueing before flushing. ([#7598](open-telemetry/opentelemetry-dotnet#7598)) * Fix logger, meter and tracer providers leaking background threads if an exception is thrown by their constructor after resource creation. ([#7615](open-telemetry/opentelemetry-dotnet#7615)) * `CircularBufferBuckets.Copy` optimized to use bulk array copies. ([#7670](open-telemetry/opentelemetry-dotnet#7670)) * Restored configured `MaxScale` after delta exponential histogram collection. ([#7671](open-telemetry/opentelemetry-dotnet#7671)) See [CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.18.0/src/OpenTelemetry/CHANGELOG.md) for details. * NuGet: [OpenTelemetry.Api v1.18.0](https://www.nuget.org/packages/OpenTelemetry.Api/1.18.0) * Avoid formatting exceptions and creating exception attributes when `RecordException` is called on a span that is not recorded. ([#7669](open-telemetry/opentelemetry-dotnet#7669)) See [CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.18.0/src/OpenTelemetry.Api/CHANGELOG.md) for details. * NuGet: [OpenTelemetry.Api.ProviderBuilderExtensions v1.18.0](https://www.nuget.org/packages/OpenTelemetry.Api.ProviderBuilderExtensions/1.18.0) No notable changes. See [CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.18.0/src/OpenTelemetry.Api.ProviderBuilderExtensions/CHANGELOG.md) for details. * NuGet: [OpenTelemetry.Exporter.Console v1.18.0](https://www.nuget.org/packages/OpenTelemetry.Exporter.Console/1.18.0) * Added support for serializing attribute values that are key/value lists (`IEnumerable<KeyValuePair<string, object?>>`). These attributes will be serialized as JSON objects. ([#7015](open-telemetry/opentelemetry-dotnet#7015)) ... (truncated) ## 1.18.0-rc.1 The following changes are from the previous release [1.17.0](https://github.com/open-telemetry/opentelemetry-dotnet/releases/tag/core-1.17.0). * NuGet: [OpenTelemetry v1.18.0-rc.1](https://www.nuget.org/packages/OpenTelemetry/1.18.0-rc.1) * Fixed self-diagnostics log lines being silently dropped when an event message or parameter contained enough 3-byte UTF-8 characters to overflow the internal buffer estimate. Such content is now truncated. ([#7543](open-telemetry/opentelemetry-dotnet#7543)) * Fixed activity creation throwing when multiple tracer providers return a sampler attribute with the same key. ([#7558](open-telemetry/opentelemetry-dotnet#7558)) * Added the `otel.sdk.processor.log.processed` SDK self-observability metric. ([#7486](open-telemetry/opentelemetry-dotnet#7486)) * Added the `otel.sdk.processor.span.processed` SDK self-observability metric. ([#7598](open-telemetry/opentelemetry-dotnet#7598)) * `BatchActivityExportProcessor` and `SimpleActivityExportProcessor` no longer forward spans to the exporter once `Shutdown` has been called, and `BatchActivityExportProcessor.Shutdown` now waits for in-flight `OnEnd` calls to finish enqueueing before flushing. ([#7598](open-telemetry/opentelemetry-dotnet#7598)) * Fix logger, meter and tracer providers leaking background threads if an exception is thrown by their constructor after resource creation. ([#7615](open-telemetry/opentelemetry-dotnet#7615)) * `CircularBufferBuckets.Copy` optimized to use bulk array copies. ([#7670](open-telemetry/opentelemetry-dotnet#7670)) * Restored configured `MaxScale` after delta exponential histogram collection. ([#7671](open-telemetry/opentelemetry-dotnet#7671)) See [CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.18.0-rc.1/src/OpenTelemetry/CHANGELOG.md) for details. * NuGet: [OpenTelemetry.Api v1.18.0-rc.1](https://www.nuget.org/packages/OpenTelemetry.Api/1.18.0-rc.1) * Avoid formatting exceptions and creating exception attributes when `RecordException` is called on a span that is not recorded. ([#7669](open-telemetry/opentelemetry-dotnet#7669)) See [CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.18.0-rc.1/src/OpenTelemetry.Api/CHANGELOG.md) for details. * NuGet: [OpenTelemetry.Api.ProviderBuilderExtensions v1.18.0-rc.1](https://www.nuget.org/packages/OpenTelemetry.Api.ProviderBuilderExtensions/1.18.0-rc.1) No notable changes. See [CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.18.0-rc.1/src/OpenTelemetry.Api.ProviderBuilderExtensions/CHANGELOG.md) for details. * NuGet: [OpenTelemetry.Exporter.Console v1.18.0-rc.1](https://www.nuget.org/packages/OpenTelemetry.Exporter.Console/1.18.0-rc.1) * Added support for serializing attribute values that are key/value lists (`IEnumerable<KeyValuePair<string, object?>>`). These attributes will be serialized as JSON objects. ([#7015](open-telemetry/opentelemetry-dotnet#7015)) See [CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.18.0-rc.1/src/OpenTelemetry.Exporter.Console/CHANGELOG.md) for details. ... (truncated) ## 1.18.0-beta.1 The following changes are from the previous release [1.17.0-beta.1](https://github.com/open-telemetry/opentelemetry-dotnet/releases/tag/coreunstable-1.17.0-beta.1). * NuGet: [OpenTelemetry.Exporter.Prometheus.AspNetCore v1.18.0-beta.1](https://www.nuget.org/packages/OpenTelemetry.Exporter.Prometheus.AspNetCore/1.18.0-beta.1) * Fix concurrent scrapes returning an empty response under contention. Now the exporter will return an HTTP 500 error instead. ([#7571](open-telemetry/opentelemetry-dotnet#7571)) * Waiting for concurrent scrapes to finish before collecting no longer blocks, which could stall concurrent scrapes being waited on. ([#7571](open-telemetry/opentelemetry-dotnet#7571)) * Fixed the interaction between `PrometheusAspNetCoreOptions.TranslationStrategy` and content negotiation. The configured strategy is now applied before content negotiation, instead of the negotiated escaping scheme replacing the strategy's, and the `Content-Type` header now reports the escaping scheme that was applied rather than the one that was negotiated. ([#7610](open-telemetry/opentelemetry-dotnet#7610)) * Fixed metric values and histogram bucket bounds being written with 17 significant digits instead of their shortest round-trippable representation. ([#7589](open-telemetry/opentelemetry-dotnet#7589)) * Fixed the canonical representation used for histogram `le` and summary `quantile` label values falling back to 17 significant digits incorrectly. ([#7589](open-telemetry/opentelemetry-dotnet#7589)) * Fixed a race where a slow scrape could return an HTTP 200 instead of 408. ([#7615](open-telemetry/opentelemetry-dotnet#7615)) * Updated OpenTelemetry core component version(s) to `1.18.0`. ([#7674](open-telemetry/opentelemetry-dotnet#7674)) See [CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/coreunstable-1.18.0-beta.1/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md) for details. * NuGet: [OpenTelemetry.Exporter.Prometheus.HttpListener v1.18.0-beta.1](https://www.nuget.org/packages/OpenTelemetry.Exporter.Prometheus.HttpListener/1.18.0-beta.1) * Fix concurrent scrapes returning an empty response under contention. Now the exporter will return an HTTP 500 error instead. ([#7571](open-telemetry/opentelemetry-dotnet#7571)) * Waiting for concurrent scrapes to finish before collecting no longer blocks, which could stall concurrent scrapes being waited on. ([#7571](open-telemetry/opentelemetry-dotnet#7571)) * A scrape which is still collecting when the listener is disposed now returns an HTTP 503 response. ([#7587](open-telemetry/opentelemetry-dotnet#7587)) * Shutting down the listener no longer waits indefinitely for its request processing loop to stop, and no longer throws if the loop faulted. ([#7587](open-telemetry/opentelemetry-dotnet#7587)) * Fixed the interaction between `PrometheusHttpListenerOptions.TranslationStrategy` and content negotiation. The configured strategy is now applied before content negotiation, instead of the negotiated escaping scheme replacing the strategy's, and the `Content-Type` header now reports the escaping scheme that was applied rather than the one that was negotiated. ([#7610](open-telemetry/opentelemetry-dotnet#7610)) * Fixed metric values and histogram bucket bounds being written with 17 significant digits instead of their shortest round-trippable representation. ([#7589](open-telemetry/opentelemetry-dotnet#7589)) * Fixed the canonical representation used for histogram `le` and summary `quantile` label values falling back to 17 significant digits incorrectly. ([#7589](open-telemetry/opentelemetry-dotnet#7589)) ... (truncated) Commits viewable in [compare view](open-telemetry/opentelemetry-dotnet@core-1.17.0...core-1.18.0). </details> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Changes
Avoid disposal race in
PrometheusHttpListenerthat can cause shutdown to hang indefinitely and flaky tests (1, 2, 3).Merge requirement checklist
CHANGELOG.mdfiles updated for non-trivial changesChanges in public API reviewed (if applicable)