diff --git a/docs/querying/functions.md b/docs/querying/functions.md index 419823430..0698658b5 100644 --- a/docs/querying/functions.md +++ b/docs/querying/functions.md @@ -182,8 +182,8 @@ is assumed to be 0 if the upper bound of that bucket is greater than bucket. Otherwise, the upper bound of the lowest bucket is returned for quantiles located in the lowest bucket. -If `b` contains fewer than two buckets, `NaN` is returned. For φ < 0, `-Inf` is -returned. For φ > 1, `+Inf` is returned. +If `b` has 0 observations, `NaN` is returned. If `b` contains fewer than two buckets, +`NaN` is returned. For φ < 0, `-Inf` is returned. For φ > 1, `+Inf` is returned. ## `holt_winters()` diff --git a/promql/quantile.go b/promql/quantile.go index 6fef2c4f6..6e63efab6 100644 --- a/promql/quantile.go +++ b/promql/quantile.go @@ -61,6 +61,8 @@ type metricWithBuckets struct { // happening during evaluations of AST functions, we should report those // explicitly): // +// If 'buckets' has 0 observations, NaN is returned. +// // If 'buckets' has fewer than 2 elements, NaN is returned. // // If the highest bucket is not +Inf, NaN is returned. @@ -86,8 +88,11 @@ func bucketQuantile(q float64, buckets buckets) float64 { if len(buckets) < 2 { return math.NaN() } - - rank := q * buckets[len(buckets)-1].count + observations := buckets[len(buckets)-1].count + if observations == 0 { + return math.NaN() + } + rank := q * observations b := sort.Search(len(buckets)-1, func(i int) bool { return buckets[i].count >= rank }) if b == len(buckets)-1 { diff --git a/promql/testdata/histograms.test b/promql/testdata/histograms.test index 594295c56..b7ec13682 100644 --- a/promql/testdata/histograms.test +++ b/promql/testdata/histograms.test @@ -179,3 +179,11 @@ eval instant at 50m histogram_quantile(0.75, rate(mixed_bucket[5m])) eval instant at 50m histogram_quantile(1, rate(mixed_bucket[5m])) {instance="ins1", job="job1"} 0.2 {instance="ins2", job="job1"} NaN + +load 5m + empty_bucket{le="0.1", job="job1", instance="ins1"} 0x10 + empty_bucket{le="0.2", job="job1", instance="ins1"} 0x10 + empty_bucket{le="+Inf", job="job1", instance="ins1"} 0x10 + +eval instant at 50m histogram_quantile(0.2, rate(empty_bucket[5m])) + {instance="ins1", job="job1"} NaN \ No newline at end of file