[BUGFIX] PromQL: Fix <aggr_over_time> functions with histograms (#15711)

fix aggr_over_time with histograms

Signed-off-by: Neeraj Gartia <neerajgartia211002@gmail.com>

---------

Signed-off-by: Neeraj Gartia <neerajgartia211002@gmail.com>
This commit is contained in:
Neeraj Gartia 2025-01-09 21:08:42 +05:30 committed by GitHub
parent d173c0b61c
commit b3e30d52ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 111 additions and 42 deletions

View file

@ -691,9 +691,15 @@ func funcLastOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNod
// === mad_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
func funcMadOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
if len(vals[0].(Matrix)[0].Floats) == 0 {
samples := vals[0].(Matrix)[0]
var annos annotations.Annotations
if len(samples.Floats) == 0 {
return enh.Out, nil
}
if len(samples.Histograms) > 0 {
metricName := samples.Metric.Get(labels.MetricName)
annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
}
return aggrOverTime(vals, enh, func(s Series) float64 {
values := make(vectorByValueHeap, 0, len(s.Floats))
for _, f := range s.Floats {
@ -705,18 +711,20 @@ func funcMadOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNode
values = append(values, Sample{F: math.Abs(f.F - median)})
}
return quantile(0.5, values)
}), nil
}), annos
}
// === max_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
func funcMaxOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
if len(vals[0].(Matrix)[0].Floats) == 0 {
// TODO(beorn7): The passed values only contain
// histograms. max_over_time ignores histograms for now. If
// there are only histograms, we have to return without adding
// anything to enh.Out.
samples := vals[0].(Matrix)[0]
var annos annotations.Annotations
if len(samples.Floats) == 0 {
return enh.Out, nil
}
if len(samples.Histograms) > 0 {
metricName := samples.Metric.Get(labels.MetricName)
annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
}
return aggrOverTime(vals, enh, func(s Series) float64 {
maxVal := s.Floats[0].F
for _, f := range s.Floats {
@ -725,18 +733,20 @@ func funcMaxOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNode
}
}
return maxVal
}), nil
}), annos
}
// === min_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
func funcMinOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
if len(vals[0].(Matrix)[0].Floats) == 0 {
// TODO(beorn7): The passed values only contain
// histograms. min_over_time ignores histograms for now. If
// there are only histograms, we have to return without adding
// anything to enh.Out.
samples := vals[0].(Matrix)[0]
var annos annotations.Annotations
if len(samples.Floats) == 0 {
return enh.Out, nil
}
if len(samples.Histograms) > 0 {
metricName := samples.Metric.Get(labels.MetricName)
annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
}
return aggrOverTime(vals, enh, func(s Series) float64 {
minVal := s.Floats[0].F
for _, f := range s.Floats {
@ -745,7 +755,7 @@ func funcMinOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNode
}
}
return minVal
}), nil
}), annos
}
// === sum_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
@ -794,10 +804,6 @@ func funcQuantileOverTime(vals []parser.Value, args parser.Expressions, enh *Eva
q := vals[0].(Vector)[0].F
el := vals[1].(Matrix)[0]
if len(el.Floats) == 0 {
// TODO(beorn7): The passed values only contain
// histograms. quantile_over_time ignores histograms for now. If
// there are only histograms, we have to return without adding
// anything to enh.Out.
return enh.Out, nil
}
@ -805,7 +811,10 @@ func funcQuantileOverTime(vals []parser.Value, args parser.Expressions, enh *Eva
if math.IsNaN(q) || q < 0 || q > 1 {
annos.Add(annotations.NewInvalidQuantileWarning(q, args[0].PositionRange()))
}
if len(el.Histograms) > 0 {
metricName := el.Metric.Get(labels.MetricName)
annos.Add(annotations.NewHistogramIgnoredInAggregationInfo(metricName, args[0].PositionRange()))
}
values := make(vectorByValueHeap, 0, len(el.Floats))
for _, f := range el.Floats {
values = append(values, Sample{F: f.F})
@ -815,13 +824,15 @@ func funcQuantileOverTime(vals []parser.Value, args parser.Expressions, enh *Eva
// === stddev_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
func funcStddevOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
if len(vals[0].(Matrix)[0].Floats) == 0 {
// TODO(beorn7): The passed values only contain
// histograms. stddev_over_time ignores histograms for now. If
// there are only histograms, we have to return without adding
// anything to enh.Out.
samples := vals[0].(Matrix)[0]
var annos annotations.Annotations
if len(samples.Floats) == 0 {
return enh.Out, nil
}
if len(samples.Histograms) > 0 {
metricName := samples.Metric.Get(labels.MetricName)
annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
}
return aggrOverTime(vals, enh, func(s Series) float64 {
var count float64
var mean, cMean float64
@ -833,18 +844,20 @@ func funcStddevOverTime(vals []parser.Value, args parser.Expressions, enh *EvalN
aux, cAux = kahanSumInc(delta*(f.F-(mean+cMean)), aux, cAux)
}
return math.Sqrt((aux + cAux) / count)
}), nil
}), annos
}
// === stdvar_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
func funcStdvarOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
if len(vals[0].(Matrix)[0].Floats) == 0 {
// TODO(beorn7): The passed values only contain
// histograms. stdvar_over_time ignores histograms for now. If
// there are only histograms, we have to return without adding
// anything to enh.Out.
samples := vals[0].(Matrix)[0]
var annos annotations.Annotations
if len(samples.Floats) == 0 {
return enh.Out, nil
}
if len(samples.Histograms) > 0 {
metricName := samples.Metric.Get(labels.MetricName)
annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
}
return aggrOverTime(vals, enh, func(s Series) float64 {
var count float64
var mean, cMean float64
@ -856,7 +869,7 @@ func funcStdvarOverTime(vals []parser.Value, args parser.Expressions, enh *EvalN
aux, cAux = kahanSumInc(delta*(f.F-(mean+cMean)), aux, cAux)
}
return (aux + cAux) / count
}), nil
}), annos
}
// === absent(Vector parser.ValueTypeVector) (Vector, Annotations) ===

