fix: map prometheus /metrics upstream timeouts to 504 instead of 502 - #314
Open
detail-app[bot] wants to merge 1 commit into
Open
detail-app[bot] wants to merge 1 commit into
detail-app[bot] wants to merge 1 commit into
Conversation
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a focused one-file bug fix that maps existing Prometheus upstream watchdog timeouts to the already-defined 504 response while preserving non-timeout error handling. It introduces no schema, infrastructure, security, billing, or authentication changes and includes targeted status-mapping coverage. You can add or adjust custom eligibility rules. Learn more. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Detail bug report: View on Detail
Fixes ENG-715
Bug
On the httpz Prometheus scrape path (
GET /metricsandGET /metrics/...), a stalled upstream that trips the 30-second upstream watchdog returned HTTP 502 Bad Gateway instead of the intended 504 Gateway Timeout.Root cause: commit
e75cfca(PR #263) registeredexecFetchFilteredwith the watchdog viatrackUpstreambut — unlike its siblingexchangehelper — never added thebufs.timed_outcheck at the phase boundaries. When the watchdog fires it shuts down the upstream socket (both directions), so the in-flightsendBodiless()/receiveHead()/streamReaderToWriter()fails with a generic transport error (HttpConnectionClosing,HttpRequestTruncated,ReadFailed). That propagates tohandle, whereerrorStatusmaps it via theelse => 502arm.errorStatusalready mapserror.UpstreamTimeout => 504, but this path never returned that error. The net effect: a timeout was indistinguishable from a broken/malformed upstream in both the status line and theRequestFailed/RequestCompleteddebug telemetry.Fix
src/frontend/httpz/server.zig,Handler.execFetchFiltered— addbufs.timed_outchecks mirroringexchangeat both phase boundaries, converting watchdog-induced transport errors toerror.UpstreamTimeout(→ 504):sendBodiless+receiveHead): restructured the block so the block produces an error union the outercatchcan handle (Zig 0.16tryinside a labeled block propagates to the enclosing function, so the literal pattern from the report doesn't compile). The block-scopederrdefereviction was moved explicitly into thecatchto preserve the prior unconditional eviction (abreak :blk errnormal break does not fireerrdefer), matchingexchange's evict-then-check ordering.streamReaderToWriter): catch and returnerror.UpstreamTimeoutafter evicting whentimed_outis set; otherwise re-propagate the original error so non-timeout body errors keep their status (e.g.BodyTooLarge→ 413).The upstream send mechanics (
sendBodiless()+receiveHead(&.{})) are byte-for-byte unchanged; only the error handling around them changed.errorStatusandexchangeare untouched. The stdio frontend's parallelexecFetchFiltered(src/frontend/stdio/conn.zig) has no watchdog wiring and is intentionally unchanged.A unit test
errorStatus maps upstream timeout to 504 and watchdog errors to 502pins the status-code contract the bug hinged on:UpstreamTimeout → 504, the watchdog-induced transport errors (ReadFailed,HttpRequestTruncated) → 502, and the other arms (413/400/503) stay stable.Testing
zig build,zig build -Doptimize=ReleaseSafe,zig build test(520 pass / 1 pre-existing skip),zig fmt --check,ziglint(v0.5.2), andtask doall green. The new unit test was confirmed to actually execute by temporarily flipping its assertion (the suite went 520 pass → 519 pass + 1 fail on the exact test), then restoring.edge-prometheus(ReleaseSafe) and a Python mock upstream that accepts the connection and then stalls (never responds, never closes). HitGET /metricsthrough the real proxy.request.failed err="HttpConnectionClosing"andrequest.completed status=502— the reported bug.request.failed err="UpstreamTimeout", anupstream.connection.evictedevent, andrequest.completed status=504.HttpRequestTruncatedpath named in the report) also returned 504 after the fix.okmock) returns 200 with the policy-filtered body; no timeout/eviction events.timed_out.max_input_bytes_per_scrapeset small so the body exceeds the budget) returns 413, not 504 — confirms the body catch doesn't mistranslate non-timeout errors.exchangepath: builtedge-datadogand stalledPOST /api/v2/logs; it returned 504 witherr="UpstreamTimeout"(no regression to the Datadog/OTLP intake paths).zig build upstream-pool-harnessPASSes (PR fix: retry replayable log intake on stale upstream connections #263's eviction/replay behavior intact)./metricsinterrupted the I/O via the watchdog's force-expiry path, reported 504, and the server drained and stopped cleanly with no hang.pipeline.streamReaderToWritertreats as a successful end-of-stream. The body-phasecatchis therefore defensive/parity-only and doesn't fire today. This is a separate latent issue (truncated upstream body reported as a successful 200), independent of the 502-vs-504 defect, and is left for a follow-up.Automatic Fixes PRs can be configured here.