promql: add histogram_avg function (#13467)

Add histogram_avg function

---------

Signed-off-by: Faustas Butkus <faustas.butkus@gmail.com>
Signed-off-by: Björn Rabenstein <github@rabenste.in>
Co-authored-by: Björn Rabenstein <github@rabenste.in>
This commit is contained in:
Faustas Butkus 2024-02-01 19:28:42 +02:00 committed by GitHub
parent c006c57efc
commit 6feffeb92e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 106 additions and 8 deletions

View file

@ -175,6 +175,27 @@ Special cases are:
`floor(v instant-vector)` rounds the sample values of all elements in `v` down
to the nearest integer.
## `histogram_avg()`
_This function only acts on native histograms, which are an experimental
feature. The behavior of this function may change in future versions of
Prometheus, including its removal from PromQL._
`histogram_avg(v instant-vector)` returns the arithmetic average of observed values stored in
a native histogram. Samples that are not native histograms are ignored and do
not show up in the returned vector.
Use `histogram_avg` as demonstrated below to compute the average request duration
over a 5-minute window from a native histogram:
histogram_avg(rate(http_request_duration_seconds[5m]))
Which is equivalent to the following query:
histogram_sum(rate(http_request_duration_seconds[5m]))
/
histogram_count(rate(http_request_duration_seconds[5m]))
## `histogram_count()` and `histogram_sum()`
_Both functions only act on native histograms, which are an experimental
@ -193,13 +214,6 @@ Use `histogram_count` in the following way to calculate a rate of observations
histogram_count(rate(http_request_duration_seconds[10m]))
The additional use of `histogram_sum` enables the calculation of the average of
observed values (in this case corresponding to “average request duration”):
histogram_sum(rate(http_request_duration_seconds[10m]))
/
histogram_count(rate(http_request_duration_seconds[10m]))
## `histogram_fraction()`
_This function only acts on native histograms, which are an experimental

View file

@ -1081,6 +1081,23 @@ func funcHistogramSum(vals []parser.Value, args parser.Expressions, enh *EvalNod
return enh.Out, nil
}
// === histogram_avg(Vector parser.ValueTypeVector) (Vector, Annotations) ===
func funcHistogramAvg(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
inVec := vals[0].(Vector)
for _, sample := range inVec {
// Skip non-histogram samples.
if sample.H == nil {
continue
}
enh.Out = append(enh.Out, Sample{
Metric: sample.Metric.DropMetricName(),
F: sample.H.Sum / sample.H.Count,
})
}
return enh.Out, nil
}
// === histogram_stddev(Vector parser.ValueTypeVector) (Vector, Annotations) ===
func funcHistogramStdDev(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
inVec := vals[0].(Vector)
@ -1532,6 +1549,7 @@ var FunctionCalls = map[string]FunctionCall{
"deriv": funcDeriv,
"exp": funcExp,
"floor": funcFloor,
"histogram_avg": funcHistogramAvg,
"histogram_count": funcHistogramCount,
"histogram_fraction": funcHistogramFraction,
"histogram_quantile": funcHistogramQuantile,

View file

@ -167,6 +167,11 @@ var Functions = map[string]*Function{
ArgTypes: []ValueType{ValueTypeVector},
ReturnType: ValueTypeVector,
},
"histogram_avg": {
Name: "histogram_avg",
ArgTypes: []ValueType{ValueTypeVector},
ReturnType: ValueTypeVector,
},
"histogram_count": {
Name: "histogram_count",
ArgTypes: []ValueType{ValueTypeVector},

View file

@ -11,6 +11,9 @@ eval instant at 5m histogram_count(empty_histogram)
eval instant at 5m histogram_sum(empty_histogram)
{} 0
eval instant at 5m histogram_avg(empty_histogram)
{} NaN
eval instant at 5m histogram_fraction(-Inf, +Inf, empty_histogram)
{} NaN
@ -31,6 +34,10 @@ eval instant at 5m histogram_count(single_histogram)
eval instant at 5m histogram_sum(single_histogram)
{} 5
# histogram_avg calculates the average from sum and count properties.
eval instant at 5m histogram_avg(single_histogram)
{} 1.25
# We expect half of the values to fall in the range 1 < x <= 2.
eval instant at 5m histogram_fraction(1, 2, single_histogram)
{} 0.5
@ -55,6 +62,9 @@ eval instant at 5m histogram_count(multi_histogram)
eval instant at 5m histogram_sum(multi_histogram)
{} 5
eval instant at 5m histogram_avg(multi_histogram)
{} 1.25
eval instant at 5m histogram_fraction(1, 2, multi_histogram)
{} 0.5
@ -69,6 +79,9 @@ eval instant at 50m histogram_count(multi_histogram)
eval instant at 50m histogram_sum(multi_histogram)
{} 5
eval instant at 50m histogram_avg(multi_histogram)
{} 1.25
eval instant at 50m histogram_fraction(1, 2, multi_histogram)
{} 0.5
@ -89,6 +102,9 @@ eval instant at 5m histogram_count(incr_histogram)
eval instant at 5m histogram_sum(incr_histogram)
{} 6
eval instant at 5m histogram_avg(incr_histogram)
{} 1.2
# We expect 3/5ths of the values to fall in the range 1 < x <= 2.
eval instant at 5m histogram_fraction(1, 2, incr_histogram)
{} 0.6
@ -106,6 +122,9 @@ eval instant at 50m histogram_count(incr_histogram)
eval instant at 50m histogram_sum(incr_histogram)
{} 24
eval instant at 50m histogram_avg(incr_histogram)
{} 1.7142857142857142
# We expect 12/14ths of the values to fall in the range 1 < x <= 2.
eval instant at 50m histogram_fraction(1, 2, incr_histogram)
{} 0.8571428571428571
@ -140,6 +159,9 @@ eval instant at 5m histogram_count(low_res_histogram)
eval instant at 5m histogram_sum(low_res_histogram)
{} 8
eval instant at 5m histogram_avg(low_res_histogram)
{} 1.6
# We expect all values to fall into the lower-resolution bucket with the range 1 < x <= 4.
eval instant at 5m histogram_fraction(1, 4, low_res_histogram)
{} 1
@ -157,6 +179,9 @@ eval instant at 5m histogram_count(single_zero_histogram)
eval instant at 5m histogram_sum(single_zero_histogram)
{} 0.25
eval instant at 5m histogram_avg(single_zero_histogram)
{} 0.25
# When only the zero bucket is populated, or there are negative buckets, the distribution is assumed to be equally
# distributed around zero; i.e. that there are an equal number of positive and negative observations. Therefore the
# entire distribution must lie within the full range of the zero bucket, in this case: -0.5 < x <= +0.5.
@ -179,6 +204,9 @@ eval instant at 5m histogram_count(negative_histogram)
eval instant at 5m histogram_sum(negative_histogram)
{} -5
eval instant at 5m histogram_avg(negative_histogram)
{} -1.25
# We expect half of the values to fall in the range -2 < x <= -1.
eval instant at 5m histogram_fraction(-2, -1, negative_histogram)
{} 0.5
@ -199,6 +227,9 @@ eval instant at 10m histogram_count(two_samples_histogram)
eval instant at 10m histogram_sum(two_samples_histogram)
{} -4
eval instant at 10m histogram_avg(two_samples_histogram)
{} -1
eval instant at 10m histogram_fraction(-2, -1, two_samples_histogram)
{} 0.5
@ -217,6 +248,9 @@ eval instant at 5m histogram_count(balanced_histogram)
eval instant at 5m histogram_sum(balanced_histogram)
{} 0
eval instant at 5m histogram_avg(balanced_histogram)
{} 0
eval instant at 5m histogram_fraction(0, 4, balanced_histogram)
{} 0.5

View file

@ -215,6 +215,12 @@ export const functionIdentifierTerms = [
info: 'Round down values of input series to nearest integer',
type: 'function',
},
{
label: 'histogram_avg',
detail: 'function',
info: 'Return the average of observations from a native histogram (experimental feature)',
type: 'function',
},
{
label: 'histogram_count',
detail: 'function',

View file

@ -757,6 +757,18 @@ describe('promql operations', () => {
expectedValueType: ValueType.vector,
expectedDiag: [],
},
{
expr:
'histogram_avg( # Root of the query, final result, returns the average of observations.\n' +
' sum by(method, path) ( # Argument to histogram_avg(), an aggregated histogram.\n' +
' rate( # Argument to sum(), the per-second increase of a histogram over 5m.\n' +
' demo_api_request_duration_seconds{job="demo"}[5m] # Argument to rate(), a vector of sparse histogram series over the last 5m.\n' +
' )\n' +
' )\n' +
')',
expectedValueType: ValueType.vector,
expectedDiag: [],
},
{
expr:
'histogram_stddev( # Root of the query, final result, returns the standard deviation of observations.\n' +

View file

@ -39,6 +39,7 @@ import {
Deriv,
Exp,
Floor,
HistogramAvg,
HistogramCount,
HistogramFraction,
HistogramQuantile,
@ -269,6 +270,12 @@ const promqlFunctions: { [key: number]: PromQLFunction } = {
variadic: 0,
returnType: ValueType.vector,
},
[HistogramAvg]: {
name: 'histogram_avg',
argTypes: [ValueType.vector],
variadic: 0,
returnType: ValueType.vector,
},
[HistogramCount]: {
name: 'histogram_count',
argTypes: [ValueType.vector],

View file

@ -20,7 +20,7 @@ export const promQLHighLight = styleTags({
NumberLiteral: tags.number,
Duration: tags.number,
Identifier: tags.variableName,
'Abs Absent AbsentOverTime Acos Acosh Asin Asinh Atan Atanh AvgOverTime Ceil Changes Clamp ClampMax ClampMin Cos Cosh CountOverTime DaysInMonth DayOfMonth DayOfWeek DayOfYear Deg Delta Deriv Exp Floor HistogramCount HistogramFraction HistogramQuantile HistogramSum HoltWinters Hour Idelta Increase Irate LabelReplace LabelJoin LastOverTime Ln Log10 Log2 MaxOverTime MinOverTime Minute Month Pi PredictLinear PresentOverTime QuantileOverTime Rad Rate Resets Round Scalar Sgn Sin Sinh Sort SortDesc SortByLabel SortByLabelDesc Sqrt StddevOverTime StdvarOverTime SumOverTime Tan Tanh Time Timestamp Vector Year':
'Abs Absent AbsentOverTime Acos Acosh Asin Asinh Atan Atanh AvgOverTime Ceil Changes Clamp ClampMax ClampMin Cos Cosh CountOverTime DaysInMonth DayOfMonth DayOfWeek DayOfYear Deg Delta Deriv Exp Floor HistogramAvg HistogramCount HistogramFraction HistogramQuantile HistogramSum HoltWinters Hour Idelta Increase Irate LabelReplace LabelJoin LastOverTime Ln Log10 Log2 MaxOverTime MinOverTime Minute Month Pi PredictLinear PresentOverTime QuantileOverTime Rad Rate Resets Round Scalar Sgn Sin Sinh Sort SortDesc SortByLabel SortByLabelDesc Sqrt StddevOverTime StdvarOverTime SumOverTime Tan Tanh Time Timestamp Vector Year':
tags.function(tags.variableName),
'Avg Bottomk Count Count_values Group Max Min Quantile Stddev Stdvar Sum Topk': tags.operatorKeyword,
'By Without Bool On Ignoring GroupLeft GroupRight Offset Start End': tags.modifier,

View file

@ -138,6 +138,7 @@ FunctionIdentifier {
HistogramStdDev |
HistogramStdVar |
HistogramSum |
HistogramAvg |
HoltWinters |
Hour |
Idelta |
@ -364,6 +365,7 @@ NumberLiteral {
Deriv { condFn<"deriv"> }
Exp { condFn<"exp"> }
Floor { condFn<"floor"> }
HistogramAvg { condFn<"histogram_avg"> }
HistogramCount { condFn<"histogram_count"> }
HistogramFraction { condFn<"histogram_fraction"> }
HistogramQuantile { condFn<"histogram_quantile"> }