mirror of
https://github.com/prometheus/prometheus
synced 2024-12-24 23:42:32 +00:00
In histogram_quantile merge buckets with equivalent le values (#5158)
This makes things generally more resilient, and will help with OpenMetrics transitions (and inconsistencies). Signed-off-by: Brian Brazil <brian.brazil@robustperception.io>
This commit is contained in:
parent
a60431f3cd
commit
c66aeb3fff
@ -75,16 +75,18 @@ func bucketQuantile(q float64, buckets buckets) float64 {
|
||||
if q > 1 {
|
||||
return math.Inf(+1)
|
||||
}
|
||||
if len(buckets) < 2 {
|
||||
return math.NaN()
|
||||
}
|
||||
sort.Sort(buckets)
|
||||
if !math.IsInf(buckets[len(buckets)-1].upperBound, +1) {
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
buckets = coalesceBuckets(buckets)
|
||||
ensureMonotonic(buckets)
|
||||
|
||||
if len(buckets) < 2 {
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
rank := q * buckets[len(buckets)-1].count
|
||||
b := sort.Search(len(buckets)-1, func(i int) bool { return buckets[i].count >= rank })
|
||||
|
||||
@ -107,6 +109,25 @@ func bucketQuantile(q float64, buckets buckets) float64 {
|
||||
return bucketStart + (bucketEnd-bucketStart)*(rank/count)
|
||||
}
|
||||
|
||||
// coalesceBuckets merges buckets with the same upper bound.
|
||||
//
|
||||
// The input buckets must be sorted.
|
||||
func coalesceBuckets(buckets buckets) buckets {
|
||||
last := buckets[0]
|
||||
i := 0
|
||||
for _, b := range buckets[1:] {
|
||||
if b.upperBound == last.upperBound {
|
||||
last.count += b.count
|
||||
} else {
|
||||
buckets[i] = last
|
||||
last = b
|
||||
i++
|
||||
}
|
||||
}
|
||||
buckets[i] = last
|
||||
return buckets[:i+1]
|
||||
}
|
||||
|
||||
// The assumption that bucket counts increase monotonically with increasing
|
||||
// upperBound may be violated during:
|
||||
//
|
||||
|
22
promql/testdata/histograms.test
vendored
22
promql/testdata/histograms.test
vendored
@ -31,6 +31,15 @@ load 5m
|
||||
request_duration_seconds_bucket{job="job2", instance="ins2", le="0.2"} 0+7x10
|
||||
request_duration_seconds_bucket{job="job2", instance="ins2", le="+Inf"} 0+9x10
|
||||
|
||||
# Different le representations in one histogram.
|
||||
load 5m
|
||||
mixed_bucket{job="job1", instance="ins1", le="0.1"} 0+1x10
|
||||
mixed_bucket{job="job1", instance="ins1", le="0.2"} 0+1x10
|
||||
mixed_bucket{job="job1", instance="ins1", le="2e-1"} 0+1x10
|
||||
mixed_bucket{job="job1", instance="ins1", le="2.0e-1"} 0+1x10
|
||||
mixed_bucket{job="job1", instance="ins1", le="+Inf"} 0+4x10
|
||||
mixed_bucket{job="job1", instance="ins2", le="+inf"} 0+0x10
|
||||
mixed_bucket{job="job1", instance="ins2", le="+Inf"} 0+0x10
|
||||
|
||||
# Quantile too low.
|
||||
eval instant at 50m histogram_quantile(-0.1, testhistogram_bucket)
|
||||
@ -157,3 +166,16 @@ load 5m
|
||||
# Nonmonotonic buckets
|
||||
eval instant at 50m histogram_quantile(0.99, nonmonotonic_bucket)
|
||||
{} 0.989875
|
||||
|
||||
# Buckets with different representations of the same upper bound.
|
||||
eval instant at 50m histogram_quantile(0.5, rate(mixed_bucket[5m]))
|
||||
{instance="ins1", job="job1"} 0.15
|
||||
{instance="ins2", job="job1"} NaN
|
||||
|
||||
eval instant at 50m histogram_quantile(0.75, rate(mixed_bucket[5m]))
|
||||
{instance="ins1", job="job1"} 0.2
|
||||
{instance="ins2", job="job1"} NaN
|
||||
|
||||
eval instant at 50m histogram_quantile(1, rate(mixed_bucket[5m]))
|
||||
{instance="ins1", job="job1"} 0.2
|
||||
{instance="ins2", job="job1"} NaN
|
||||
|
Loading…
Reference in New Issue
Block a user