View file

@ -929,35 +929,58 @@ eval instant at 1m avg_over_time(metric[2m])
# Tests for stddev_over_time and stdvar_over_time.
clear
load 10s
metric 0 8 8 2 3
metric 0 8 8 2 3
metric_histogram{type="only_histogram"} {{schema:1 sum:2 count:3}}x5
metric_histogram{type="mix"} 1 1 1 {{schema:1 sum:2 count:3}} {{schema:1 sum:2 count:3}}
eval instant at 1m stdvar_over_time(metric[2m])
{} 10.56
{} 10.56
eval instant at 1m stddev_over_time(metric[2m])
{} 3.249615
{} 3.249615
eval instant at 1m stddev_over_time((metric[2m]))
{} 3.249615
{} 3.249615
# Tests for stddev_over_time and stdvar_over_time with histograms.
eval instant at 1m stddev_over_time(metric_histogram{type="only_histogram"}[2m])
#empty
eval_info instant at 1m stddev_over_time(metric_histogram{type="mix"}[2m])
{type="mix"} 0
eval instant at 1m stdvar_over_time(metric_histogram{type="only_histogram"}[2m])
#empty
eval_info instant at 1m stdvar_over_time(metric_histogram{type="mix"}[2m])
{type="mix"} 0
# Tests for stddev_over_time and stdvar_over_time #4927.
clear
load 10s
metric 1.5990505637277868 1.5990505637277868 1.5990505637277868
metric 1.5990505637277868 1.5990505637277868 1.5990505637277868
eval instant at 1m stdvar_over_time(metric[1m])
{} 0
{} 0
eval instant at 1m stddev_over_time(metric[1m])
{} 0
{} 0
# Tests for mad_over_time.
clear
load 10s
metric 4 6 2 1 999 1 2
metric 4 6 2 1 999 1 2
metric_histogram{type="only_histogram"} {{schema:1 sum:2 count:3}}x5
metric_histogram{type="mix"} 1 1 1 {{schema:1 sum:2 count:3}} {{schema:1 sum:2 count:3}}
eval instant at 70s mad_over_time(metric[70s])
{} 1
{} 1
eval instant at 70s mad_over_time(metric_histogram{type="only_histogram"}[70s])
#empty
eval_info instant at 70s mad_over_time(metric_histogram{type="mix"}[70s])
{type="mix"} 0
# Tests for quantile_over_time
clear
@ -966,6 +989,8 @@ load 10s
data{test="two samples"} 0 1
data{test="three samples"} 0 1 2
data{test="uneven samples"} 0 1 4
data_histogram{test="only histogram samples"} {{schema:0 sum:1 count:2}}x4
data_histogram{test="mix samples"} 0 1 2 {{schema:0 sum:1 count:2}}x2
eval instant at 1m quantile_over_time(0, data[2m])
{test="two samples"} 0
@ -1007,6 +1032,12 @@ eval_warn instant at 1m (quantile_over_time(2, (data[2m])))
{test="three samples"} +Inf
{test="uneven samples"} +Inf
eval instant at 1m quantile_over_time(0.5, data_histogram{test="only histogram samples"}[2m])
#empty
eval_info instant at 1m quantile_over_time(0.5, data_histogram{test="mix samples"}[2m])
{test="mix samples"} 1
clear
# Test time-related functions.
@ -1120,15 +1151,17 @@ load 5m
eval_fail instant at 0m changes({__name__=~'testmetric1|testmetric2'}[5m])
# Tests for *_over_time
clear
# Tests for *_over_time
load 10s
data{type="numbers"} 2 0 3
data{type="some_nan"} 2 0 NaN
data{type="some_nan2"} 2 NaN 1
data{type="some_nan3"} NaN 0 1
data{type="only_nan"} NaN NaN NaN
data_histogram{type="only_histogram"} {{schema:0 sum:1 count:2}} {{schema:0 sum:2 count:3}} {{schema:0 sum:3 count:4}}
data_histogram{type="mix_samples"} 0 1 {{schema:0 sum:1 count:2}} {{schema:0 sum:2 count:3}}
eval instant at 1m min_over_time(data[2m])
{type="numbers"} 0
@ -1137,6 +1170,12 @@ eval instant at 1m min_over_time(data[2m])
{type="some_nan3"} 0
{type="only_nan"} NaN
eval instant at 1m min_over_time(data_histogram{type="only_histogram"}[2m])
#empty
eval_info instant at 1m min_over_time(data_histogram{type="mix_samples"}[2m])
{type="mix_samples"} 0
eval instant at 1m max_over_time(data[2m])
{type="numbers"} 3
{type="some_nan"} 2
@ -1144,12 +1183,29 @@ eval instant at 1m max_over_time(data[2m])
{type="some_nan3"} 1
{type="only_nan"} NaN
eval instant at 1m last_over_time(data[2m])
eval instant at 1m max_over_time(data_histogram{type="only_histogram"}[2m])
#empty
eval_info instant at 1m max_over_time(data_histogram{type="mix_samples"}[2m])
{type="mix_samples"} 1
eval instant at 1m last_over_time({__name__=~"data(_histogram)?"}[2m])
data{type="numbers"} 3
data{type="some_nan"} NaN
data{type="some_nan2"} 1
data{type="some_nan3"} 1
data{type="only_nan"} NaN
data_histogram{type="only_histogram"} {{schema:0 sum:3 count:4}}
data_histogram{type="mix_samples"} {{schema:0 sum:2 count:3}}
eval instant at 1m count_over_time({__name__=~"data(_histogram)?"}[2m])
{type="numbers"} 3
{type="some_nan"} 3
{type="some_nan2"} 3
{type="some_nan3"} 3
{type="only_nan"} 3
{type="only_histogram"} 3
{type="mix_samples"} 4
clear