Merge branch 'release-3.8' into sync-release-3.8

Conflicts:
	storage/remote/write_handler.go
	storage/remote/write_handler_test.go
            Pick `main`

Signed-off-by: Jan Fajerski <jfajersk@redhat.com>
This commit is contained in:
Jan Fajerski 2025-12-02 12:48:59 +01:00
commit fae20b73ff
13 changed files with 472 additions and 26 deletions

View File

@ -19,7 +19,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
- uses: prometheus/promci@c0916f0a41f13444612a8f0f5e700ea34edd7c19 # v0.5.3
- uses: ./.github/promci/actions/setup_environment
with:
enable_npm: true
@ -37,7 +37,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
- uses: prometheus/promci@c0916f0a41f13444612a8f0f5e700ea34edd7c19 # v0.5.3
- uses: ./.github/promci/actions/setup_environment
- run: go test --tags=dedupelabels ./...
- run: go test --tags=slicelabels -race ./cmd/prometheus ./model/textparse ./prompb/...
@ -81,7 +81,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
- uses: prometheus/promci@c0916f0a41f13444612a8f0f5e700ea34edd7c19 # v0.5.3
- uses: ./.github/promci/actions/setup_environment
with:
enable_go: false
@ -146,7 +146,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
- uses: prometheus/promci@c0916f0a41f13444612a8f0f5e700ea34edd7c19 # v0.5.3
- uses: ./.github/promci/actions/build
with:
promu_opts: "-p linux/amd64 -p windows/amd64 -p linux/arm64 -p darwin/amd64 -p darwin/arm64 -p linux/386"
@ -173,7 +173,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
- uses: prometheus/promci@c0916f0a41f13444612a8f0f5e700ea34edd7c19 # v0.5.3
- uses: ./.github/promci/actions/build
with:
parallelism: 12
@ -268,7 +268,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
- uses: prometheus/promci@c0916f0a41f13444612a8f0f5e700ea34edd7c19 # v0.5.3
- uses: ./.github/promci/actions/publish_main
with:
docker_hub_login: ${{ secrets.docker_hub_login }}
@ -287,7 +287,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
- uses: prometheus/promci@c0916f0a41f13444612a8f0f5e700ea34edd7c19 # v0.5.3
- uses: ./.github/promci/actions/publish_release
with:
docker_hub_login: ${{ secrets.docker_hub_login }}
@ -304,7 +304,7 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: prometheus/promci@443c7fc2397e946bc9f5029e313a9c3441b9b86d # v0.4.7
- uses: prometheus/promci@c0916f0a41f13444612a8f0f5e700ea34edd7c19 # v0.5.3
- name: Install nodejs
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:

View File

