Skip to content

fix(metrics): terminate classic histogram buckets with +Inf - #260

Merged
TheJokr merged 1 commit into
cloudflare:mainfrom
ethanolchik:fix/classic-histogram-terminal-bucket-inf
Aug 26, 2026
Merged

fix(metrics): terminate classic histogram buckets with +Inf#260
TheJokr merged 1 commit into
cloudflare:mainfrom
ethanolchik:fix/classic-histogram-terminal-bucket-inf

Conversation

@ethanolchik

@ethanolchik ethanolchik commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Classic histogram bucket lists terminated with a finite f64::MAX instead of f64::INFINITY. encode_to_text rewrote it to +Inf; encode_to_protobuf did not, so scrapes gained a phantom le="1.7976931348623157e+308" series and histogram_quantile interpolated against the sentinel (p99 returned ~1.8e308 rather than clamping to the top bucket).

Changes:

  • metrics/histogram.rsHistogram::new and TimeHistogram::new append f64::INFINITY.
  • metrics/native_histogram.rs – new classic_upper_bound translates the sentinel where prometheus_client's buckets enter our protobuf model. Needed separately: upstream appends f64::MAX itself, so NativeHistogram isn't covered by the change above.
  • encoding/text.rs – dropped the rewrite. Nothing emits the sentinel now, and its comment wrongly claimed the finite bound was required by the protobuf data model.
  • tests/histogram_terminal_bucket.rs – asserts the terminal bound across both encoders for classic, time and native+classic histograms.

Also fixes observe(f64::INFINITY) matching no bucket while still incrementing the count.

Breaking for consumers reading the terminal bound: HistogramSnapshot::buckets() and the encoded value are now f64::INFINITY. cloudchamberd-rs/src/rpc_metrics.rs asserts the old value.

`Histogram` and `TimeHistogram` appended a finite `f64::MAX` bucket as the
catch-all bound, and the classic buckets borrowed from `prometheus_client` for
a `NativeHistogram` carry the same sentinel. The text encoder rewrote it to
`+Inf` on the way out, but `encode_to_protobuf` did not, so the two encoders
disagreed and every classic bucket set scraped over protobuf gained a phantom
`le="1.7976931348623157e+308"` series.

Prometheus treats an infinite bound as the terminator of a classic bucket list
(`IsInf` in `protobufparse.go`) and appends a synthetic `+Inf` bucket carrying
the sample count when it does not find one. A finite sentinel therefore scrapes
as an extra series duplicating `+Inf`, which inflates series counts and, worse,
lets `histogram_quantile` interpolate between the last real bound and
`f64::MAX`: a p99 above the top configured bucket returned ~1.8e308 seconds
instead of being clamped to that bucket.

Use `f64::INFINITY` at the source instead, and translate the sentinel where
upstream's buckets enter our protobuf model. The comment claiming the finite
bound was needed for the protobuf data model was wrong: `upper_bound` is an
IEEE 754 double and Prometheus depends on `+Inf` appearing there.

This also fixes an observation being dropped: with a finite terminal bound an
`f64::INFINITY` observation matched no bucket while still incrementing the
count, leaving the catch-all bucket below `_count`.

The text encoder no longer rewrites the sentinel. Nothing in the crate emits it
any more, and tolerating it in one encoder but not the other is what hid this.
Note for downstream: tests asserting `f64::MAX` as the encoded terminal bound,
or reading it back from `HistogramSnapshot::buckets`, now see `f64::INFINITY`.
@TheJokr
TheJokr self-requested a review August 26, 2026 12:36
@TheJokr
TheJokr merged commit 41dcaa9 into cloudflare:main Aug 26, 2026
18 checks passed
@ethanolchik
ethanolchik deleted the fix/classic-histogram-terminal-bucket-inf branch August 26, 2026 12:45
@TheJokr TheJokr mentioned this pull request Aug 26, 2026
dotjs pushed a commit that referenced this pull request Aug 26, 2026
### Fixed
- #258: With `foundations-metrics-backend` enabled, the `process`
metrics implementation relied on an accessor in the `prometheus` crate
that is not present when the `protobuf` feature is enabled. This change
enables the `protobuf` feature in `prometheus` instead and uses a
different accessor.
- #260: `NativeHistogram`, when scraped in protobuf format, now
correctly exposes an upper bound of `f64::INFINITY` rather than
`f64::MAX`.
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.

3 participants