All checks were successful
check / check (push) Successful in 3m1s
The metrics recorder labelled its `handler` dimension with the concrete request path, so every distinct /webhook/<uuid> minted a permanent label set that nothing ever evicted. Measured on this branch's parent: a scrape went from 106 series and 12 KB to 78,132 series and 10.7 MB after 3,000 unauthenticated POSTs to invented entrypoint UUIDs, and stayed there. It also published those UUIDs -- the receiver's only credential -- verbatim in the scrape. The label now comes from chi's route pattern. It cannot be supplied as go-http-metrics' handler id: the recorder is global middleware, so it is entered before chi has matched anything, and the library fixes the id up front. What the library does pass through unchanged is the request context, on every recorder call, and the duration and size observations happen after the wrapped handler returns -- the same point accessLogURL already reads the pattern from. So a recorder decorator rewrites the id there instead. std.Handler and its response-writer interceptor are untouched, so status and size capture are unchanged. Recording after the whole chain returns is what makes this hold for requests the route-level receiver limiter rejects, which were the majority of the leaked series: chi has matched the route before the limiter runs, so a 429 carries the pattern like any other response. A path matching no route carries the existing unmatchedRoute sentinel, the same fixed value the access log uses. http_requests_inflight cannot carry a pattern -- it is incremented before routing and decremented after, so a route-derived label would increment one series and decrement another and leave the gauge permanently wrong. It gets a fixed aggregate label instead. Verified against a live instance: 6,000 distinct receiver paths and 250 unmatched paths across two floods left the scrape at 206 series and 21 KB, flat between floods, with no UUID anywhere in the output and all 4,800 429s on the single pattern.
20 KiB
20 KiB