Skip to content

Commit e1ae427

Browse files
execution: update histogram quantile after promql bump (#306)
Signed-off-by: Michael Hoffmann <[email protected]>
1 parent 14f9f74 commit e1ae427

File tree

2 files changed

+32
-11
lines changed

2 files changed

+32
-11
lines changed

engine/engine_test.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4541,12 +4541,10 @@ func TestNativeHistograms(t *testing.T) {
45414541
name: "histogram_count",
45424542
query: "histogram_count(native_histogram_series)",
45434543
},
4544-
// TODO(mhoffm): fails after bumping to github.com/prometheus/prometheus v0.46.1-0.20230818184859-4d8e380269da.
4545-
// Investigate in followup PR.
4546-
// {
4547-
// name: "histogram_quantile",
4548-
// query: "histogram_quantile(0.7, native_histogram_series)",
4549-
// },
4544+
{
4545+
name: "histogram_quantile",
4546+
query: "histogram_quantile(0.7, native_histogram_series)",
4547+
},
45504548
{
45514549
name: "histogram_fraction",
45524550
query: "histogram_fraction(0, 0.2, native_histogram_series)",

execution/function/quantile.go

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,21 @@ func histogramQuantile(q float64, h *histogram.FloatHistogram) float64 {
182182
var (
183183
bucket histogram.Bucket[float64]
184184
count float64
185-
it = h.AllBucketIterator()
186-
rank = q * h.Count
185+
it histogram.BucketIterator[float64]
186+
rank float64
187187
)
188+
189+
// if there are NaN observations in the histogram (h.Sum is NaN), use the forward iterator
190+
// if the q < 0.5, use the forward iterator
191+
// if the q >= 0.5, use the reverse iterator
192+
if math.IsNaN(h.Sum) || q < 0.5 {
193+
it = h.AllBucketIterator()
194+
rank = q * h.Count
195+
} else {
196+
it = h.AllReverseBucketIterator()
197+
rank = (1 - q) * h.Count
198+
}
199+
188200
for it.Next() {
189201
bucket = it.At()
190202
count += bucket.Count
@@ -193,11 +205,12 @@ func histogramQuantile(q float64, h *histogram.FloatHistogram) float64 {
193205
}
194206
}
195207
if bucket.Lower < 0 && bucket.Upper > 0 {
196-
if len(h.NegativeBuckets) == 0 && len(h.PositiveBuckets) > 0 {
208+
switch {
209+
case len(h.NegativeBuckets) == 0 && len(h.PositiveBuckets) > 0:
197210
// The result is in the zero bucket and the histogram has only
198211
// positive buckets. So we consider 0 to be the lower bound.
199212
bucket.Lower = 0
200-
} else if len(h.PositiveBuckets) == 0 && len(h.NegativeBuckets) > 0 {
213+
case len(h.PositiveBuckets) == 0 && len(h.NegativeBuckets) > 0:
201214
// The result is in the zero bucket and the histogram has only
202215
// negative buckets. So we consider 0 to be the upper bound.
203216
bucket.Upper = 0
@@ -216,7 +229,17 @@ func histogramQuantile(q float64, h *histogram.FloatHistogram) float64 {
216229
return bucket.Upper
217230
}
218231

219-
rank -= count - bucket.Count
232+
// NaN observations increase h.Count but not the total number of
233+
// observations in the buckets. Therefore, we have to use the forward
234+
// iterator to find percentiles. We recognize histograms containing NaN
235+
// observations by checking if their h.Sum is NaN.
236+
if math.IsNaN(h.Sum) || q < 0.5 {
237+
rank -= count - bucket.Count
238+
} else {
239+
rank = count - rank
240+
}
241+
242+
// TODO(codesome): Use a better estimation than linear.
220243
return bucket.Lower + (bucket.Upper-bucket.Lower)*(rank/bucket.Count)
221244
}
222245

0 commit comments

Comments
 (0)