mirror of
https://github.com/prometheus/prometheus.git
synced 2026-06-11 09:30:13 -04:00
Merge pull request #18687 from roidelapluie/roidelapluie/greatest_least
Duration expression: Rename min/max -> min_of, max_of
This commit is contained in:
commit
aa260fcde5
18 changed files with 878 additions and 684 deletions
2
cmd/prometheus/testdata/features.json
vendored
2
cmd/prometheus/testdata/features.json
vendored
|
|
@ -100,7 +100,9 @@
|
|||
"log10": true,
|
||||
"log2": true,
|
||||
"mad_over_time": false,
|
||||
"max_of": false,
|
||||
"max_over_time": true,
|
||||
"min_of": false,
|
||||
"min_over_time": true,
|
||||
"minute": true,
|
||||
"month": true,
|
||||
|
|
|
|||
|
|
@ -236,7 +236,10 @@ For a **range query**, it resolves to the full range of the query (end time - st
|
|||
For an **instant query**, it resolves to `0s`.
|
||||
This is particularly useful in combination with `@end()` to look back over the entire query range, e.g., `max_over_time(metric[range()] @ end())`.
|
||||
|
||||
`min(<duration>, <duration>)` and `max(<duration>, <duration>)` can be used to find the minimum or maximum of two duration expressions.
|
||||
`min_of(<duration>, <duration>)` and `max_of(<duration>, <duration>)` select between two duration expressions.
|
||||
`min_of` returns the smaller of the two, which is useful for capping a duration at a maximum value.
|
||||
`max_of` returns the larger of the two, which is useful for enforcing a minimum value.
|
||||
For example, `max_of(step(), 5s)` ensures the duration is never shorter than `5s`, while `min_of(range(), 1h)` caps the duration at `1h`.
|
||||
|
||||
**Note**: Duration expressions are not supported in the @ timestamp operator.
|
||||
|
||||
|
|
@ -258,8 +261,8 @@ Examples of equivalent durations:
|
|||
* `4h % 3h` is equivalent to `1h` or `3600s`
|
||||
* `(2 ^ 3) * 1m` is equivalent to `8m` or `480s`
|
||||
* `step() + 1` is equivalent to the query step width increased by 1s.
|
||||
* `max(step(), 5s)` is equivalent to the larger of the query step width and `5s`.
|
||||
* `min(2 * step() + 5s, 5m)` is equivalent to the smaller of twice the query step increased by `5s` and `5m`.
|
||||
* `max_of(step(), 5s)` is equivalent to the larger of the query step width and `5s`.
|
||||
* `min_of(2 * step() + 5s, 5m)` is equivalent to the smaller of twice the query step increased by `5s` and `5m`.
|
||||
|
||||
|
||||
## OTLP Native Delta Support
|
||||
|
|
|
|||
|
|
@ -693,6 +693,24 @@ This second example has the same effect than the first example, and illustrates
|
|||
label_replace(up{job="api-server",service="a:c"}, "foo", "$name", "service", "(?P<name>.*):(?P<version>.*)")
|
||||
```
|
||||
|
||||
## `max_of()`
|
||||
|
||||
**This function has to be enabled via the [feature
|
||||
flag](../feature_flags.md#experimental-promql-functions)
|
||||
`--enable-feature=promql-experimental-functions`.**
|
||||
|
||||
`max_of(a scalar, b scalar)` returns the larger of the two scalar values `a`
|
||||
and `b`.
|
||||
|
||||
## `min_of()`
|
||||
|
||||
**This function has to be enabled via the [feature
|
||||
flag](../feature_flags.md#experimental-promql-functions)
|
||||
`--enable-feature=promql-experimental-functions`.**
|
||||
|
||||
`min_of(a scalar, b scalar)` returns the smaller of the two scalar values `a`
|
||||
and `b`.
|
||||
|
||||
## `ln()`
|
||||
|
||||
`ln(v instant-vector)` calculates the natural logarithm for all float samples
|
||||
|
|
|
|||
|
|
@ -135,9 +135,9 @@ func (v *durationVisitor) evaluateDurationExpr(expr parser.Expr) (float64, error
|
|||
return float64(v.step.Seconds()), nil
|
||||
case parser.RANGE:
|
||||
return float64(v.queryRange.Seconds()), nil
|
||||
case parser.MIN:
|
||||
case parser.MIN_OF:
|
||||
return math.Min(lhs, rhs), nil
|
||||
case parser.MAX:
|
||||
case parser.MAX_OF:
|
||||
return math.Max(lhs, rhs), nil
|
||||
case parser.ADD:
|
||||
if n.LHS == nil {
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ func TestCalculateDuration(t *testing.T) {
|
|||
expected: 150 * time.Second,
|
||||
},
|
||||
{
|
||||
name: "max of step and range",
|
||||
name: "max_of of step and range",
|
||||
expr: &parser.DurationExpr{
|
||||
LHS: &parser.DurationExpr{
|
||||
Op: parser.STEP,
|
||||
|
|
@ -236,7 +236,7 @@ func TestCalculateDuration(t *testing.T) {
|
|||
RHS: &parser.DurationExpr{
|
||||
Op: parser.RANGE,
|
||||
},
|
||||
Op: parser.MAX,
|
||||
Op: parser.MAX_OF,
|
||||
},
|
||||
expected: 5 * time.Minute,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1708,6 +1708,16 @@ func funcSqrt(vectorVals []Vector, _ Matrix, _ parser.Expressions, enh *EvalNode
|
|||
return simpleFloatFunc(vectorVals, enh, math.Sqrt), nil
|
||||
}
|
||||
|
||||
// === max_of(a, b parser.ValueTypeScalar) Scalar ===
|
||||
func funcMaxOf(vectorVals []Vector, _ Matrix, _ parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return append(enh.Out, Sample{F: math.Max(vectorVals[0][0].F, vectorVals[1][0].F)}), nil
|
||||
}
|
||||
|
||||
// === min_of(a, b parser.ValueTypeScalar) Scalar ===
|
||||
func funcMinOf(vectorVals []Vector, _ Matrix, _ parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return append(enh.Out, Sample{F: math.Min(vectorVals[0][0].F, vectorVals[1][0].F)}), nil
|
||||
}
|
||||
|
||||
// === ln(Vector parser.ValueTypeVector) (Vector, Annotations) ===
|
||||
func funcLn(vectorVals []Vector, _ Matrix, _ parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
return simpleFloatFunc(vectorVals, enh, math.Log), nil
|
||||
|
|
@ -2565,8 +2575,10 @@ var FunctionCalls = map[string]FunctionCall{
|
|||
"increase": funcIncrease,
|
||||
"info": nil,
|
||||
"irate": funcIrate,
|
||||
"max_of": funcMaxOf,
|
||||
"label_replace": nil, // evalLabelReplace not called via this map.
|
||||
"label_join": nil, // evalLabelJoin not called via this map.
|
||||
"min_of": funcMinOf,
|
||||
"ln": funcLn,
|
||||
"log10": funcLog10,
|
||||
"log2": funcLog2,
|
||||
|
|
|
|||
|
|
@ -263,11 +263,23 @@ var Functions = map[string]*Function{
|
|||
Variadic: -1,
|
||||
ReturnType: ValueTypeVector,
|
||||
},
|
||||
"max_of": {
|
||||
Name: "max_of",
|
||||
ArgTypes: []ValueType{ValueTypeScalar, ValueTypeScalar},
|
||||
ReturnType: ValueTypeScalar,
|
||||
Experimental: true,
|
||||
},
|
||||
"last_over_time": {
|
||||
Name: "last_over_time",
|
||||
ArgTypes: []ValueType{ValueTypeMatrix},
|
||||
ReturnType: ValueTypeVector,
|
||||
},
|
||||
"min_of": {
|
||||
Name: "min_of",
|
||||
ArgTypes: []ValueType{ValueTypeScalar, ValueTypeScalar},
|
||||
ReturnType: ValueTypeScalar,
|
||||
Experimental: true,
|
||||
},
|
||||
"ln": {
|
||||
Name: "ln",
|
||||
ArgTypes: []ValueType{ValueTypeVector},
|
||||
|
|
|
|||
|
|
@ -159,6 +159,8 @@ START
|
|||
END
|
||||
STEP
|
||||
RANGE
|
||||
MAX_OF
|
||||
MIN_OF
|
||||
%token preprocessorEnd
|
||||
|
||||
// Counter reset hints.
|
||||
|
|
@ -183,7 +185,7 @@ START_METRIC_SELECTOR
|
|||
// Type definitions for grammar rules.
|
||||
%type <matchers> label_match_list
|
||||
%type <matcher> label_matcher
|
||||
%type <item> aggregate_op grouping_label match_op maybe_label metric_identifier unary_op at_modifier_preprocessors string_identifier counter_reset_hint min_max
|
||||
%type <item> aggregate_op grouping_label match_op maybe_label metric_identifier unary_op at_modifier_preprocessors string_identifier counter_reset_hint max_of_min_of
|
||||
%type <labels> label_set metric
|
||||
%type <lblList> label_set_list
|
||||
%type <label> label_set_item
|
||||
|
|
@ -526,6 +528,24 @@ function_call : IDENTIFIER function_call_body
|
|||
},
|
||||
}
|
||||
}
|
||||
| max_of_min_of function_call_body
|
||||
{
|
||||
fn, exist := getFunction($1.Val, yylex.(*parser).functions)
|
||||
if !exist{
|
||||
yylex.(*parser).addParseErrf($1.PositionRange(),"unknown function with name %q", $1.Val)
|
||||
}
|
||||
if fn != nil && fn.Experimental && !yylex.(*parser).options.EnableExperimentalFunctions {
|
||||
yylex.(*parser).addParseErrf($1.PositionRange(),"function %q is not enabled", $1.Val)
|
||||
}
|
||||
$$ = &Call{
|
||||
Func: fn,
|
||||
Args: $2.(Expressions),
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: $1.PositionRange().Start,
|
||||
End: yylex.(*parser).lastClosing,
|
||||
},
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
function_call_body: LEFT_PAREN function_call_args RIGHT_PAREN
|
||||
|
|
@ -814,7 +834,7 @@ metric : metric_identifier label_set
|
|||
;
|
||||
|
||||
|
||||
metric_identifier: AVG | BOTTOMK | BY | COUNT | COUNT_VALUES | FILL | FILL_LEFT | FILL_RIGHT | GROUP | IDENTIFIER | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | QUANTILE | STDDEV | STDVAR | SUM | TOPK | WITHOUT | START | END | LIMITK | LIMIT_RATIO | STEP | RANGE | ANCHORED | SMOOTHED;
|
||||
metric_identifier: AVG | BOTTOMK | BY | COUNT | COUNT_VALUES | FILL | FILL_LEFT | FILL_RIGHT | GROUP | IDENTIFIER | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | QUANTILE | STDDEV | STDVAR | SUM | TOPK | WITHOUT | START | END | LIMITK | LIMIT_RATIO | STEP | RANGE | ANCHORED | SMOOTHED | MAX_OF | MIN_OF;
|
||||
|
||||
label_set : LEFT_BRACE label_set_list RIGHT_BRACE
|
||||
{ $$ = labels.New($2...) }
|
||||
|
|
@ -1072,7 +1092,7 @@ counter_reset_hint : UNKNOWN_COUNTER_RESET | COUNTER_RESET | NOT_COUNTER_RESET |
|
|||
aggregate_op : AVG | BOTTOMK | COUNT | COUNT_VALUES | GROUP | MAX | MIN | QUANTILE | STDDEV | STDVAR | SUM | TOPK | LIMITK | LIMIT_RATIO;
|
||||
|
||||
// Inside of grouping options label names can be recognized as keywords by the lexer. This is a list of keywords that could also be a label name.
|
||||
maybe_label : AVG | BOOL | BOTTOMK | BY | COUNT | COUNT_VALUES | GROUP | GROUP_LEFT | GROUP_RIGHT | FILL | FILL_LEFT | FILL_RIGHT | IDENTIFIER | IGNORING | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | ON | QUANTILE | STDDEV | STDVAR | SUM | TOPK | START | END | ATAN2 | LIMITK | LIMIT_RATIO | STEP | RANGE | ANCHORED | SMOOTHED;
|
||||
maybe_label : AVG | BOOL | BOTTOMK | BY | COUNT | COUNT_VALUES | GROUP | GROUP_LEFT | GROUP_RIGHT | FILL | FILL_LEFT | FILL_RIGHT | IDENTIFIER | IGNORING | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | ON | QUANTILE | STDDEV | STDVAR | SUM | TOPK | START | END | ATAN2 | LIMITK | LIMIT_RATIO | STEP | RANGE | ANCHORED | SMOOTHED | MAX_OF | MIN_OF;
|
||||
|
||||
unary_op : ADD | SUB;
|
||||
|
||||
|
|
@ -1247,7 +1267,7 @@ offset_duration_expr : number_duration_literal
|
|||
yylex.(*parser).experimentalDurationExpr(de)
|
||||
$$ = de
|
||||
}
|
||||
| min_max LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
|
||||
| max_of_min_of LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
|
||||
{
|
||||
de := &DurationExpr{
|
||||
Op: $1.Typ,
|
||||
|
|
@ -1259,7 +1279,7 @@ offset_duration_expr : number_duration_literal
|
|||
yylex.(*parser).experimentalDurationExpr(de)
|
||||
$$ = de
|
||||
}
|
||||
| unary_op min_max LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
|
||||
| unary_op max_of_min_of LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
|
||||
{
|
||||
de := &DurationExpr{
|
||||
Op: $1.Typ,
|
||||
|
|
@ -1293,7 +1313,7 @@ offset_duration_expr : number_duration_literal
|
|||
| duration_expr
|
||||
;
|
||||
|
||||
min_max: MIN | MAX ;
|
||||
max_of_min_of: MAX_OF | MIN_OF ;
|
||||
|
||||
duration_expr : number_duration_literal
|
||||
{
|
||||
|
|
@ -1397,7 +1417,7 @@ duration_expr : number_duration_literal
|
|||
yylex.(*parser).experimentalDurationExpr(de)
|
||||
$$ = de
|
||||
}
|
||||
| min_max LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
|
||||
| max_of_min_of LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
|
||||
{
|
||||
de := &DurationExpr{
|
||||
Op: $1.Typ,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -143,10 +143,12 @@ var key = map[string]ItemType{
|
|||
"bool": BOOL,
|
||||
|
||||
// Preprocessors.
|
||||
"start": START,
|
||||
"end": END,
|
||||
"step": STEP,
|
||||
"range": RANGE,
|
||||
"start": START,
|
||||
"end": END,
|
||||
"step": STEP,
|
||||
"range": RANGE,
|
||||
"max_of": MAX_OF,
|
||||
"min_of": MIN_OF,
|
||||
}
|
||||
|
||||
var histogramDesc = map[string]ItemType{
|
||||
|
|
@ -937,10 +939,10 @@ func lexNumber(l *Lexer) stateFn {
|
|||
|
||||
// durationKeywordTokens maps lowercase duration keyword names to their token types.
|
||||
var durationKeywordTokens = map[string]ItemType{
|
||||
"step": STEP,
|
||||
"range": RANGE,
|
||||
"min": MIN,
|
||||
"max": MAX,
|
||||
"step": STEP,
|
||||
"range": RANGE,
|
||||
"max_of": MAX_OF,
|
||||
"min_of": MIN_OF,
|
||||
}
|
||||
|
||||
// durationKeywordStartChars is the set of lowercase runes that can start a duration keyword,
|
||||
|
|
|
|||
|
|
@ -4610,7 +4610,7 @@ var testExpr = []struct {
|
|||
},
|
||||
},
|
||||
{
|
||||
input: `foo[max(step(),5s)]`,
|
||||
input: `foo[max_of(step(),5s)]`,
|
||||
expected: &MatrixSelector{
|
||||
VectorSelector: &VectorSelector{
|
||||
Name: "foo",
|
||||
|
|
@ -4620,83 +4620,83 @@ var testExpr = []struct {
|
|||
PosRange: posrange.PositionRange{Start: 0, End: 3},
|
||||
},
|
||||
RangeExpr: &DurationExpr{
|
||||
Op: MAX,
|
||||
Op: MAX_OF,
|
||||
LHS: &DurationExpr{
|
||||
Op: STEP,
|
||||
StartPos: 8,
|
||||
EndPos: 14,
|
||||
StartPos: 11,
|
||||
EndPos: 17,
|
||||
},
|
||||
RHS: &NumberLiteral{
|
||||
Val: 5,
|
||||
Duration: true,
|
||||
PosRange: posrange.PositionRange{Start: 15, End: 17},
|
||||
PosRange: posrange.PositionRange{Start: 18, End: 20},
|
||||
},
|
||||
StartPos: 4,
|
||||
EndPos: 18,
|
||||
EndPos: 21,
|
||||
},
|
||||
EndPos: 19,
|
||||
EndPos: 22,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `foo offset max(step(),5s)`,
|
||||
input: `foo offset max_of(step(),5s)`,
|
||||
expected: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
||||
},
|
||||
PosRange: posrange.PositionRange{Start: 0, End: 25},
|
||||
PosRange: posrange.PositionRange{Start: 0, End: 28},
|
||||
OriginalOffsetExpr: &DurationExpr{
|
||||
Op: MAX,
|
||||
Op: MAX_OF,
|
||||
LHS: &DurationExpr{
|
||||
Op: STEP,
|
||||
StartPos: 15,
|
||||
EndPos: 21,
|
||||
StartPos: 18,
|
||||
EndPos: 24,
|
||||
},
|
||||
RHS: &NumberLiteral{
|
||||
Val: 5,
|
||||
Duration: true,
|
||||
PosRange: posrange.PositionRange{Start: 22, End: 24},
|
||||
PosRange: posrange.PositionRange{Start: 25, End: 27},
|
||||
},
|
||||
StartPos: 11,
|
||||
EndPos: 25,
|
||||
EndPos: 28,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `foo offset -min(5s,step()+8s)`,
|
||||
input: `foo offset -min_of(5s,step()+8s)`,
|
||||
expected: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
||||
},
|
||||
PosRange: posrange.PositionRange{Start: 0, End: 29},
|
||||
PosRange: posrange.PositionRange{Start: 0, End: 32},
|
||||
OriginalOffsetExpr: &DurationExpr{
|
||||
Op: SUB,
|
||||
RHS: &DurationExpr{
|
||||
Op: MIN,
|
||||
Op: MIN_OF,
|
||||
LHS: &NumberLiteral{
|
||||
Val: 5,
|
||||
Duration: true,
|
||||
PosRange: posrange.PositionRange{Start: 16, End: 18},
|
||||
PosRange: posrange.PositionRange{Start: 19, End: 21},
|
||||
},
|
||||
RHS: &DurationExpr{
|
||||
Op: ADD,
|
||||
LHS: &DurationExpr{
|
||||
Op: STEP,
|
||||
StartPos: 19,
|
||||
EndPos: 25,
|
||||
StartPos: 22,
|
||||
EndPos: 28,
|
||||
},
|
||||
RHS: &NumberLiteral{
|
||||
Val: 8,
|
||||
Duration: true,
|
||||
PosRange: posrange.PositionRange{Start: 26, End: 28},
|
||||
PosRange: posrange.PositionRange{Start: 29, End: 31},
|
||||
},
|
||||
},
|
||||
StartPos: 12,
|
||||
EndPos: 28,
|
||||
EndPos: 31,
|
||||
},
|
||||
StartPos: 11,
|
||||
EndPos: 28,
|
||||
EndPos: 31,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -4793,7 +4793,7 @@ var testExpr = []struct {
|
|||
},
|
||||
},
|
||||
{
|
||||
input: `foo[max(range(),5s)]`,
|
||||
input: `foo[max_of(range(),5s)]`,
|
||||
expected: &MatrixSelector{
|
||||
VectorSelector: &VectorSelector{
|
||||
Name: "foo",
|
||||
|
|
@ -4803,21 +4803,21 @@ var testExpr = []struct {
|
|||
PosRange: posrange.PositionRange{Start: 0, End: 3},
|
||||
},
|
||||
RangeExpr: &DurationExpr{
|
||||
Op: MAX,
|
||||
Op: MAX_OF,
|
||||
LHS: &DurationExpr{
|
||||
Op: RANGE,
|
||||
StartPos: 8,
|
||||
EndPos: 15,
|
||||
StartPos: 11,
|
||||
EndPos: 18,
|
||||
},
|
||||
RHS: &NumberLiteral{
|
||||
Val: 5,
|
||||
Duration: true,
|
||||
PosRange: posrange.PositionRange{Start: 16, End: 18},
|
||||
PosRange: posrange.PositionRange{Start: 19, End: 21},
|
||||
},
|
||||
StartPos: 4,
|
||||
EndPos: 19,
|
||||
EndPos: 22,
|
||||
},
|
||||
EndPos: 20,
|
||||
EndPos: 23,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -205,14 +205,14 @@ func (node *DurationExpr) writeTo(b *bytes.Buffer) {
|
|||
b.WriteString("step()")
|
||||
case node.Op == RANGE:
|
||||
b.WriteString("range()")
|
||||
case node.Op == MIN:
|
||||
b.WriteString("min(")
|
||||
case node.Op == MIN_OF:
|
||||
b.WriteString("min_of(")
|
||||
b.WriteString(node.LHS.String())
|
||||
b.WriteString(", ")
|
||||
b.WriteString(node.RHS.String())
|
||||
b.WriteByte(')')
|
||||
case node.Op == MAX:
|
||||
b.WriteString("max(")
|
||||
case node.Op == MAX_OF:
|
||||
b.WriteString("max_of(")
|
||||
b.WriteString(node.LHS.String())
|
||||
b.WriteString(", ")
|
||||
b.WriteString(node.RHS.String())
|
||||
|
|
|
|||
|
|
@ -270,22 +270,22 @@ func TestExprString(t *testing.T) {
|
|||
out: "foo offset (5 * 2)",
|
||||
},
|
||||
{
|
||||
in: "foo offset +min(10s, 20s)",
|
||||
out: "foo offset min(10s, 20s)",
|
||||
in: "foo offset +min_of(10s, 20s)",
|
||||
out: "foo offset min_of(10s, 20s)",
|
||||
},
|
||||
{
|
||||
in: "foo offset -min(10s, 20s)",
|
||||
in: "foo offset -min_of(10s, 20s)",
|
||||
},
|
||||
{
|
||||
in: "foo offset -min(10s, +max(step() ^ 2, 2))",
|
||||
out: "foo offset -min(10s, max(step() ^ 2, 2))",
|
||||
in: "foo offset -min_of(10s, +max_of(step() ^ 2, 2))",
|
||||
out: "foo offset -min_of(10s, max_of(step() ^ 2, 2))",
|
||||
},
|
||||
{
|
||||
in: "foo[200-min(-step()^+step(),1)]",
|
||||
out: "foo[200 - min(-step() ^ step(), 1)]",
|
||||
in: "foo[200-min_of(-step()^+step(),1)]",
|
||||
out: "foo[200 - min_of(-step() ^ step(), 1)]",
|
||||
},
|
||||
{
|
||||
in: "foo[200 - min(step() + 10s, -max(step() ^ 2, 3))]",
|
||||
in: "foo[200 - min_of(step() + 10s, -max_of(step() ^ 2, 3))]",
|
||||
},
|
||||
{
|
||||
in: "foo[range()]",
|
||||
|
|
@ -300,7 +300,7 @@ func TestExprString(t *testing.T) {
|
|||
in: "foo offset -range()",
|
||||
},
|
||||
{
|
||||
in: "foo[max(range(), 5s)]",
|
||||
in: "foo[max_of(range(), 5s)]",
|
||||
},
|
||||
{
|
||||
in: `predict_linear(foo[1h], 3000)`,
|
||||
|
|
|
|||
|
|
@ -167,13 +167,13 @@ eval range from 50s to 60s step 10s count_over_time(metric1_total[1+(STep()-5)*2
|
|||
eval range from 50s to 60s step 5s count_over_time(metric1_total[step()+1])
|
||||
{} 6 6 6
|
||||
|
||||
eval range from 50s to 60s step 5s count_over_time(metric1_total[min(step()+1,1h)])
|
||||
eval range from 50s to 60s step 5s count_over_time(metric1_total[min_of(step()+1,1h)])
|
||||
{} 6 6 6
|
||||
|
||||
eval range from 50s to 60s step 5s count_over_time(metric1_total[max(min(step()+1,1h),1ms)])
|
||||
eval range from 50s to 60s step 5s count_over_time(metric1_total[max_of(min_of(step()+1,1h),1ms)])
|
||||
{} 6 6 6
|
||||
|
||||
eval range from 50s to 60s step 5s count_over_time(metric1_total[((max(min((step()+1),((1h))),1ms)))])
|
||||
eval range from 50s to 60s step 5s count_over_time(metric1_total[((max_of(min_of((step()+1),((1h))),1ms)))])
|
||||
{} 6 6 6
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset STEP()
|
||||
|
|
@ -200,31 +200,31 @@ eval range from 50s to 60s step 5s metric1_total offset (STEP()/10)
|
|||
eval range from 50s to 60s step 5s metric1_total offset (step())
|
||||
metric1_total{} 45 50 55
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset min(step(), 1s)
|
||||
eval range from 50s to 60s step 5s metric1_total offset min_of(step(), 1s)
|
||||
metric1_total{} 49 54 59
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset min(step(), 1s)+8000
|
||||
eval range from 50s to 60s step 5s metric1_total offset min_of(step(), 1s)+8000
|
||||
{} 8049 8054 8059
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset -min(step(), 1s)+8000
|
||||
eval range from 50s to 60s step 5s metric1_total offset -min_of(step(), 1s)+8000
|
||||
{} 8051 8056 8061
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset -(min(step(), 1s))+8000
|
||||
eval range from 50s to 60s step 5s metric1_total offset -(min_of(step(), 1s))+8000
|
||||
{} 8051 8056 8061
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset -min(step(), 1s)^0
|
||||
eval range from 50s to 60s step 5s metric1_total offset -min_of(step(), 1s)^0
|
||||
{} 1 1 1
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset +min(step(), 1s)^0
|
||||
eval range from 50s to 60s step 5s metric1_total offset +min_of(step(), 1s)^0
|
||||
{} 1 1 1
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset min(step(), 1s)^0
|
||||
eval range from 50s to 60s step 5s metric1_total offset min_of(step(), 1s)^0
|
||||
{} 1 1 1
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset max(3s,min(step(), 1s))+8000
|
||||
eval range from 50s to 60s step 5s metric1_total offset max_of(3s,min_of(step(), 1s))+8000
|
||||
{} 8047 8052 8057
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset -(min(step(), 2s)-5)+8000
|
||||
eval range from 50s to 60s step 5s metric1_total offset -(min_of(step(), 2s)-5)+8000
|
||||
{} 8047 8052 8057
|
||||
|
||||
# Test range() function - resolves to query range (end - start).
|
||||
|
|
@ -238,7 +238,7 @@ eval range from 50s to 60s step 5s count_over_time(metric1_total[range()])
|
|||
eval range from 50s to 60s step 5s metric1_total offset range()
|
||||
metric1_total{} 40 45 50
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset min(range(), 8s)
|
||||
eval range from 50s to 60s step 5s metric1_total offset min_of(range(), 8s)
|
||||
metric1_total{} 42 47 52
|
||||
|
||||
clear
|
||||
|
|
|
|||
52
promql/promqltest/testdata/functions.test
vendored
52
promql/promqltest/testdata/functions.test
vendored
|
|
@ -2187,3 +2187,55 @@ eval range from 0s to 9s step 3s metric_for_at @ start()
|
|||
|
||||
eval range from 1s to 9s step 2s metric_for_at @ end()
|
||||
{__name__="metric_for_at"} 10 10 10 10 10
|
||||
|
||||
# Tests for min_of() and max_of() scalar functions.
|
||||
|
||||
# min_of(a, b) returns math.Min(a, b).
|
||||
# max_of(a, b) returns math.Max(a, b).
|
||||
|
||||
# Basic literal comparisons.
|
||||
eval instant at 0s min_of(3, 5)
|
||||
3
|
||||
|
||||
eval instant at 0s min_of(5, 3)
|
||||
3
|
||||
|
||||
eval instant at 0s max_of(3, 5)
|
||||
5
|
||||
|
||||
eval instant at 0s max_of(5, 3)
|
||||
5
|
||||
|
||||
# Equal values.
|
||||
eval instant at 0s min_of(4, 4)
|
||||
4
|
||||
|
||||
eval instant at 0s max_of(4, 4)
|
||||
4
|
||||
|
||||
# Negative values.
|
||||
eval instant at 0s min_of(-2, -5)
|
||||
-5
|
||||
|
||||
eval instant at 0s max_of(-2, -5)
|
||||
-2
|
||||
|
||||
# Zero and positive.
|
||||
eval instant at 0s min_of(0, 1)
|
||||
0
|
||||
|
||||
eval instant at 0s max_of(0, 1)
|
||||
1
|
||||
|
||||
# NaN propagation: any NaN input yields NaN.
|
||||
eval instant at 0s min_of(NaN, 3)
|
||||
NaN
|
||||
|
||||
eval instant at 0s min_of(3, NaN)
|
||||
NaN
|
||||
|
||||
eval instant at 0s max_of(NaN, 3)
|
||||
NaN
|
||||
|
||||
eval instant at 0s max_of(3, NaN)
|
||||
NaN
|
||||
|
|
|
|||
|
|
@ -75,11 +75,11 @@ func TestTranslateASTDurationExpressions(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "complex matrix selector range expression",
|
||||
query: `foo[max(step(),5m+3m) ]`,
|
||||
query: `foo[max_of(step(),5m+3m) ]`,
|
||||
wantType: "matrixSelector",
|
||||
wantFields: map[string]any{
|
||||
"range": int64(0),
|
||||
"rangeExpr": durationExpr("max",
|
||||
"rangeExpr": durationExpr("max_of",
|
||||
durationExpr("step", nil, nil, false),
|
||||
durationExpr("+", durationNumber("300", true), durationNumber("180", true), false),
|
||||
false,
|
||||
|
|
@ -87,13 +87,13 @@ func TestTranslateASTDurationExpressions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "nested min and max matrix selector range expression",
|
||||
query: `foo[min(max(step(),5m+3m),10m-2m)]`,
|
||||
name: "nested min_of and max_of matrix selector range expression",
|
||||
query: `foo[min_of(max_of(step(),5m+3m),10m-2m)]`,
|
||||
wantType: "matrixSelector",
|
||||
wantFields: map[string]any{
|
||||
"range": int64(0),
|
||||
"rangeExpr": durationExpr("min",
|
||||
durationExpr("max",
|
||||
"rangeExpr": durationExpr("min_of",
|
||||
durationExpr("max_of",
|
||||
durationExpr("step", nil, nil, false),
|
||||
durationExpr("+", durationNumber("300", true), durationNumber("180", true), false),
|
||||
false,
|
||||
|
|
@ -149,12 +149,12 @@ func TestTranslateASTDurationExpressions(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "vector selector offset expression",
|
||||
query: `foo offset -min(5s,step()+8s)`,
|
||||
query: `foo offset -min_of(5s,step()+8s)`,
|
||||
wantType: "vectorSelector",
|
||||
wantFields: map[string]any{
|
||||
"offset": int64(0),
|
||||
"offsetExpr": durationExpr("-", nil,
|
||||
durationExpr("min",
|
||||
durationExpr("min_of",
|
||||
durationNumber("5", true),
|
||||
durationExpr("+", durationExpr("step", nil, nil, false), durationNumber("8", true), false),
|
||||
false,
|
||||
|
|
|
|||
|
|
@ -2228,6 +2228,22 @@ const funcDocs: Record<string, React.ReactNode> = {
|
|||
</p>
|
||||
</>
|
||||
),
|
||||
max_of: (
|
||||
<>
|
||||
<p>
|
||||
<strong>
|
||||
This function has to be enabled via the{" "}
|
||||
<a href="../feature_flags.md#experimental-promql-functions">feature flag</a>
|
||||
<code>--enable-feature=promql-experimental-functions</code>.
|
||||
</strong>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<code>max_of(a scalar, b scalar)</code> returns the larger of the two scalar values <code>a</code>
|
||||
and <code>b</code>.
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
max_over_time: (
|
||||
<>
|
||||
<p>
|
||||
|
|
@ -2338,6 +2354,22 @@ const funcDocs: Record<string, React.ReactNode> = {
|
|||
</p>
|
||||
</>
|
||||
),
|
||||
min_of: (
|
||||
<>
|
||||
<p>
|
||||
<strong>
|
||||
This function has to be enabled via the{" "}
|
||||
<a href="../feature_flags.md#experimental-promql-functions">feature flag</a>
|
||||
<code>--enable-feature=promql-experimental-functions</code>.
|
||||
</strong>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<code>min_of(a scalar, b scalar)</code> returns the smaller of the two scalar values <code>a</code>
|
||||
and <code>b</code>.
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
min_over_time: (
|
||||
<>
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -111,7 +111,9 @@ export const functionSignatures: Record<string, Func> = {
|
|||
log10: { name: "log10", argTypes: [valueType.vector], variadic: 0, returnType: valueType.vector },
|
||||
log2: { name: "log2", argTypes: [valueType.vector], variadic: 0, returnType: valueType.vector },
|
||||
mad_over_time: { name: "mad_over_time", argTypes: [valueType.matrix], variadic: 0, returnType: valueType.vector },
|
||||
max_of: { name: "max_of", argTypes: [valueType.scalar, valueType.scalar], variadic: 0, returnType: valueType.scalar },
|
||||
max_over_time: { name: "max_over_time", argTypes: [valueType.matrix], variadic: 0, returnType: valueType.vector },
|
||||
min_of: { name: "min_of", argTypes: [valueType.scalar, valueType.scalar], variadic: 0, returnType: valueType.scalar },
|
||||
min_over_time: { name: "min_over_time", argTypes: [valueType.matrix], variadic: 0, returnType: valueType.vector },
|
||||
minute: { name: "minute", argTypes: [valueType.vector], variadic: 1, returnType: valueType.vector },
|
||||
month: { name: "month", argTypes: [valueType.vector], variadic: 1, returnType: valueType.vector },
|
||||
|
|
|
|||
Loading…
Reference in a new issue