mirror of
https://github.com/prometheus/prometheus.git
synced 2025-08-06 14:17:12 +02:00
promql: add ts_of_(max,min,last)_over_time functions
This commit adds the ts_of_(max,min,last)_over_time functions behind the experimental feature flag. Signed-off-by: Michael Hoffmann <mhoffmann@cloudflare.com>
This commit is contained in:
parent
f8508ccafa
commit
a5fa9431d8
@ -847,6 +847,12 @@ additional functions are available:
|
|||||||
|
|
||||||
* `mad_over_time(range-vector)`: the median absolute deviation of all float
|
* `mad_over_time(range-vector)`: the median absolute deviation of all float
|
||||||
samples in the specified interval.
|
samples in the specified interval.
|
||||||
|
* `ts_of_min_over_time(range-vector)`: the timestamp of the last float sample
|
||||||
|
that has the minimum value of all float samples in the specified interval.
|
||||||
|
* `ts_of_max_over_time(range-vector)`: the timestamp of the last float sample
|
||||||
|
that has the maximum value of all float samples in the specified interval.
|
||||||
|
* `ts_of_last_over_time(range-vector)`: the timestamp of last sample in the
|
||||||
|
specified interval.
|
||||||
|
|
||||||
Note that all values in the specified interval have the same weight in the
|
Note that all values in the specified interval have the same weight in the
|
||||||
aggregation even if the values are not equally spaced throughout the interval.
|
aggregation even if the values are not equally spaced throughout the interval.
|
||||||
|
@ -784,8 +784,42 @@ func funcMadOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNode
|
|||||||
}), annos
|
}), annos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === ts_of_last_over_time(Matrix parser.ValueTypeMatrix) (Vector, Notes) ===
|
||||||
|
func funcTsOfLastOverTime(vals []parser.Value, _ parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||||
|
el := vals[0].(Matrix)[0]
|
||||||
|
|
||||||
|
var tf int64
|
||||||
|
if len(el.Floats) > 0 {
|
||||||
|
tf = el.Floats[len(el.Floats)-1].T
|
||||||
|
}
|
||||||
|
|
||||||
|
var th int64
|
||||||
|
if len(el.Histograms) > 0 {
|
||||||
|
th = el.Floats[len(el.Floats)-1].T
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(enh.Out, Sample{
|
||||||
|
Metric: el.Metric,
|
||||||
|
F: float64(max(tf, th)) / 1000,
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// === ts_of_max_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
|
||||||
|
func funcTsOfMaxOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||||
|
return compareOverTime(vals, args, enh, func(cur, maxVal float64) bool {
|
||||||
|
return (cur >= maxVal) || math.IsNaN(maxVal)
|
||||||
|
}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// === ts_of_min_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
|
||||||
|
func funcTsOfMinOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||||
|
return compareOverTime(vals, args, enh, func(cur, maxVal float64) bool {
|
||||||
|
return (cur <= maxVal) || math.IsNaN(maxVal)
|
||||||
|
}, true)
|
||||||
|
}
|
||||||
|
|
||||||
// compareOverTime is a helper used by funcMaxOverTime and funcMinOverTime.
|
// compareOverTime is a helper used by funcMaxOverTime and funcMinOverTime.
|
||||||
func compareOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper, compareFn func(float64, float64) bool) (Vector, annotations.Annotations) {
|
func compareOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper, compareFn func(float64, float64) bool, returnTimestamp bool) (Vector, annotations.Annotations) {
|
||||||
samples := vals[0].(Matrix)[0]
|
samples := vals[0].(Matrix)[0]
|
||||||
var annos annotations.Annotations
|
var annos annotations.Annotations
|
||||||
if len(samples.Floats) == 0 {
|
if len(samples.Floats) == 0 {
|
||||||
@ -797,11 +831,16 @@ func compareOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNode
|
|||||||
}
|
}
|
||||||
return aggrOverTime(vals, enh, func(s Series) float64 {
|
return aggrOverTime(vals, enh, func(s Series) float64 {
|
||||||
maxVal := s.Floats[0].F
|
maxVal := s.Floats[0].F
|
||||||
|
tsOfMax := s.Floats[0].T
|
||||||
for _, f := range s.Floats {
|
for _, f := range s.Floats {
|
||||||
if compareFn(f.F, maxVal) {
|
if compareFn(f.F, maxVal) {
|
||||||
maxVal = f.F
|
maxVal = f.F
|
||||||
|
tsOfMax = f.T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if returnTimestamp {
|
||||||
|
return float64(tsOfMax) / 1000
|
||||||
|
}
|
||||||
return maxVal
|
return maxVal
|
||||||
}), annos
|
}), annos
|
||||||
}
|
}
|
||||||
@ -810,14 +849,14 @@ func compareOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNode
|
|||||||
func funcMaxOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
func funcMaxOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||||
return compareOverTime(vals, args, enh, func(cur, maxVal float64) bool {
|
return compareOverTime(vals, args, enh, func(cur, maxVal float64) bool {
|
||||||
return (cur > maxVal) || math.IsNaN(maxVal)
|
return (cur > maxVal) || math.IsNaN(maxVal)
|
||||||
})
|
}, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// === min_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
|
// === min_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
|
||||||
func funcMinOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
func funcMinOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||||
return compareOverTime(vals, args, enh, func(cur, maxVal float64) bool {
|
return compareOverTime(vals, args, enh, func(cur, maxVal float64) bool {
|
||||||
return (cur < maxVal) || math.IsNaN(maxVal)
|
return (cur < maxVal) || math.IsNaN(maxVal)
|
||||||
})
|
}, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// === sum_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
|
// === sum_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
|
||||||
@ -1723,6 +1762,9 @@ var FunctionCalls = map[string]FunctionCall{
|
|||||||
"mad_over_time": funcMadOverTime,
|
"mad_over_time": funcMadOverTime,
|
||||||
"max_over_time": funcMaxOverTime,
|
"max_over_time": funcMaxOverTime,
|
||||||
"min_over_time": funcMinOverTime,
|
"min_over_time": funcMinOverTime,
|
||||||
|
"ts_of_last_over_time": funcTsOfLastOverTime,
|
||||||
|
"ts_of_max_over_time": funcTsOfMaxOverTime,
|
||||||
|
"ts_of_min_over_time": funcTsOfMinOverTime,
|
||||||
"minute": funcMinute,
|
"minute": funcMinute,
|
||||||
"month": funcMonth,
|
"month": funcMonth,
|
||||||
"pi": funcPi,
|
"pi": funcPi,
|
||||||
|
@ -283,6 +283,24 @@ var Functions = map[string]*Function{
|
|||||||
ArgTypes: []ValueType{ValueTypeMatrix},
|
ArgTypes: []ValueType{ValueTypeMatrix},
|
||||||
ReturnType: ValueTypeVector,
|
ReturnType: ValueTypeVector,
|
||||||
},
|
},
|
||||||
|
"ts_of_max_over_time": {
|
||||||
|
Name: "ts_of_max_over_time",
|
||||||
|
ArgTypes: []ValueType{ValueTypeMatrix},
|
||||||
|
ReturnType: ValueTypeVector,
|
||||||
|
Experimental: true,
|
||||||
|
},
|
||||||
|
"ts_of_min_over_time": {
|
||||||
|
Name: "ts_of_min_over_time",
|
||||||
|
ArgTypes: []ValueType{ValueTypeMatrix},
|
||||||
|
ReturnType: ValueTypeVector,
|
||||||
|
Experimental: true,
|
||||||
|
},
|
||||||
|
"ts_of_last_over_time": {
|
||||||
|
Name: "ts_of_last_over_time",
|
||||||
|
ArgTypes: []ValueType{ValueTypeMatrix},
|
||||||
|
ReturnType: ValueTypeVector,
|
||||||
|
Experimental: true,
|
||||||
|
},
|
||||||
"minute": {
|
"minute": {
|
||||||
Name: "minute",
|
Name: "minute",
|
||||||
ArgTypes: []ValueType{ValueTypeVector},
|
ArgTypes: []ValueType{ValueTypeVector},
|
||||||
|
22
promql/promqltest/testdata/functions.test
vendored
22
promql/promqltest/testdata/functions.test
vendored
@ -1186,6 +1186,28 @@ eval instant at 70s mad_over_time(metric_histogram{type="only_histogram"}[70s])
|
|||||||
eval_info instant at 70s mad_over_time(metric_histogram{type="mix"}[70s])
|
eval_info instant at 70s mad_over_time(metric_histogram{type="mix"}[70s])
|
||||||
{type="mix"} 0
|
{type="mix"} 0
|
||||||
|
|
||||||
|
# Tests for ts_of_max_over_time and ts_of_min_over_time. Using odd scrape interval to test for rounding bugs.
|
||||||
|
clear
|
||||||
|
load 10s53ms
|
||||||
|
metric 1 2 3 0 5 6 2 1 4
|
||||||
|
|
||||||
|
eval instant at 90s ts_of_min_over_time(metric[90s])
|
||||||
|
{} 30.159
|
||||||
|
|
||||||
|
eval instant at 90s ts_of_max_over_time(metric[90s])
|
||||||
|
{} 50.265
|
||||||
|
|
||||||
|
# Tests for ts_of_last_over_time. Using odd load interval to test for rounding bugs.
|
||||||
|
clear
|
||||||
|
load 10s53ms
|
||||||
|
metric 1 2 3 _ _
|
||||||
|
|
||||||
|
eval instant at 90s ts_of_last_over_time(metric[90s])
|
||||||
|
{} 20.106
|
||||||
|
|
||||||
|
eval instant at 95s ts_of_last_over_time(metric[90s])
|
||||||
|
{} 20.106
|
||||||
|
|
||||||
# Tests for quantile_over_time
|
# Tests for quantile_over_time
|
||||||
clear
|
clear
|
||||||
|
|
||||||
|
@ -347,6 +347,24 @@ export const functionIdentifierTerms = [
|
|||||||
info: 'Return the minimum value over time for input series',
|
info: 'Return the minimum value over time for input series',
|
||||||
type: 'function',
|
type: 'function',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'ts_of_max_over_time',
|
||||||
|
detail: 'function',
|
||||||
|
info: 'Return the timestamp of the maximum value over time for input series',
|
||||||
|
type: 'function',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'ts_of_min_over_time',
|
||||||
|
detail: 'function',
|
||||||
|
info: 'Return the timestamp of the minimum value over time for input series',
|
||||||
|
type: 'function',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'ts_of_last_over_time',
|
||||||
|
detail: 'function',
|
||||||
|
info: 'Return the timestamp of the last value over time for input series',
|
||||||
|
type: 'function',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'minute',
|
label: 'minute',
|
||||||
detail: 'function',
|
detail: 'function',
|
||||||
|
@ -105,6 +105,21 @@ describe('promql operations', () => {
|
|||||||
expectedValueType: ValueType.vector,
|
expectedValueType: ValueType.vector,
|
||||||
expectedDiag: [] as Diagnostic[],
|
expectedDiag: [] as Diagnostic[],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
expr: 'ts_of_max_over_time(rate(metric_name[5m])[1h:] offset 1m)',
|
||||||
|
expectedValueType: ValueType.vector,
|
||||||
|
expectedDiag: [] as Diagnostic[],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: 'ts_of_min_over_time(rate(metric_name[5m])[1h:] offset 1m)',
|
||||||
|
expectedValueType: ValueType.vector,
|
||||||
|
expectedDiag: [] as Diagnostic[],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: 'ts_of_last_over_time(rate(metric_name[5m])[1h:] offset 1m)',
|
||||||
|
expectedValueType: ValueType.vector,
|
||||||
|
expectedDiag: [] as Diagnostic[],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
expr: 'foo * bar',
|
expr: 'foo * bar',
|
||||||
expectedValueType: ValueType.vector,
|
expectedValueType: ValueType.vector,
|
||||||
|
@ -61,6 +61,9 @@ import {
|
|||||||
MadOverTime,
|
MadOverTime,
|
||||||
MaxOverTime,
|
MaxOverTime,
|
||||||
MinOverTime,
|
MinOverTime,
|
||||||
|
TsOfMaxOverTime,
|
||||||
|
TsOfMinOverTime,
|
||||||
|
TsOfLastOverTime,
|
||||||
Minute,
|
Minute,
|
||||||
Month,
|
Month,
|
||||||
Pi,
|
Pi,
|
||||||
@ -403,6 +406,24 @@ const promqlFunctions: { [key: number]: PromQLFunction } = {
|
|||||||
variadic: 0,
|
variadic: 0,
|
||||||
returnType: ValueType.vector,
|
returnType: ValueType.vector,
|
||||||
},
|
},
|
||||||
|
[TsOfMaxOverTime]: {
|
||||||
|
name: 'ts_of_max_over_time',
|
||||||
|
argTypes: [ValueType.matrix],
|
||||||
|
variadic: 0,
|
||||||
|
returnType: ValueType.vector,
|
||||||
|
},
|
||||||
|
[TsOfMinOverTime]: {
|
||||||
|
name: 'ts_of_min_over_time',
|
||||||
|
argTypes: [ValueType.matrix],
|
||||||
|
variadic: 0,
|
||||||
|
returnType: ValueType.vector,
|
||||||
|
},
|
||||||
|
[TsOfLastOverTime]: {
|
||||||
|
name: 'ts_of_last_over_time',
|
||||||
|
argTypes: [ValueType.matrix],
|
||||||
|
variadic: 0,
|
||||||
|
returnType: ValueType.vector,
|
||||||
|
},
|
||||||
[Minute]: {
|
[Minute]: {
|
||||||
name: 'minute',
|
name: 'minute',
|
||||||
argTypes: [ValueType.vector],
|
argTypes: [ValueType.vector],
|
||||||
|
@ -156,6 +156,9 @@ FunctionIdentifier {
|
|||||||
MadOverTime |
|
MadOverTime |
|
||||||
MaxOverTime |
|
MaxOverTime |
|
||||||
MinOverTime |
|
MinOverTime |
|
||||||
|
TsOfMaxOverTime |
|
||||||
|
TsOfMinOverTime |
|
||||||
|
TsOfLastOverTime |
|
||||||
Minute |
|
Minute |
|
||||||
Month |
|
Month |
|
||||||
Pi |
|
Pi |
|
||||||
@ -404,6 +407,9 @@ NumberDurationLiteralInDurationContext {
|
|||||||
MadOverTime { condFn<"mad_over_time"> }
|
MadOverTime { condFn<"mad_over_time"> }
|
||||||
MaxOverTime { condFn<"max_over_time"> }
|
MaxOverTime { condFn<"max_over_time"> }
|
||||||
MinOverTime { condFn<"min_over_time"> }
|
MinOverTime { condFn<"min_over_time"> }
|
||||||
|
TsOfMaxOverTime { condFn<"ts_of_max_over_time"> }
|
||||||
|
TsOfMinOverTime { condFn<"ts_of_min_over_time"> }
|
||||||
|
TsOfLastOverTime { condFn<"ts_of_last_over_time"> }
|
||||||
Minute { condFn<"minute"> }
|
Minute { condFn<"minute"> }
|
||||||
Month { condFn<"month"> }
|
Month { condFn<"month"> }
|
||||||
Pi { condFn<"pi">}
|
Pi { condFn<"pi">}
|
||||||
|
Loading…
Reference in New Issue
Block a user