@ -5,6 +5,42 @@
* [FEATURE] Templates: Add urlQueryEscape to template functions. #17403
* [BUGFIX] TSDB: Register `prometheus_tsdb_sample_ooo_delta` metric properly. #17477
## 3.8.0 / 2025-11-28
* [CHANGE] Remote-write 2 (receiving): Update to [2.0-rc.4 spec](https://github.com/prometheus/docs/blob/60c24e450010df38cfcb4f65df874f6f9b26dbcb/docs/specs/prw/remote_write_spec_2_0.md). "created timestamp" (CT) is now called "start timestamp" (ST). #17411
* [CHANGE] TSDB: Native Histogram Custom Bounds with a NaN threshold are now rejected. #17287
* [FEATURE] OAuth2: support jwt-bearer grant-type (RFC7523 3.1). #17592
* [FEATURE] Dockerfile: Add OpenContainers spec labels to Dockerfile. #16483
* [FEATURE] SD: Add unified AWS service discovery for ec2, lightsail and ecs services. #17046
* [FEATURE] Native histograms are now a stable, but optional feature, use the `scrape_native_histogram` config setting. #17232 #17315
* [FEATURE] UI: Support anchored and smoothed keyword in promql editor. #17239
* [FEATURE] UI: Show detailed relabeling steps for each discovered target. #17337
* [FEATURE] Alerting: Add urlQueryEscape to template functions. #17403
* [FEATURE] Promtool: Add Remote-Write 2.0 support to `promtool push metrics` via the `--protobuf_message` flag. #17417
* [ENHANCEMENT] Clarify the docs about handling negative native histograms. #17249
* [ENHANCEMENT] Mixin: Add static UID to the remote-write dashboard. #17256
* [ENHANCEMENT] PromQL: Reconcile mismatched NHCB bounds in `Add` and `Sub`. #17278
* [ENHANCEMENT] Alerting: Add "unknown" state for alerting rules that haven't been evaluated yet. #17282
* [ENHANCEMENT] Scrape: Allow simultaneous use of classic histogram → NHCB conversion and zero-timestamp ingestion. #17305
* [ENHANCEMENT] UI: Add smoothed/anchored in explain. #17334
* [ENHANCEMENT] OTLP: De-duplicate any `target_info` samples with the same timestamp for the same series. #17400
* [ENHANCEMENT] Document `use_fips_sts_endpoint` in `sigv4` config sections. #17304
* [ENHANCEMENT] Document Prometheus Agent. #14519
* [PERF] PromQL: Speed up parsing of variadic functions. #17316
* [PERF] UI: Speed up alerts/rules/... pages by not rendering collapsed content. #17485
* [PERF] UI: Performance improvement when getting label name and values in promql editor. #17194
* [PERF] UI: Speed up /alerts for many firing alerts via virtual scrolling. #17254
* [BUGFIX] PromQL: Fix slice indexing bug in info function on churning series. #17199
* [BUGFIX] API: Reduce lock contention on `/api/v1/targets`. #17306
* [BUGFIX] PromQL: Consistent handling of gauge vs. counter histograms in aggregations. #17312
* [BUGFIX] TSDB: Allow NHCB with -Inf as the first custom value. #17320
* [BUGFIX] UI: Fix duplicate loading of data from the API speed up rendering of some pages. #17357
* [BUGFIX] Old UI: Fix createExpressionLink to correctly build /graph URLs so links from Alerts/Rules work again. #17365
* [BUGFIX] PromQL: Avoid panic when parsing malformed `info` call. #17379
* [BUGFIX] PromQL: Include histograms when enforcing sample_limit. #17390
* [BUGFIX] Config: Fix panic if TLS CA file is absent. #17418
* [BUGFIX] PromQL: Fix `histogram_fraction` for classic histograms and NHCB if lower bound is in the first bucket. #17424
## 3.7.3 / 2025-10-29
* [BUGFIX] UI: Revert changed (and breaking) redirect behavior for `-web.external-url` if `-web.route-prefix` is configured, which was introduced in #17240. #17389

View File

@ -1 +1 @@
3.7.3
3.8.0

View File

@ -979,6 +979,7 @@ remote_write:
// | dataPending | 0 | 1228.8 |
// | desiredShards | 0.6 | 369.2 |.
func TestRemoteWrite_ReshardingWithoutDeadlock(t *testing.T) {
t.Skip("flaky test, see https://github.com/prometheus/prometheus/issues/17489")
t.Parallel()
tmpDir := t.TempDir()

2
go.mod
View File

@ -58,7 +58,7 @@ require (
github.com/prometheus/client_golang v1.23.2
github.com/prometheus/client_golang/exp v0.0.0-20250914183048-a974e0d45e0a
github.com/prometheus/client_model v0.6.2
github.com/prometheus/common v0.67.2
github.com/prometheus/common v0.67.4
github.com/prometheus/common/assets v0.2.0
github.com/prometheus/exporter-toolkit v0.15.0
github.com/prometheus/sigv4 v0.3.0

4
go.sum
View File

@ -447,8 +447,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyAEN8=
github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko=
github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=
github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
github.com/prometheus/common/assets v0.2.0 h1:0P5OrzoHrYBOSM1OigWL3mY8ZvV2N4zIE/5AahrSrfM=
github.com/prometheus/common/assets v0.2.0/go.mod h1:D17UVUE12bHbim7HzwUvtqm6gwBEaDQ0F+hIGbFbccI=
github.com/prometheus/exporter-toolkit v0.15.0 h1:Pcle5sSViwR1x0gdPd0wtYrPQENBieQAM7TmT0qtb2U=

View File

@ -158,6 +158,383 @@ eval instant at 50m histogram_fraction(0, 0.2, rate(testhistogram3_bucket[10m]))
{start="positive"} 0.6363636363636364
{start="negative"} 0
# Positive buckets, lower falls in the first bucket.
load_with_nhcb 5m
positive_buckets_lower_falls_in_the_first_bucket_bucket{le="1"} 1+0x10
positive_buckets_lower_falls_in_the_first_bucket_bucket{le="2"} 3+0x10
positive_buckets_lower_falls_in_the_first_bucket_bucket{le="3"} 6+0x10
positive_buckets_lower_falls_in_the_first_bucket_bucket{le="+Inf"} 100+0x10
# - Bucket [0, 1]: contributes 1.0 observation (full bucket).
# - Bucket [1, 2]: contributes (1.5-1)/(2-1) * (3-1) = 0.5 * 2 = 1.0 observations.
# Total: (1.0 + 1.0) / 100.0 = 0.02
eval instant at 50m histogram_fraction(0, 1.5, positive_buckets_lower_falls_in_the_first_bucket_bucket)
expect no_warn
{} 0.02
eval instant at 50m histogram_fraction(0, 1.5, positive_buckets_lower_falls_in_the_first_bucket)
expect no_warn
{} 0.02
# Negative buckets, lower falls in the first bucket.
load_with_nhcb 5m
negative_buckets_lower_falls_in_the_first_bucket_bucket{le="-3"} 10+0x10
negative_buckets_lower_falls_in_the_first_bucket_bucket{le="-2"} 12+0x10
negative_buckets_lower_falls_in_the_first_bucket_bucket{le="-1"} 15+0x10
negative_buckets_lower_falls_in_the_first_bucket_bucket{le="+Inf"} 100+0x10
# - Bucket [-Inf, -3]: contributes zero observations (no interpolation with infinite width bucket).
# - Bucket [-3, -2]: contributes 12-10 = 2.0 observations (full bucket).
# Total: 2.0 / 100.0 = 0.02
eval instant at 50m histogram_fraction(-4, -2, negative_buckets_lower_falls_in_the_first_bucket_bucket)
expect no_warn
{} 0.02
eval instant at 50m histogram_fraction(-4, -2, negative_buckets_lower_falls_in_the_first_bucket)
expect no_warn
{} 0.02
# Lower is -Inf.
load_with_nhcb 5m
lower_is_negative_Inf_bucket{le="-3"} 10+0x10
lower_is_negative_Inf_bucket{le="-2"} 12+0x10
lower_is_negative_Inf_bucket{le="-1"} 15+0x10
lower_is_negative_Inf_bucket{le="+Inf"} 100+0x10
# - Bucket [-Inf, -3]: contributes 10.0 observations (full bucket).
# - Bucket [-3, -2]: contributes 12-10 = 2.0 observations (full bucket).
# - Bucket [-2, -1]: contributes (-1.5-(-2))/(-1-(-2)) * (15-12) = 0.5 * 3 = 1.5 observations.
# Total: (10.0 + 2.0 + 1.5) / 100.0 = 0.135
eval instant at 50m histogram_fraction(-Inf, -1.5, lower_is_negative_Inf_bucket)
expect no_warn
{} 0.135
eval instant at 50m histogram_fraction(-Inf, -1.5, lower_is_negative_Inf)
expect no_warn
{} 0.135
# Lower is -Inf and upper is +Inf (positive buckets).
load_with_nhcb 5m
lower_is_negative_Inf_and_upper_is_positive_Inf__positive_buckets__bucket{le="1"} 1+0x10
lower_is_negative_Inf_and_upper_is_positive_Inf__positive_buckets__bucket{le="2"} 3+0x10
lower_is_negative_Inf_and_upper_is_positive_Inf__positive_buckets__bucket{le="3"} 6+0x10
lower_is_negative_Inf_and_upper_is_positive_Inf__positive_buckets__bucket{le="+Inf"} 100+0x10
# Range [-Inf, +Inf] captures all observations.
eval instant at 50m histogram_fraction(-Inf, +Inf, lower_is_negative_Inf_and_upper_is_positive_Inf__positive_buckets__bucket)
expect no_warn
{} 1.0
eval instant at 50m histogram_fraction(-Inf, +Inf, lower_is_negative_Inf_and_upper_is_positive_Inf__positive_buckets_)
expect no_warn
{} 1.0
# Lower is -Inf and upper is +Inf (negative buckets).
load_with_nhcb 5m
lower_is_negative_Inf_and_upper_is_positive_Inf__negative_buckets__bucket{le="-3"} 10+0x10
lower_is_negative_Inf_and_upper_is_positive_Inf__negative_buckets__bucket{le="-2"} 12+0x10
lower_is_negative_Inf_and_upper_is_positive_Inf__negative_buckets__bucket{le="-1"} 15+0x10
lower_is_negative_Inf_and_upper_is_positive_Inf__negative_buckets__bucket{le="+Inf"} 100+0x10
# Range [-Inf, +Inf] captures all observations.
eval instant at 50m histogram_fraction(-Inf, +Inf, lower_is_negative_Inf_and_upper_is_positive_Inf__negative_buckets__bucket)
expect no_warn
{} 1.0
eval instant at 50m histogram_fraction(-Inf, +Inf, lower_is_negative_Inf_and_upper_is_positive_Inf__negative_buckets_)
expect no_warn
{} 1.0
# Lower and upper fall in last bucket (positive buckets).
load_with_nhcb 5m
lower_and_upper_fall_in_last_bucket__positive_buckets__bucket{le="1"} 1+0x10
lower_and_upper_fall_in_last_bucket__positive_buckets__bucket{le="2"} 3+0x10
lower_and_upper_fall_in_last_bucket__positive_buckets__bucket{le="3"} 6+0x10
lower_and_upper_fall_in_last_bucket__positive_buckets__bucket{le="+Inf"} 100+0x10
# - Bucket [3, +Inf]: contributes zero observations (no interpolation with infinite width bucket).
# Total: 0.0 / 100.0 = 0.0
eval instant at 50m histogram_fraction(4, 5, lower_and_upper_fall_in_last_bucket__positive_buckets__bucket)
expect no_warn
{} 0.0
eval instant at 50m histogram_fraction(4, 5, lower_and_upper_fall_in_last_bucket__positive_buckets_)
expect no_warn
{} 0.0
# Lower and upper fall in last bucket (negative buckets).
load_with_nhcb 5m
lower_and_upper_fall_in_last_bucket__negative_buckets__bucket{le="-3"} 10+0x10
lower_and_upper_fall_in_last_bucket__negative_buckets__bucket{le="-2"} 12+0x10
lower_and_upper_fall_in_last_bucket__negative_buckets__bucket{le="-1"} 15+0x10
lower_and_upper_fall_in_last_bucket__negative_buckets__bucket{le="+Inf"} 100+0x10
# - Bucket [-1, +Inf]: contributes zero observations (no interpolation with infinite width bucket).
# Total: 0.0 / 100.0 = 0.0
eval instant at 50m histogram_fraction(0, 1, lower_and_upper_fall_in_last_bucket__negative_buckets__bucket)
expect no_warn
{} 0.0
eval instant at 50m histogram_fraction(0, 1, lower_and_upper_fall_in_last_bucket__negative_buckets_)
expect no_warn
{} 0.0
# Upper falls in last bucket.
load_with_nhcb 5m
upper_falls_in_last_bucket_bucket{le="1"} 1+0x10
upper_falls_in_last_bucket_bucket{le="2"} 3+0x10
upper_falls_in_last_bucket_bucket{le="3"} 6+0x10
upper_falls_in_last_bucket_bucket{le="+Inf"} 100+0x10
# - Bucket [2, 3]: 6-3 = 3.0 observations (full bucket).
# - Bucket [3, +Inf]: contributes zero observations (no interpolation with infinite width bucket).
# Total: 3.0 / 100.0 = 0.03
eval instant at 50m histogram_fraction(2, 5, upper_falls_in_last_bucket_bucket)
expect no_warn
{} 0.03
eval instant at 50m histogram_fraction(2, 5, upper_falls_in_last_bucket)
expect no_warn
{} 0.03
# Upper is +Inf.
load_with_nhcb 5m
upper_is_positive_Inf_bucket{le="1"} 1+0x10
upper_is_positive_Inf_bucket{le="2"} 3+0x10
upper_is_positive_Inf_bucket{le="3"} 6+0x10
upper_is_positive_Inf_bucket{le="+Inf"} 100+0x10
# All observations in +Inf bucket: 100-6 = 94.0 observations.
# Total: 94.0 / 100.0 = 0.94
eval instant at 50m histogram_fraction(400, +Inf, upper_is_positive_Inf_bucket)
expect no_warn
{} 0.94
eval instant at 50m histogram_fraction(400, +Inf, upper_is_positive_Inf)
expect no_warn
{} 0.94
# Lower equals upper.
load_with_nhcb 5m
lower_equals_upper_bucket{le="1"} 1+0x10
lower_equals_upper_bucket{le="2"} 3+0x10
lower_equals_upper_bucket{le="3"} 6+0x10
lower_equals_upper_bucket{le="+Inf"} 100+0x10
# No observations can be captured in a zero-width range.
eval instant at 50m histogram_fraction(2, 2, lower_equals_upper_bucket)
expect no_warn
{} 0.0
eval instant at 50m histogram_fraction(2, 2, lower_equals_upper)
expect no_warn
{} 0.0
# Lower greater than upper.
load_with_nhcb 5m
lower_greater_than_upper_bucket{le="1"} 1+0x10
lower_greater_than_upper_bucket{le="2"} 3+0x10
lower_greater_than_upper_bucket{le="3"} 6+0x10
lower_greater_than_upper_bucket{le="+Inf"} 100+0x10
eval instant at 50m histogram_fraction(3, 2, lower_greater_than_upper_bucket)
expect no_warn
{} 0.0
eval instant at 50m histogram_fraction(3, 2, lower_greater_than_upper)
expect no_warn
{} 0.0
# Single bucket.
load_with_nhcb 5m
single_bucket_bucket{le="+Inf"} 100+0x10
# - Bucket [0, +Inf]: contributes zero observations (no interpolation with infinite width bucket).
# Total: 0.0 / 100.0 = 0.0
eval instant at 50m histogram_fraction(0, 1, single_bucket_bucket)
expect no_warn
{} 0.0
eval instant at 50m histogram_fraction(0, 1, single_bucket)
expect no_warn
{} 0.0
# All zero counts.
load_with_nhcb 5m
all_zero_counts_bucket{le="1"} 0+0x10
all_zero_counts_bucket{le="2"} 0+0x10
all_zero_counts_bucket{le="3"} 0+0x10
all_zero_counts_bucket{le="+Inf"} 0+0x10
eval instant at 50m histogram_fraction(0, 5, all_zero_counts_bucket)
expect no_warn
{} NaN
eval instant at 50m histogram_fraction(0, 5, all_zero_counts)
expect no_warn
{} NaN
# Lower exactly on bucket boundary.
load_with_nhcb 5m
lower_exactly_on_bucket_boundary_bucket{le="1"} 1+0x10
lower_exactly_on_bucket_boundary_bucket{le="2"} 3+0x10
lower_exactly_on_bucket_boundary_bucket{le="3"} 6+0x10
lower_exactly_on_bucket_boundary_bucket{le="+Inf"} 100+0x10
# - Bucket [2, 3]: 6-3 = 3.0 observations (full bucket).
# - Bucket [3, +Inf]: contributes zero observations (no interpolation with infinite width bucket).
# Total: 3.0 / 100.0 = 0.03
eval instant at 50m histogram_fraction(2, 3.5, lower_exactly_on_bucket_boundary_bucket)
expect no_warn
{} 0.03
eval instant at 50m histogram_fraction(2, 3.5, lower_exactly_on_bucket_boundary)
expect no_warn
{} 0.03
# Upper exactly on bucket boundary.
load_with_nhcb 5m
upper_exactly_on_bucket_boundary_bucket{le="1"} 1+0x10
upper_exactly_on_bucket_boundary_bucket{le="2"} 3+0x10
upper_exactly_on_bucket_boundary_bucket{le="3"} 6+0x10
upper_exactly_on_bucket_boundary_bucket{le="+Inf"} 100+0x10
# - Bucket [0, 1]: (1.0-0.5)/(1.0-0.0) * 1.0 = 0.5 * 1.0 = 0.5 observations.
# - Bucket [1, 2]: 3-1 = 2.0 observations (full bucket).
# Total: (0.5 + 2.0) / 100.0 = 0.025
eval instant at 50m histogram_fraction(0.5, 2, upper_exactly_on_bucket_boundary_bucket)
expect no_warn
{} 0.025
eval instant at 50m histogram_fraction(0.5, 2, upper_exactly_on_bucket_boundary)
expect no_warn
{} 0.025
# Both bounds exactly on bucket boundaries.
load_with_nhcb 5m
both_bounds_exactly_on_bucket_boundaries_bucket{le="1"} 1+0x10
both_bounds_exactly_on_bucket_boundaries_bucket{le="2"} 3+0x10
both_bounds_exactly_on_bucket_boundaries_bucket{le="3"} 6+0x10
both_bounds_exactly_on_bucket_boundaries_bucket{le="+Inf"} 100+0x10
# - Bucket [1, 2]: 3-1 = 2.0 observations (full bucket).
# - Bucket [2, 3]: 6-3 = 3.0 observations (full bucket).
# Total: (2.0 + 3.0) / 100.0 = 0.05
eval instant at 50m histogram_fraction(1, 3, both_bounds_exactly_on_bucket_boundaries_bucket)
expect no_warn
{} 0.05
eval instant at 50m histogram_fraction(1, 3, both_bounds_exactly_on_bucket_boundaries)
expect no_warn
{} 0.05
# Fractional bucket bounds.
load_with_nhcb 5m
fractional_bucket_bounds_bucket{le="0.5"} 2.5+0x10
fractional_bucket_bounds_bucket{le="1"} 7.5+0x10
fractional_bucket_bounds_bucket{le="+Inf"} 100+0x10
# - Bucket [0, 0.5]: (0.5-0.1)/(0.5-0.0) * 2.5 = 0.8 * 2.5 = 2.0 observations.
# - Bucket [0.5, 1.0]: (0.75-0.5)/(1.0-0.5) * (7.5-2.5) = 0.5 * 5.0 = 2.5 observations.
# Total: (2.0 + 2.5) / 100.0 = 0.045
eval instant at 50m histogram_fraction(0.1, 0.75, fractional_bucket_bounds_bucket)
expect no_warn
{} 0.045
eval instant at 50m histogram_fraction(0.1, 0.75, fractional_bucket_bounds)
expect no_warn
{} 0.045
# Range crosses zero.
load_with_nhcb 5m
range_crosses_zero_bucket{le="-2"} 5+0x10
range_crosses_zero_bucket{le="-1"} 10+0x10
range_crosses_zero_bucket{le="0"} 15+0x10
range_crosses_zero_bucket{le="1"} 20+0x10
range_crosses_zero_bucket{le="+Inf"} 100+0x10
# - Bucket [-1, 0]: 15-10 = 5.0 observations (full bucket).
# - Bucket [0, 1]: 20-15 = 5.0 observations (full bucket).
# Total: (5.0 + 5.0) / 100.0 = 0.1
eval instant at 50m histogram_fraction(-1, 1, range_crosses_zero_bucket)
expect no_warn
{} 0.1
eval instant at 50m histogram_fraction(-1, 1, range_crosses_zero)
expect no_warn
{} 0.1
# Lower is NaN.
load_with_nhcb 5m
lower_is_NaN_bucket{le="1"} 1+0x10
lower_is_NaN_bucket{le="+Inf"} 100+0x10
eval instant at 50m histogram_fraction(NaN, 1, lower_is_NaN_bucket)
expect no_warn
{} NaN
eval instant at 50m histogram_fraction(NaN, 1, lower_is_NaN)
expect no_warn
{} NaN
# Upper is NaN.
load_with_nhcb 5m
upper_is_NaN_bucket{le="1"} 1+0x10
upper_is_NaN_bucket{le="+Inf"} 100+0x10
eval instant at 50m histogram_fraction(0, NaN, upper_is_NaN_bucket)
expect no_warn
{} NaN
eval instant at 50m histogram_fraction(0, NaN, upper_is_NaN)
expect no_warn
{} NaN
# Range entirely below all buckets.
load_with_nhcb 5m
range_entirely_below_all_buckets_bucket{le="1"} 1+0x10
range_entirely_below_all_buckets_bucket{le="2"} 3+0x10
range_entirely_below_all_buckets_bucket{le="+Inf"} 10+0x10
eval instant at 50m histogram_fraction(-10, -5, range_entirely_below_all_buckets_bucket)
expect no_warn
{} 0.0
eval instant at 50m histogram_fraction(-10, -5, range_entirely_below_all_buckets)
expect no_warn
{} 0.0
# Range entirely above all buckets.
load_with_nhcb 5m
range_entirely_above_all_buckets_bucket{le="1"} 1+0x10
range_entirely_above_all_buckets_bucket{le="2"} 3+0x10
range_entirely_above_all_buckets_bucket{le="+Inf"} 10+0x10
eval instant at 50m histogram_fraction(5, 10, range_entirely_above_all_buckets_bucket)
expect no_warn
{} 0.0
eval instant at 50m histogram_fraction(5, 10, range_entirely_above_all_buckets)
expect no_warn
{} 0.0
# In the classic histogram, we can access the corresponding bucket (if
# it exists) and divide by the count to get the same result.

View File

@ -406,6 +406,18 @@ func HistogramFraction(lower, upper float64, h *histogram.FloatHistogram, metric
// consistent with the linear interpolation known from classic
// histograms. It is also used for the zero bucket.
interpolateLinearly := func(v float64) float64 {
// Note: `v` is a finite value.
// For buckets with infinite bounds, we cannot interpolate meaningfully.
// For +Inf upper bound, interpolation returns the cumulative count of the previous bucket
// as the second term in the interpolation formula yields 0 (finite/Inf).
// In other words, no observations from the last bucket are considered in the fraction calculation.
// For -Inf lower bound, however, the second term would be (v-(-Inf))/(upperBound-(-Inf)) = Inf/Inf = NaN.
// To achieve the same effect of no contribution as the +Inf bucket, handle the -Inf case by returning
// the cumulative count at the first bucket (which equals the bucket's count).
// In both cases, we effectively skip interpolation within the infinite-width bucket.
if b.Lower == math.Inf(-1) {
return b.Count
}
return rank + b.Count*(v-b.Lower)/(b.Upper-b.Lower)
}
@ -531,14 +543,34 @@ func BucketFraction(lower, upper float64, buckets Buckets) float64 {
rank, lowerRank, upperRank float64
lowerSet, upperSet bool
)
// If the upper bound of the first bucket is greater than 0, we assume
// we are dealing with positive buckets only and lowerBound for the
// first bucket is set to 0; otherwise it is set to -Inf.
lowerBound := 0.0
if buckets[0].UpperBound <= 0 {
lowerBound = math.Inf(-1)
}
for i, b := range buckets {
lowerBound := math.Inf(-1)
if i > 0 {
lowerBound = buckets[i-1].UpperBound
}
upperBound := b.UpperBound
interpolateLinearly := func(v float64) float64 {
// Note: `v` is a finite value.
// For buckets with infinite bounds, we cannot interpolate meaningfully.
// For +Inf upper bound, interpolation returns the cumulative count of the previous bucket
// as the second term in the interpolation formula yields 0 (finite/Inf).
// In other words, no observations from the last bucket are considered in the fraction calculation.
// For -Inf lower bound, however, the second term would be (v-(-Inf))/(upperBound-(-Inf)) = Inf/Inf = NaN.
// To achieve the same effect of no contribution as the +Inf bucket, handle the -Inf case by returning
// the cumulative count at the first bucket.
// In both cases, we effectively skip interpolation within the infinite-width bucket.
if lowerBound == math.Inf(-1) {
return b.Count
}
return rank + (b.Count-rank)*(v-lowerBound)/(upperBound-lowerBound)
}

View File

@ -1,7 +1,7 @@
{
"name": "@prometheus-io/mantine-ui",
"private": true,
"version": "0.307.3",
"version": "0.308.0",
"type": "module",
"scripts": {
"start": "vite",
@ -28,7 +28,7 @@
"@microsoft/fetch-event-source": "^2.0.1",
"@nexucis/fuzzy": "^0.5.1",
"@nexucis/kvsearch": "^0.9.1",
"@prometheus-io/codemirror-promql": "0.307.3",
"@prometheus-io/codemirror-promql": "0.308.0",
"@reduxjs/toolkit": "^2.10.1",
"@tabler/icons-react": "^3.35.0",
"@tanstack/react-query": "^5.90.7",

View File

@ -1,6 +1,6 @@
{
"name": "@prometheus-io/codemirror-promql",
"version": "0.307.3",
"version": "0.308.0",
"description": "a CodeMirror mode for the PromQL language",
"types": "dist/esm/index.d.ts",
"module": "dist/esm/index.js",
@ -29,7 +29,7 @@
},
"homepage": "https://github.com/prometheus/prometheus/blob/main/web/ui/module/codemirror-promql/README.md",
"dependencies": {
"@prometheus-io/lezer-promql": "0.307.3",
"@prometheus-io/lezer-promql": "0.308.0",
"lru-cache": "^11.2.2"
},
"devDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@prometheus-io/lezer-promql",
"version": "0.307.3",
"version": "0.308.0",
"description": "lezer-based PromQL grammar",
"main": "dist/index.cjs",
"type": "module",

View File

@ -1,12 +1,12 @@
{
"name": "prometheus-io",
"version": "0.307.3",
"version": "0.308.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "prometheus-io",
"version": "0.307.3",
"version": "0.308.0",
"workspaces": [
"mantine-ui",
"module/*"
@ -24,7 +24,7 @@
},
"mantine-ui": {
"name": "@prometheus-io/mantine-ui",
"version": "0.307.3",
"version": "0.308.0",
"dependencies": {
"@codemirror/autocomplete": "^6.19.1",
"@codemirror/language": "^6.11.3",
@ -42,7 +42,7 @@
"@microsoft/fetch-event-source": "^2.0.1",
"@nexucis/fuzzy": "^0.5.1",
"@nexucis/kvsearch": "^0.9.1",
"@prometheus-io/codemirror-promql": "0.307.3",
"@prometheus-io/codemirror-promql": "0.308.0",
"@reduxjs/toolkit": "^2.10.1",
"@tabler/icons-react": "^3.35.0",
"@tanstack/react-query": "^5.90.7",
@ -88,10 +88,10 @@
},
"module/codemirror-promql": {
"name": "@prometheus-io/codemirror-promql",
"version": "0.307.3",
"version": "0.308.0",
"license": "Apache-2.0",
"dependencies": {
"@prometheus-io/lezer-promql": "0.307.3",
"@prometheus-io/lezer-promql": "0.308.0",
"lru-cache": "^11.2.2"
},
"devDependencies": {
@ -121,7 +121,7 @@
},
"module/lezer-promql": {
"name": "@prometheus-io/lezer-promql",
"version": "0.307.3",
"version": "0.308.0",
"license": "Apache-2.0",
"devDependencies": {
"@lezer/generator": "^1.8.0",

View File

@ -1,7 +1,7 @@
{
"name": "prometheus-io",
"description": "Monorepo for the Prometheus UI",
"version": "0.307.3",
"version": "0.308.0",
"private": true,
"scripts": {
"build": "bash build_ui.sh --all",