diff --git a/promql/promql_test.go b/promql/promql_test.go index 92ebeee9ba..0a7f452716 100644 --- a/promql/promql_test.go +++ b/promql/promql_test.go @@ -111,1044 +111,6 @@ func newTestStorage(t testing.TB) (storage local.Storage, closer test.Closer) { return storage, closer } -func TestExpressions(t *testing.T) { - // Labels in expected output need to be alphabetically sorted. - expressionTests := []struct { - expr string - output []string - shouldFail bool - checkOrder bool - }{ - { - expr: `SUM(http_requests)`, - output: []string{`{} => 3600 @[%v]`}, - }, { - expr: `SUM(http_requests{instance="0"}) BY(job)`, - output: []string{ - `{job="api-server"} => 400 @[%v]`, - `{job="app-server"} => 1200 @[%v]`, - }, - }, { - expr: `SUM(http_requests{instance="0"}) BY(job) KEEPING_EXTRA`, - output: []string{ - `{instance="0", job="api-server"} => 400 @[%v]`, - `{instance="0", job="app-server"} => 1200 @[%v]`, - }, - }, { - expr: `SUM(http_requests) BY (job)`, - output: []string{ - `{job="api-server"} => 1000 @[%v]`, - `{job="app-server"} => 2600 @[%v]`, - }, - }, { - // Non-existent labels mentioned in BY-clauses shouldn't propagate to output. - expr: `SUM(http_requests) BY (job, nonexistent)`, - output: []string{ - `{job="api-server"} => 1000 @[%v]`, - `{job="app-server"} => 2600 @[%v]`, - }, - }, { - expr: ` - # Test comment. - SUM(http_requests) BY # comments shouldn't have any effect - (job) # another comment`, - output: []string{ - `{job="api-server"} => 1000 @[%v]`, - `{job="app-server"} => 2600 @[%v]`, - }, - }, { - expr: `COUNT(http_requests) BY (job)`, - output: []string{ - `{job="api-server"} => 4 @[%v]`, - `{job="app-server"} => 4 @[%v]`, - }, - }, { - expr: `SUM(http_requests) BY (job, group)`, - output: []string{ - `{group="canary", job="api-server"} => 700 @[%v]`, - `{group="canary", job="app-server"} => 1500 @[%v]`, - `{group="production", job="api-server"} => 300 @[%v]`, - `{group="production", job="app-server"} => 1100 @[%v]`, - }, - }, { - expr: `AVG(http_requests) BY (job)`, - output: []string{ - `{job="api-server"} => 250 @[%v]`, - `{job="app-server"} => 650 @[%v]`, - }, - }, { - expr: `MIN(http_requests) BY (job)`, - output: []string{ - `{job="api-server"} => 100 @[%v]`, - `{job="app-server"} => 500 @[%v]`, - }, - }, { - expr: `MAX(http_requests) BY (job)`, - output: []string{ - `{job="api-server"} => 400 @[%v]`, - `{job="app-server"} => 800 @[%v]`, - }, - }, { - expr: `SUM(http_requests) BY (job) - COUNT(http_requests) BY (job)`, - output: []string{ - `{job="api-server"} => 996 @[%v]`, - `{job="app-server"} => 2596 @[%v]`, - }, - }, { - expr: `2 - SUM(http_requests) BY (job)`, - output: []string{ - `{job="api-server"} => -998 @[%v]`, - `{job="app-server"} => -2598 @[%v]`, - }, - }, { - expr: `1000 / SUM(http_requests) BY (job)`, - output: []string{ - `{job="api-server"} => 1 @[%v]`, - `{job="app-server"} => 0.38461538461538464 @[%v]`, - }, - }, { - expr: `SUM(http_requests) BY (job) - 2`, - output: []string{ - `{job="api-server"} => 998 @[%v]`, - `{job="app-server"} => 2598 @[%v]`, - }, - }, { - expr: `SUM(http_requests) BY (job) % 3`, - output: []string{ - `{job="api-server"} => 1 @[%v]`, - `{job="app-server"} => 2 @[%v]`, - }, - }, { - expr: `SUM(http_requests) BY (job) / 0`, - output: []string{ - `{job="api-server"} => +Inf @[%v]`, - `{job="app-server"} => +Inf @[%v]`, - }, - }, { - expr: `SUM(http_requests) BY (job) > 1000`, - output: []string{ - `{job="app-server"} => 2600 @[%v]`, - }, - }, { - expr: `1000 < SUM(http_requests) BY (job)`, - output: []string{ - `{job="app-server"} => 1000 @[%v]`, - }, - }, { - expr: `SUM(http_requests) BY (job) <= 1000`, - output: []string{ - `{job="api-server"} => 1000 @[%v]`, - }, - }, { - expr: `SUM(http_requests) BY (job) != 1000`, - output: []string{ - `{job="app-server"} => 2600 @[%v]`, - }, - }, { - expr: `SUM(http_requests) BY (job) == 1000`, - output: []string{ - `{job="api-server"} => 1000 @[%v]`, - }, - }, { - expr: `SUM(http_requests) BY (job) + SUM(http_requests) BY (job)`, - output: []string{ - `{job="api-server"} => 2000 @[%v]`, - `{job="app-server"} => 5200 @[%v]`, - }, - }, { - expr: `http_requests{job="api-server", group="canary"}`, - output: []string{ - `http_requests{group="canary", instance="0", job="api-server"} => 300 @[%v]`, - `http_requests{group="canary", instance="1", job="api-server"} => 400 @[%v]`, - }, - }, { - expr: `http_requests{job="api-server", group="canary"} + rate(http_requests{job="api-server"}[5m]) * 5 * 60`, - output: []string{ - `{group="canary", instance="0", job="api-server"} => 330 @[%v]`, - `{group="canary", instance="1", job="api-server"} => 440 @[%v]`, - }, - }, { - expr: `rate(http_requests[25m]) * 25 * 60`, - output: []string{ - `{group="canary", instance="0", job="api-server"} => 150 @[%v]`, - `{group="canary", instance="0", job="app-server"} => 350 @[%v]`, - `{group="canary", instance="1", job="api-server"} => 200 @[%v]`, - `{group="canary", instance="1", job="app-server"} => 400 @[%v]`, - `{group="production", instance="0", job="api-server"} => 50 @[%v]`, - `{group="production", instance="0", job="app-server"} => 249.99999999999997 @[%v]`, - `{group="production", instance="1", job="api-server"} => 100 @[%v]`, - `{group="production", instance="1", job="app-server"} => 300 @[%v]`, - }, - }, { - expr: `delta(http_requests[25m], 1)`, - output: []string{ - `{group="canary", instance="0", job="api-server"} => 150 @[%v]`, - `{group="canary", instance="0", job="app-server"} => 350 @[%v]`, - `{group="canary", instance="1", job="api-server"} => 200 @[%v]`, - `{group="canary", instance="1", job="app-server"} => 400 @[%v]`, - `{group="production", instance="0", job="api-server"} => 50 @[%v]`, - `{group="production", instance="0", job="app-server"} => 250 @[%v]`, - `{group="production", instance="1", job="api-server"} => 100 @[%v]`, - `{group="production", instance="1", job="app-server"} => 300 @[%v]`, - }, - }, - { - expr: `sort(http_requests)`, - output: []string{ - `http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`, - `http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`, - `http_requests{group="canary", instance="0", job="api-server"} => 300 @[%v]`, - `http_requests{group="canary", instance="1", job="api-server"} => 400 @[%v]`, - `http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`, - `http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`, - `http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`, - `http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`, - }, - checkOrder: true, - }, { - expr: `sort(0 / round(http_requests, 400) + http_requests)`, - output: []string{ - `{group="production", instance="0", job="api-server"} => NaN @[%v]`, - `{group="production", instance="1", job="api-server"} => 200 @[%v]`, - `{group="canary", instance="0", job="api-server"} => 300 @[%v]`, - `{group="canary", instance="1", job="api-server"} => 400 @[%v]`, - `{group="production", instance="0", job="app-server"} => 500 @[%v]`, - `{group="production", instance="1", job="app-server"} => 600 @[%v]`, - `{group="canary", instance="0", job="app-server"} => 700 @[%v]`, - `{group="canary", instance="1", job="app-server"} => 800 @[%v]`, - }, - checkOrder: true, - }, { - expr: `sort_desc(http_requests)`, - output: []string{ - `http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`, - `http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`, - `http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`, - `http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`, - `http_requests{group="canary", instance="1", job="api-server"} => 400 @[%v]`, - `http_requests{group="canary", instance="0", job="api-server"} => 300 @[%v]`, - `http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`, - `http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`, - }, - checkOrder: true, - }, - { - expr: `topk(3, http_requests)`, - output: []string{ - `http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`, - `http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`, - `http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`, - }, - checkOrder: true, - }, { - expr: `topk(5, http_requests{group="canary",job="app-server"})`, - output: []string{ - `http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`, - `http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`, - }, - checkOrder: true, - }, { - expr: `bottomk(3, http_requests)`, - output: []string{ - `http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`, - `http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`, - `http_requests{group="canary", instance="0", job="api-server"} => 300 @[%v]`, - }, - checkOrder: true, - }, { - expr: `bottomk(5, http_requests{group="canary",job="app-server"})`, - output: []string{ - `http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`, - `http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`, - }, - checkOrder: true, - }, - { - // Single-letter label names and values. - expr: `x{y="testvalue"}`, - output: []string{ - `x{y="testvalue"} => 100 @[%v]`, - }, - }, { - // Lower-cased aggregation operators should work too. - expr: `sum(http_requests) by (job) + min(http_requests) by (job) + max(http_requests) by (job) + avg(http_requests) by (job)`, - output: []string{ - `{job="app-server"} => 4550 @[%v]`, - `{job="api-server"} => 1750 @[%v]`, - }, - }, { - // Deltas should be adjusted for target interval vs. samples under target interval. - expr: `delta(http_requests{group="canary", instance="1", job="app-server"}[18m])`, - output: []string{`{group="canary", instance="1", job="app-server"} => 288 @[%v]`}, - }, { - // Deltas should perform the same operation when 2nd argument is 0. - expr: `delta(http_requests{group="canary", instance="1", job="app-server"}[18m], 0)`, - output: []string{`{group="canary", instance="1", job="app-server"} => 288 @[%v]`}, - }, { - // Rates should calculate per-second rates. - expr: `rate(http_requests{group="canary", instance="1", job="app-server"}[60m])`, - output: []string{`{group="canary", instance="1", job="app-server"} => 0.26666666666666666 @[%v]`}, - }, - { - // Deriv should return the same as rate in simple cases. - expr: `deriv(http_requests{group="canary", instance="1", job="app-server"}[60m])`, - output: []string{`{group="canary", instance="1", job="app-server"} => 0.26666666666666666 @[%v]`}, - }, - { - // Counter resets at in the middle of range are handled correctly by rate(). - expr: `rate(testcounter_reset_middle[60m])`, - output: []string{`{} => 0.03 @[%v]`}, - }, { - // Counter resets at end of range are ignored by rate(). - expr: `rate(testcounter_reset_end[5m])`, - output: []string{`{} => 0 @[%v]`}, - }, - { - // Deriv should return correct result. - expr: `deriv(testcounter_reset_middle[100m])`, - output: []string{`{} => 0.010606060606060607 @[%v]`}, - }, - { - // count_scalar for a non-empty vector should return scalar element count. - expr: `count_scalar(http_requests)`, - output: []string{`scalar: 8 @[%v]`}, - }, { - // count_scalar for an empty vector should return scalar 0. - expr: `count_scalar(nonexistent)`, - output: []string{`scalar: 0 @[%v]`}, - }, { - // Empty expressions shouldn't parse. - expr: ``, - shouldFail: true, - }, { - // Interval durations can't be in quotes. - expr: `http_requests["1m"]`, - shouldFail: true, - }, { - // Binop arguments need to be scalar or vector. - expr: `http_requests - http_requests[1m]`, - shouldFail: true, - }, { - expr: `http_requests{group!="canary"}`, - output: []string{ - `http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`, - `http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`, - `http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`, - `http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`, - }, - }, { - expr: `http_requests{job=~"server",group!="canary"}`, - output: []string{ - `http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`, - `http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`, - `http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`, - `http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`, - }, - }, { - expr: `http_requests{job!~"api",group!="canary"}`, - output: []string{ - `http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`, - `http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`, - }, - }, { - expr: `count_scalar(http_requests{job=~"^server$"})`, - output: []string{`scalar: 0 @[%v]`}, - }, { - expr: `http_requests{group="production",job=~"^api"}`, - output: []string{ - `http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`, - `http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`, - }, - }, - { - expr: `abs(-1 * http_requests{group="production",job="api-server"})`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 100 @[%v]`, - `{group="production", instance="1", job="api-server"} => 200 @[%v]`, - }, - }, - { - expr: `floor(0.004 * http_requests{group="production",job="api-server"})`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 0 @[%v]`, - `{group="production", instance="1", job="api-server"} => 0 @[%v]`, - }, - }, - { - expr: `ceil(0.004 * http_requests{group="production",job="api-server"})`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 1 @[%v]`, - `{group="production", instance="1", job="api-server"} => 1 @[%v]`, - }, - }, - { - expr: `round(0.004 * http_requests{group="production",job="api-server"})`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 0 @[%v]`, - `{group="production", instance="1", job="api-server"} => 1 @[%v]`, - }, - }, - { // Round should correctly handle negative numbers. - expr: `round(-1 * (0.004 * http_requests{group="production",job="api-server"}))`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 0 @[%v]`, - `{group="production", instance="1", job="api-server"} => -1 @[%v]`, - }, - }, - { // Round should round half up. - expr: `round(0.005 * http_requests{group="production",job="api-server"})`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 1 @[%v]`, - `{group="production", instance="1", job="api-server"} => 1 @[%v]`, - }, - }, - { - expr: `round(-1 * (0.005 * http_requests{group="production",job="api-server"}))`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 0 @[%v]`, - `{group="production", instance="1", job="api-server"} => -1 @[%v]`, - }, - }, - { - expr: `round(1 + 0.005 * http_requests{group="production",job="api-server"})`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 2 @[%v]`, - `{group="production", instance="1", job="api-server"} => 2 @[%v]`, - }, - }, - { - expr: `round(-1 * (1 + 0.005 * http_requests{group="production",job="api-server"}))`, - output: []string{ - `{group="production", instance="0", job="api-server"} => -1 @[%v]`, - `{group="production", instance="1", job="api-server"} => -2 @[%v]`, - }, - }, - { // Round should accept the number to round nearest to. - expr: `round(0.0005 * http_requests{group="production",job="api-server"}, 0.1)`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 0.1 @[%v]`, - `{group="production", instance="1", job="api-server"} => 0.1 @[%v]`, - }, - }, - { - expr: `round(2.1 + 0.0005 * http_requests{group="production",job="api-server"}, 0.1)`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 2.2 @[%v]`, - `{group="production", instance="1", job="api-server"} => 2.2 @[%v]`, - }, - }, - { - expr: `round(5.2 + 0.0005 * http_requests{group="production",job="api-server"}, 0.1)`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 5.3 @[%v]`, - `{group="production", instance="1", job="api-server"} => 5.3 @[%v]`, - }, - }, - { // Round should work correctly with negative numbers and multiple decimal places. - expr: `round(-1 * (5.2 + 0.0005 * http_requests{group="production",job="api-server"}), 0.1)`, - output: []string{ - `{group="production", instance="0", job="api-server"} => -5.2 @[%v]`, - `{group="production", instance="1", job="api-server"} => -5.3 @[%v]`, - }, - }, - { // Round should work correctly with big toNearests. - expr: `round(0.025 * http_requests{group="production",job="api-server"}, 5)`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 5 @[%v]`, - `{group="production", instance="1", job="api-server"} => 5 @[%v]`, - }, - }, - { - expr: `round(0.045 * http_requests{group="production",job="api-server"}, 5)`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 5 @[%v]`, - `{group="production", instance="1", job="api-server"} => 10 @[%v]`, - }, - }, - { - expr: `avg_over_time(http_requests{group="production",job="api-server"}[1h])`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 50 @[%v]`, - `{group="production", instance="1", job="api-server"} => 100 @[%v]`, - }, - }, - { - expr: `count_over_time(http_requests{group="production",job="api-server"}[1h])`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 11 @[%v]`, - `{group="production", instance="1", job="api-server"} => 11 @[%v]`, - }, - }, - { - expr: `max_over_time(http_requests{group="production",job="api-server"}[1h])`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 100 @[%v]`, - `{group="production", instance="1", job="api-server"} => 200 @[%v]`, - }, - }, - { - expr: `min_over_time(http_requests{group="production",job="api-server"}[1h])`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 0 @[%v]`, - `{group="production", instance="1", job="api-server"} => 0 @[%v]`, - }, - }, - { - expr: `sum_over_time(http_requests{group="production",job="api-server"}[1h])`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 550 @[%v]`, - `{group="production", instance="1", job="api-server"} => 1100 @[%v]`, - }, - }, - { - expr: `time()`, - output: []string{`scalar: 3000 @[%v]`}, - }, - { - expr: `drop_common_labels(http_requests{group="production",job="api-server"})`, - output: []string{ - `http_requests{instance="0"} => 100 @[%v]`, - `http_requests{instance="1"} => 200 @[%v]`, - }, - }, - { - expr: `{` + string(clientmodel.MetricNameLabel) + `=~".*"}`, - output: []string{ - `http_requests{group="canary", instance="0", job="api-server"} => 300 @[%v]`, - `http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`, - `http_requests{group="canary", instance="1", job="api-server"} => 400 @[%v]`, - `http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`, - `http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`, - `http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`, - `http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`, - `http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`, - `testcounter_reset_end => 0 @[%v]`, - `testcounter_reset_middle => 50 @[%v]`, - `x{y="testvalue"} => 100 @[%v]`, - `label_grouping_test{a="a", b="abb"} => 200 @[%v]`, - `label_grouping_test{a="aa", b="bb"} => 100 @[%v]`, - `request_duration_seconds_bucket{instance="ins1", job="job1", le="0.1"} => 10 @[%v]`, - `request_duration_seconds_bucket{instance="ins1", job="job1", le="0.2"} => 30 @[%v]`, - `request_duration_seconds_bucket{instance="ins1", job="job1", le="+Inf"} => 40 @[%v]`, - `request_duration_seconds_bucket{instance="ins2", job="job1", le="0.1"} => 20 @[%v]`, - `request_duration_seconds_bucket{instance="ins2", job="job1", le="0.2"} => 50 @[%v]`, - `request_duration_seconds_bucket{instance="ins2", job="job1", le="+Inf"} => 60 @[%v]`, - `request_duration_seconds_bucket{instance="ins1", job="job2", le="0.1"} => 30 @[%v]`, - `request_duration_seconds_bucket{instance="ins1", job="job2", le="0.2"} => 40 @[%v]`, - `request_duration_seconds_bucket{instance="ins1", job="job2", le="+Inf"} => 60 @[%v]`, - `request_duration_seconds_bucket{instance="ins2", job="job2", le="0.1"} => 40 @[%v]`, - `request_duration_seconds_bucket{instance="ins2", job="job2", le="0.2"} => 70 @[%v]`, - `request_duration_seconds_bucket{instance="ins2", job="job2", le="+Inf"} => 90 @[%v]`, - `vector_matching_a{l="x"} => 10 @[%v]`, - `vector_matching_a{l="y"} => 20 @[%v]`, - `vector_matching_b{l="x"} => 40 @[%v]`, - `cpu_count{instance="1", type="smp"} => 200 @[%v]`, - `cpu_count{instance="0", type="smp"} => 100 @[%v]`, - `cpu_count{instance="0", type="numa"} => 300 @[%v]`, - }, - }, - { - expr: `{job=~"server", job!~"api"}`, - output: []string{ - `http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`, - `http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`, - `http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`, - `http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`, - }, - }, - { - // Test alternative "by"-clause order. - expr: `sum by (group) (http_requests{job="api-server"})`, - output: []string{ - `{group="canary"} => 700 @[%v]`, - `{group="production"} => 300 @[%v]`, - }, - }, - { - // Test alternative "by"-clause order with "keeping_extra". - expr: `sum by (group) keeping_extra (http_requests{job="api-server"})`, - output: []string{ - `{group="canary", job="api-server"} => 700 @[%v]`, - `{group="production", job="api-server"} => 300 @[%v]`, - }, - }, - { - // Test both alternative "by"-clause orders in one expression. - // Public health warning: stick to one form within an expression (or even - // in an organization), or risk serious user confusion. - expr: `sum(sum by (group) keeping_extra (http_requests{job="api-server"})) by (job)`, - output: []string{ - `{job="api-server"} => 1000 @[%v]`, - }, - }, - { - expr: `http_requests{group="canary"} and http_requests{instance="0"}`, - output: []string{ - `http_requests{group="canary", instance="0", job="api-server"} => 300 @[%v]`, - `http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`, - }, - }, - { - expr: `(http_requests{group="canary"} + 1) and http_requests{instance="0"}`, - output: []string{ - `{group="canary", instance="0", job="api-server"} => 301 @[%v]`, - `{group="canary", instance="0", job="app-server"} => 701 @[%v]`, - }, - }, - { - expr: `(http_requests{group="canary"} + 1) and on(instance, job) http_requests{instance="0", group="production"}`, - output: []string{ - `{group="canary", instance="0", job="api-server"} => 301 @[%v]`, - `{group="canary", instance="0", job="app-server"} => 701 @[%v]`, - }, - }, - { - expr: `(http_requests{group="canary"} + 1) and on(instance) http_requests{instance="0", group="production"}`, - output: []string{ - `{group="canary", instance="0", job="api-server"} => 301 @[%v]`, - `{group="canary", instance="0", job="app-server"} => 701 @[%v]`, - }, - }, - { - expr: `http_requests{group="canary"} or http_requests{group="production"}`, - output: []string{ - `http_requests{group="canary", instance="0", job="api-server"} => 300 @[%v]`, - `http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`, - `http_requests{group="canary", instance="1", job="api-server"} => 400 @[%v]`, - `http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`, - `http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`, - `http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`, - `http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`, - `http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`, - }, - }, - { - // On overlap the rhs samples must be dropped. - expr: `(http_requests{group="canary"} + 1) or http_requests{instance="1"}`, - output: []string{ - `{group="canary", instance="0", job="api-server"} => 301 @[%v]`, - `{group="canary", instance="0", job="app-server"} => 701 @[%v]`, - `{group="canary", instance="1", job="api-server"} => 401 @[%v]`, - `{group="canary", instance="1", job="app-server"} => 801 @[%v]`, - `http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`, - `http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`, - }, - }, - { - // Matching only on instance excludes everything that has instance=0/1 but includes - // entries without the instance label. - expr: `(http_requests{group="canary"} + 1) or on(instance) (http_requests or cpu_count or vector_matching_a)`, - output: []string{ - `{group="canary", instance="0", job="api-server"} => 301 @[%v]`, - `{group="canary", instance="0", job="app-server"} => 701 @[%v]`, - `{group="canary", instance="1", job="api-server"} => 401 @[%v]`, - `{group="canary", instance="1", job="app-server"} => 801 @[%v]`, - `vector_matching_a{l="x"} => 10 @[%v]`, - `vector_matching_a{l="y"} => 20 @[%v]`, - }, - }, - { - expr: `http_requests{group="canary"} / on(instance,job) http_requests{group="production"}`, - output: []string{ - `{instance="0", job="api-server"} => 3 @[%v]`, - `{instance="0", job="app-server"} => 1.4 @[%v]`, - `{instance="1", job="api-server"} => 2 @[%v]`, - `{instance="1", job="app-server"} => 1.3333333333333333 @[%v]`, - }, - }, - { - // Include labels must guarantee uniquely identifiable time series. - expr: `http_requests{group="production"} / on(instance) group_left(group) cpu_count{type="smp"}`, - shouldFail: true, - }, - { - // Many-to-many matching is not allowed. - expr: `http_requests{group="production"} / on(instance) group_left(job,type) cpu_count`, - shouldFail: true, - }, - { - // Many-to-one matching must be explicit. - expr: `http_requests{group="production"} / on(instance) cpu_count{type="smp"}`, - shouldFail: true, - }, - { - expr: `http_requests{group="production"} / on(instance) group_left(job) cpu_count{type="smp"}`, - output: []string{ - `{instance="1", job="api-server"} => 1 @[%v]`, - `{instance="0", job="app-server"} => 5 @[%v]`, - `{instance="1", job="app-server"} => 3 @[%v]`, - `{instance="0", job="api-server"} => 1 @[%v]`, - }, - }, - { - // Ensure sidedness of grouping preserves operand sides. - expr: `cpu_count{type="smp"} / on(instance) group_right(job) http_requests{group="production"}`, - output: []string{ - `{instance="1", job="app-server"} => 0.3333333333333333 @[%v]`, - `{instance="0", job="app-server"} => 0.2 @[%v]`, - `{instance="1", job="api-server"} => 1 @[%v]`, - `{instance="0", job="api-server"} => 1 @[%v]`, - }, - }, - { - // Include labels from both sides. - expr: `http_requests{group="production"} / on(instance) group_left(job) cpu_count{type="smp"}`, - output: []string{ - `{instance="1", job="api-server"} => 1 @[%v]`, - `{instance="0", job="app-server"} => 5 @[%v]`, - `{instance="1", job="app-server"} => 3 @[%v]`, - `{instance="0", job="api-server"} => 1 @[%v]`, - }, - }, - { - expr: `http_requests{group="production"} < on(instance,job) http_requests{group="canary"}`, - output: []string{ - `{instance="1", job="app-server"} => 600 @[%v]`, - `{instance="0", job="app-server"} => 500 @[%v]`, - `{instance="1", job="api-server"} => 200 @[%v]`, - `{instance="0", job="api-server"} => 100 @[%v]`, - }, - }, - { - expr: `http_requests{group="production"} > on(instance,job) http_requests{group="canary"}`, - output: []string{}, - }, - { - expr: `http_requests{group="production"} == on(instance,job) http_requests{group="canary"}`, - output: []string{}, - }, - { - expr: `http_requests > on(instance) group_left(group,job) cpu_count{type="smp"}`, - output: []string{ - `{group="canary", instance="0", job="app-server"} => 700 @[%v]`, - `{group="canary", instance="1", job="app-server"} => 800 @[%v]`, - `{group="canary", instance="0", job="api-server"} => 300 @[%v]`, - `{group="canary", instance="1", job="api-server"} => 400 @[%v]`, - `{group="production", instance="0", job="app-server"} => 500 @[%v]`, - `{group="production", instance="1", job="app-server"} => 600 @[%v]`, - }, - }, - { - expr: `http_requests / on(instance) 3`, - shouldFail: true, - }, - { - expr: `3 / on(instance) http_requests_total`, - shouldFail: true, - }, - { - expr: `3 / on(instance) 3`, - shouldFail: true, - }, - { - // Missing label list for grouping mod. - expr: `http_requests{group="production"} / on(instance) group_left cpu_count{type="smp"}`, - shouldFail: true, - }, - { - // No group mod allowed for logical operations. - expr: `http_requests{group="production"} or on(instance) group_left(type) cpu_count{type="smp"}`, - shouldFail: true, - }, - { - // No group mod allowed for logical operations. - expr: `http_requests{group="production"} and on(instance) group_left(type) cpu_count{type="smp"}`, - shouldFail: true, - }, - { - // No duplicate use of label. - expr: `http_requests{group="production"} + on(instance) group_left(job,instance) cpu_count{type="smp"}`, - shouldFail: true, - }, - { - expr: `{l="x"} + on(__name__) {l="y"}`, - output: []string{ - `vector_matching_a => 30 @[%v]`, - }, - }, - { - expr: `absent(nonexistent)`, - output: []string{ - `{} => 1 @[%v]`, - }, - }, - { - expr: `absent(nonexistent{job="testjob", instance="testinstance", method=~".*"})`, - output: []string{ - `{instance="testinstance", job="testjob"} => 1 @[%v]`, - }, - }, - { - expr: `count_scalar(absent(http_requests))`, - output: []string{ - `scalar: 0 @[%v]`, - }, - }, - { - expr: `count_scalar(absent(sum(http_requests)))`, - output: []string{ - `scalar: 0 @[%v]`, - }, - }, - { - expr: `absent(sum(nonexistent{job="testjob", instance="testinstance"}))`, - output: []string{ - `{} => 1 @[%v]`, - }, - }, - { - expr: `http_requests{group="production",job="api-server"} offset 5m`, - output: []string{ - `http_requests{group="production", instance="0", job="api-server"} => 90 @[%v]`, - `http_requests{group="production", instance="1", job="api-server"} => 180 @[%v]`, - }, - }, - { - expr: `rate(http_requests{group="production",job="api-server"}[10m] offset 5m)`, - output: []string{ - `{group="production", instance="0", job="api-server"} => 0.03333333333333333 @[%v]`, - `{group="production", instance="1", job="api-server"} => 0.06666666666666667 @[%v]`, - }, - }, - { - expr: `rate(http_requests[10m]) offset 5m`, - shouldFail: true, - }, - { - expr: `sum(http_requests) offset 5m`, - shouldFail: true, - }, - // Regression test for missing separator byte in labelsToGroupingKey. - { - expr: `sum(label_grouping_test) by (a, b)`, - output: []string{ - `{a="a", b="abb"} => 200 @[%v]`, - `{a="aa", b="bb"} => 100 @[%v]`, - }, - }, - { - expr: `http_requests{group="canary", instance="0", job="api-server"} / 0`, - output: []string{ - `{group="canary", instance="0", job="api-server"} => +Inf @[%v]`, - }, - }, - { - expr: `-1 * http_requests{group="canary", instance="0", job="api-server"} / 0`, - output: []string{ - `{group="canary", instance="0", job="api-server"} => -Inf @[%v]`, - }, - }, - { - expr: `0 * http_requests{group="canary", instance="0", job="api-server"} / 0`, - output: []string{ - `{group="canary", instance="0", job="api-server"} => NaN @[%v]`, - }, - }, - { - expr: `0 * http_requests{group="canary", instance="0", job="api-server"} % 0`, - output: []string{ - `{group="canary", instance="0", job="api-server"} => NaN @[%v]`, - }, - }, - { - expr: `exp(vector_matching_a)`, - output: []string{ - `{l="x"} => 22026.465794806718 @[%v]`, - `{l="y"} => 485165195.4097903 @[%v]`, - }, - }, - { - expr: `exp(vector_matching_a - 10)`, - output: []string{ - `{l="y"} => 22026.465794806718 @[%v]`, - `{l="x"} => 1 @[%v]`, - }, - }, - { - expr: `exp(vector_matching_a - 20)`, - output: []string{ - `{l="x"} => 4.5399929762484854e-05 @[%v]`, - `{l="y"} => 1 @[%v]`, - }, - }, - { - expr: `ln(vector_matching_a)`, - output: []string{ - `{l="x"} => 2.302585092994046 @[%v]`, - `{l="y"} => 2.995732273553991 @[%v]`, - }, - }, - { - expr: `ln(vector_matching_a - 10)`, - output: []string{ - `{l="y"} => 2.302585092994046 @[%v]`, - `{l="x"} => -Inf @[%v]`, - }, - }, - { - expr: `ln(vector_matching_a - 20)`, - output: []string{ - `{l="y"} => -Inf @[%v]`, - `{l="x"} => NaN @[%v]`, - }, - }, - { - expr: `exp(ln(vector_matching_a))`, - output: []string{ - `{l="y"} => 20 @[%v]`, - `{l="x"} => 10 @[%v]`, - }, - }, - { - expr: `sqrt(vector_matching_a)`, - output: []string{ - `{l="x"} => 3.1622776601683795 @[%v]`, - `{l="y"} => 4.47213595499958 @[%v]`, - }, - }, - { - expr: `log2(vector_matching_a)`, - output: []string{ - `{l="x"} => 3.3219280948873626 @[%v]`, - `{l="y"} => 4.321928094887363 @[%v]`, - }, - }, - { - expr: `log2(vector_matching_a - 10)`, - output: []string{ - `{l="y"} => 3.3219280948873626 @[%v]`, - `{l="x"} => -Inf @[%v]`, - }, - }, - { - expr: `log2(vector_matching_a - 20)`, - output: []string{ - `{l="x"} => NaN @[%v]`, - `{l="y"} => -Inf @[%v]`, - }, - }, - { - expr: `log10(vector_matching_a)`, - output: []string{ - `{l="x"} => 1 @[%v]`, - `{l="y"} => 1.301029995663981 @[%v]`, - }, - }, - { - expr: `log10(vector_matching_a - 10)`, - output: []string{ - `{l="y"} => 1 @[%v]`, - `{l="x"} => -Inf @[%v]`, - }, - }, - { - expr: `log10(vector_matching_a - 20)`, - output: []string{ - `{l="x"} => NaN @[%v]`, - `{l="y"} => -Inf @[%v]`, - }, - }, - { - expr: `stddev(http_requests)`, - output: []string{ - `{} => 229.12878474779 @[%v]`, - }, - }, - { - expr: `stddev by (instance)(http_requests)`, - output: []string{ - `{instance="0"} => 223.60679774998 @[%v]`, - `{instance="1"} => 223.60679774998 @[%v]`, - }, - }, - { - expr: `stdvar(http_requests)`, - output: []string{ - `{} => 52500 @[%v]`, - }, - }, - { - expr: `stdvar by (instance)(http_requests)`, - output: []string{ - `{instance="0"} => 50000 @[%v]`, - `{instance="1"} => 50000 @[%v]`, - }, - }, - } - - storage, closer := newTestStorage(t) - defer closer.Close() - - engine := NewEngine(storage) - - for i, exprTest := range expressionTests { - expectedLines := annotateWithTime(exprTest.output, testEvalTime) - - query, err := engine.NewInstantQuery(exprTest.expr, testEvalTime) - - if err != nil { - if !exprTest.shouldFail { - t.Errorf("%d. Error during parsing: %v", i, err) - t.Errorf("%d. Expression: %v", i, exprTest.expr) - } - continue - } - - failed := false - - res := query.Exec() - if res.Err != nil { - if !exprTest.shouldFail { - t.Errorf("%d. Error evaluating query: %s", res.Err) - t.Errorf("%d. Expression: %v", i, exprTest.expr) - } - continue - } - if exprTest.shouldFail { - t.Errorf("%d. Expression should fail but did not", i) - continue - } - resultLines := strings.Split(res.String(), "\n") - // resultStr := ast.EvalToString(testExpr, testEvalTime, ast.Text, storage, stats.NewTimerGroup()) - // resultLines := strings.Split(resultStr, "\n") - - if len(exprTest.output) == 0 && strings.Trim(res.String(), "\n") == "" { - // expected and received empty vector, everything is fine - continue - } else if len(exprTest.output) != len(resultLines) { - t.Errorf("%d. Number of samples in expected and actual output don't match", i) - failed = true - } - - if exprTest.checkOrder { - for j, expectedSample := range expectedLines { - if resultLines[j] != expectedSample { - t.Errorf("%d.%d. Expected sample '%v', got '%v'", i, j, resultLines[j], expectedSample) - failed = true - } - } - } else { - for j, expectedSample := range expectedLines { - found := false - for _, actualSample := range resultLines { - if samplesAlmostEqual(actualSample, expectedSample) { - found = true - } - } - if !found { - t.Errorf("%d.%d. Couldn't find expected sample in output: '%v'", i, j, expectedSample) - failed = true - } - } - } - - if failed { - t.Errorf("%d. Expression: %v\n%v", i, exprTest.expr, vectorComparisonString(expectedLines, resultLines)) - } - - } -} - func TestRangedEvaluationRegressions(t *testing.T) { scenarios := []struct { in Matrix diff --git a/promql/testdata/legacy.test b/promql/testdata/legacy.test new file mode 100644 index 0000000000..d444090317 --- /dev/null +++ b/promql/testdata/legacy.test @@ -0,0 +1,638 @@ +load 5m + http_requests{job="api-server", instance="0", group="production"} 0+10x10 + http_requests{job="api-server", instance="1", group="production"} 0+20x10 + http_requests{job="api-server", instance="0", group="canary"} 0+30x10 + http_requests{job="api-server", instance="1", group="canary"} 0+40x10 + http_requests{job="app-server", instance="0", group="production"} 0+50x10 + http_requests{job="app-server", instance="1", group="production"} 0+60x10 + http_requests{job="app-server", instance="0", group="canary"} 0+70x10 + http_requests{job="app-server", instance="1", group="canary"} 0+80x10 + +load 5m + x{y="testvalue"} 0+10x10 + +load 5m + testcounter_reset_middle 0+10x4 0+10x5 + testcounter_reset_end 0+10x9 0 10 + +load 5m + label_grouping_test{a="aa", b="bb"} 0+10x10 + label_grouping_test{a="a", b="abb"} 0+20x10 + +load 5m + vector_matching_a{l="x"} 0+1x100 + vector_matching_a{l="y"} 0+2x50 + vector_matching_b{l="x"} 0+4x25 + +load 5m + cpu_count{instance="0", type="numa"} 0+30x10 + cpu_count{instance="0", type="smp"} 0+10x20 + cpu_count{instance="1", type="smp"} 0+20x10 + + +eval instant at 50m SUM(http_requests) + {} 3600 + +eval instant at 50m SUM(http_requests{instance="0"}) BY(job) + {job="api-server"} 400 + {job="app-server"} 1200 + +eval instant at 50m SUM(http_requests{instance="0"}) BY(job) KEEPING_EXTRA + {instance="0", job="api-server"} 400 + {instance="0", job="app-server"} 1200 + +eval instant at 50m SUM(http_requests) BY (job) + {job="api-server"} 1000 + {job="app-server"} 2600 + +# Non-existent labels mentioned in BY-clauses shouldn't propagate to output. +eval instant at 50m SUM(http_requests) BY (job, nonexistent) + {job="api-server"} 1000 + {job="app-server"} 2600 + + +eval instant at 50m COUNT(http_requests) BY (job) + {job="api-server"} 4 + {job="app-server"} 4 + + +eval instant at 50m SUM(http_requests) BY (job, group) + {group="canary", job="api-server"} 700 + {group="canary", job="app-server"} 1500 + {group="production", job="api-server"} 300 + {group="production", job="app-server"} 1100 + + +eval instant at 50m AVG(http_requests) BY (job) + {job="api-server"} 250 + {job="app-server"} 650 + + +eval instant at 50m MIN(http_requests) BY (job) + {job="api-server"} 100 + {job="app-server"} 500 + + +eval instant at 50m MAX(http_requests) BY (job) + {job="api-server"} 400 + {job="app-server"} 800 + + +eval instant at 50m SUM(http_requests) BY (job) - COUNT(http_requests) BY (job) + {job="api-server"} 996 + {job="app-server"} 2596 + + +eval instant at 50m 2 - SUM(http_requests) BY (job) + {job="api-server"} -998 + {job="app-server"} -2598 + + +eval instant at 50m 1000 / SUM(http_requests) BY (job) + {job="api-server"} 1 + {job="app-server"} 0.38461538461538464 + + +eval instant at 50m SUM(http_requests) BY (job) - 2 + {job="api-server"} 998 + {job="app-server"} 2598 + + +eval instant at 50m SUM(http_requests) BY (job) % 3 + {job="api-server"} 1 + {job="app-server"} 2 + + +eval instant at 50m SUM(http_requests) BY (job) / 0 + {job="api-server"} +Inf + {job="app-server"} +Inf + + +eval instant at 50m SUM(http_requests) BY (job) > 1000 + {job="app-server"} 2600 + + +eval instant at 50m 1000 < SUM(http_requests) BY (job) + {job="app-server"} 1000 + + +eval instant at 50m SUM(http_requests) BY (job) <= 1000 + {job="api-server"} 1000 + + +eval instant at 50m SUM(http_requests) BY (job) != 1000 + {job="app-server"} 2600 + + +eval instant at 50m SUM(http_requests) BY (job) == 1000 + {job="api-server"} 1000 + + +eval instant at 50m SUM(http_requests) BY (job) + SUM(http_requests) BY (job) + {job="api-server"} 2000 + {job="app-server"} 5200 + + +eval instant at 50m http_requests{job="api-server", group="canary"} + http_requests{group="canary", instance="0", job="api-server"} 300 + http_requests{group="canary", instance="1", job="api-server"} 400 + + +eval instant at 50m http_requests{job="api-server", group="canary"} + rate(http_requests{job="api-server"}[5m]) * 5 * 60 + {group="canary", instance="0", job="api-server"} 330 + {group="canary", instance="1", job="api-server"} 440 + + +eval instant at 50m rate(http_requests[25m]) * 25 * 60 + {group="canary", instance="0", job="api-server"} 150 + {group="canary", instance="0", job="app-server"} 350 + {group="canary", instance="1", job="api-server"} 200 + {group="canary", instance="1", job="app-server"} 400 + {group="production", instance="0", job="api-server"} 50 + {group="production", instance="0", job="app-server"} 249.99999999999997 + {group="production", instance="1", job="api-server"} 100 + {group="production", instance="1", job="app-server"} 300 + +eval instant at 50m delta(http_requests[25m], 1) + {group="canary", instance="0", job="api-server"} 150 + {group="canary", instance="0", job="app-server"} 350 + {group="canary", instance="1", job="api-server"} 200 + {group="canary", instance="1", job="app-server"} 400 + {group="production", instance="0", job="api-server"} 50 + {group="production", instance="0", job="app-server"} 250 + {group="production", instance="1", job="api-server"} 100 + {group="production", instance="1", job="app-server"} 300 + +eval_ordered instant at 50m sort(http_requests) + http_requests{group="production", instance="0", job="api-server"} 100 + http_requests{group="production", instance="1", job="api-server"} 200 + http_requests{group="canary", instance="0", job="api-server"} 300 + http_requests{group="canary", instance="1", job="api-server"} 400 + http_requests{group="production", instance="0", job="app-server"} 500 + http_requests{group="production", instance="1", job="app-server"} 600 + http_requests{group="canary", instance="0", job="app-server"} 700 + http_requests{group="canary", instance="1", job="app-server"} 800 + +eval_ordered instant at 50m sort(0 / round(http_requests, 400) + http_requests) + {group="production", instance="0", job="api-server"} NaN + {group="production", instance="1", job="api-server"} 200 + {group="canary", instance="0", job="api-server"} 300 + {group="canary", instance="1", job="api-server"} 400 + {group="production", instance="0", job="app-server"} 500 + {group="production", instance="1", job="app-server"} 600 + {group="canary", instance="0", job="app-server"} 700 + {group="canary", instance="1", job="app-server"} 800 + + +eval_ordered instant at 50m sort_desc(http_requests) + http_requests{group="canary", instance="1", job="app-server"} 800 + http_requests{group="canary", instance="0", job="app-server"} 700 + http_requests{group="production", instance="1", job="app-server"} 600 + http_requests{group="production", instance="0", job="app-server"} 500 + http_requests{group="canary", instance="1", job="api-server"} 400 + http_requests{group="canary", instance="0", job="api-server"} 300 + http_requests{group="production", instance="1", job="api-server"} 200 + http_requests{group="production", instance="0", job="api-server"} 100 + +eval_ordered instant at 50m topk(3, http_requests) + http_requests{group="canary", instance="1", job="app-server"} 800 + http_requests{group="canary", instance="0", job="app-server"} 700 + http_requests{group="production", instance="1", job="app-server"} 600 + +eval_ordered instant at 50m topk(5, http_requests{group="canary",job="app-server"}) + http_requests{group="canary", instance="1", job="app-server"} 800 + http_requests{group="canary", instance="0", job="app-server"} 700 + +eval_ordered instant at 50m bottomk(3, http_requests) + http_requests{group="production", instance="0", job="api-server"} 100 + http_requests{group="production", instance="1", job="api-server"} 200 + http_requests{group="canary", instance="0", job="api-server"} 300 + +eval_ordered instant at 50m bottomk(5, http_requests{group="canary",job="app-server"}) + http_requests{group="canary", instance="0", job="app-server"} 700 + http_requests{group="canary", instance="1", job="app-server"} 800 + + +# Single-letter label names and values. +eval instant at 50m x{y="testvalue"} + x{y="testvalue"} 100 + + +# Lower-cased aggregation operators should work too. +eval instant at 50m sum(http_requests) by (job) + min(http_requests) by (job) + max(http_requests) by (job) + avg(http_requests) by (job) + {job="app-server"} 4550 + {job="api-server"} 1750 + + +# Deltas should be adjusted for target interval vs. samples under target interval. +eval instant at 50m delta(http_requests{group="canary", instance="1", job="app-server"}[18m]) + {group="canary", instance="1", job="app-server"} 288 + + +# Deltas should perform the same operation when 2nd argument is 0. +eval instant at 50m delta(http_requests{group="canary", instance="1", job="app-server"}[18m], 0) + {group="canary", instance="1", job="app-server"} 288 + + +# Rates should calculate per-second rates. +eval instant at 50m rate(http_requests{group="canary", instance="1", job="app-server"}[60m]) + {group="canary", instance="1", job="app-server"} 0.26666666666666666 + +# Deriv should return the same as rate in simple cases. +eval instant at 50m deriv(http_requests{group="canary", instance="1", job="app-server"}[60m]) + {group="canary", instance="1", job="app-server"} 0.26666666666666666 + +# Counter resets at in the middle of range are handled correctly by rate(). +eval instant at 50m rate(testcounter_reset_middle[60m]) + {} 0.03 + + +# Counter resets at end of range are ignored by rate(). +eval instant at 50m rate(testcounter_reset_end[5m]) + {} 0 + +# Deriv should return correct result. +eval instant at 50m deriv(testcounter_reset_middle[100m]) + {} 0.010606060606060607 + +# count_scalar for a non-empty vector should return scalar element count. +eval instant at 50m count_scalar(http_requests) + 8 + +# count_scalar for an empty vector should return scalar 0. +eval instant at 50m count_scalar(nonexistent) + 0 + +eval instant at 50m http_requests{group!="canary"} + http_requests{group="production", instance="1", job="app-server"} 600 + http_requests{group="production", instance="0", job="app-server"} 500 + http_requests{group="production", instance="1", job="api-server"} 200 + http_requests{group="production", instance="0", job="api-server"} 100 + +eval instant at 50m http_requests{job=~"server",group!="canary"} + http_requests{group="production", instance="1", job="app-server"} 600 + http_requests{group="production", instance="0", job="app-server"} 500 + http_requests{group="production", instance="1", job="api-server"} 200 + http_requests{group="production", instance="0", job="api-server"} 100 + +eval instant at 50m http_requests{job!~"api",group!="canary"} + http_requests{group="production", instance="1", job="app-server"} 600 + http_requests{group="production", instance="0", job="app-server"} 500 + +eval instant at 50m count_scalar(http_requests{job=~"^server$"}) + 0 + +eval instant at 50m http_requests{group="production",job=~"^api"} + http_requests{group="production", instance="0", job="api-server"} 100 + http_requests{group="production", instance="1", job="api-server"} 200 + +eval instant at 50m abs(-1 * http_requests{group="production",job="api-server"}) + {group="production", instance="0", job="api-server"} 100 + {group="production", instance="1", job="api-server"} 200 + +eval instant at 50m floor(0.004 * http_requests{group="production",job="api-server"}) + {group="production", instance="0", job="api-server"} 0 + {group="production", instance="1", job="api-server"} 0 + +eval instant at 50m ceil(0.004 * http_requests{group="production",job="api-server"}) + {group="production", instance="0", job="api-server"} 1 + {group="production", instance="1", job="api-server"} 1 + +eval instant at 50m round(0.004 * http_requests{group="production",job="api-server"}) + {group="production", instance="0", job="api-server"} 0 + {group="production", instance="1", job="api-server"} 1 + +# Round should correctly handle negative numbers. +eval instant at 50m round(-1 * (0.004 * http_requests{group="production",job="api-server"})) + {group="production", instance="0", job="api-server"} 0 + {group="production", instance="1", job="api-server"} -1 + +# Round should round half up. +eval instant at 50m round(0.005 * http_requests{group="production",job="api-server"}) + {group="production", instance="0", job="api-server"} 1 + {group="production", instance="1", job="api-server"} 1 + +eval instant at 50m round(-1 * (0.005 * http_requests{group="production",job="api-server"})) + {group="production", instance="0", job="api-server"} 0 + {group="production", instance="1", job="api-server"} -1 + +eval instant at 50m round(1 + 0.005 * http_requests{group="production",job="api-server"}) + {group="production", instance="0", job="api-server"} 2 + {group="production", instance="1", job="api-server"} 2 + +eval instant at 50m round(-1 * (1 + 0.005 * http_requests{group="production",job="api-server"})) + {group="production", instance="0", job="api-server"} -1 + {group="production", instance="1", job="api-server"} -2 + +# Round should accept the number to round nearest to. +eval instant at 50m round(0.0005 * http_requests{group="production",job="api-server"}, 0.1) + {group="production", instance="0", job="api-server"} 0.1 + {group="production", instance="1", job="api-server"} 0.1 + +eval instant at 50m round(2.1 + 0.0005 * http_requests{group="production",job="api-server"}, 0.1) + {group="production", instance="0", job="api-server"} 2.2 + {group="production", instance="1", job="api-server"} 2.2 + +eval instant at 50m round(5.2 + 0.0005 * http_requests{group="production",job="api-server"}, 0.1) + {group="production", instance="0", job="api-server"} 5.3 + {group="production", instance="1", job="api-server"} 5.3 + +# Round should work correctly with negative numbers and multiple decimal places. +eval instant at 50m round(-1 * (5.2 + 0.0005 * http_requests{group="production",job="api-server"}), 0.1) + {group="production", instance="0", job="api-server"} -5.2 + {group="production", instance="1", job="api-server"} -5.3 + +# Round should work correctly with big toNearests. +eval instant at 50m round(0.025 * http_requests{group="production",job="api-server"}, 5) + {group="production", instance="0", job="api-server"} 5 + {group="production", instance="1", job="api-server"} 5 + +eval instant at 50m round(0.045 * http_requests{group="production",job="api-server"}, 5) + {group="production", instance="0", job="api-server"} 5 + {group="production", instance="1", job="api-server"} 10 + +eval instant at 50m avg_over_time(http_requests{group="production",job="api-server"}[1h]) + {group="production", instance="0", job="api-server"} 50 + {group="production", instance="1", job="api-server"} 100 + +eval instant at 50m count_over_time(http_requests{group="production",job="api-server"}[1h]) + {group="production", instance="0", job="api-server"} 11 + {group="production", instance="1", job="api-server"} 11 + +eval instant at 50m max_over_time(http_requests{group="production",job="api-server"}[1h]) + {group="production", instance="0", job="api-server"} 100 + {group="production", instance="1", job="api-server"} 200 + +eval instant at 50m min_over_time(http_requests{group="production",job="api-server"}[1h]) + {group="production", instance="0", job="api-server"} 0 + {group="production", instance="1", job="api-server"} 0 + +eval instant at 50m sum_over_time(http_requests{group="production",job="api-server"}[1h]) + {group="production", instance="0", job="api-server"} 550 + {group="production", instance="1", job="api-server"} 1100 + +eval instant at 50m time() + 3000 + +eval instant at 50m drop_common_labels(http_requests{group="production",job="api-server"}) + http_requests{instance="0"} 100 + http_requests{instance="1"} 200 + +eval instant at 50m {__name__=~".*"} + http_requests{group="canary", instance="0", job="api-server"} 300 + http_requests{group="canary", instance="0", job="app-server"} 700 + http_requests{group="canary", instance="1", job="api-server"} 400 + http_requests{group="canary", instance="1", job="app-server"} 800 + http_requests{group="production", instance="0", job="api-server"} 100 + http_requests{group="production", instance="0", job="app-server"} 500 + http_requests{group="production", instance="1", job="api-server"} 200 + http_requests{group="production", instance="1", job="app-server"} 600 + testcounter_reset_end 0 + testcounter_reset_middle 50 + x{y="testvalue"} 100 + label_grouping_test{a="a", b="abb"} 200 + label_grouping_test{a="aa", b="bb"} 100 + vector_matching_a{l="x"} 10 + vector_matching_a{l="y"} 20 + vector_matching_b{l="x"} 40 + cpu_count{instance="1", type="smp"} 200 + cpu_count{instance="0", type="smp"} 100 + cpu_count{instance="0", type="numa"} 300 + + +eval instant at 50m {job=~"server", job!~"api"} + http_requests{group="canary", instance="0", job="app-server"} 700 + http_requests{group="canary", instance="1", job="app-server"} 800 + http_requests{group="production", instance="0", job="app-server"} 500 + http_requests{group="production", instance="1", job="app-server"} 600 + +# Test alternative "by"-clause order. +eval instant at 50m sum by (group) (http_requests{job="api-server"}) + {group="canary"} 700 + {group="production"} 300 + +# Test alternative "by"-clause order with "keeping_extra". +eval instant at 50m sum by (group) keeping_extra (http_requests{job="api-server"}) + {group="canary", job="api-server"} 700 + {group="production", job="api-server"} 300 + +# Test both alternative "by"-clause orders in one expression. +# Public health warning: stick to one form within an expression (or even +# in an organization), or risk serious user confusion. +eval instant at 50m sum(sum by (group) keeping_extra (http_requests{job="api-server"})) by (job) + {job="api-server"} 1000 + +eval instant at 50m http_requests{group="canary"} and http_requests{instance="0"} + http_requests{group="canary", instance="0", job="api-server"} 300 + http_requests{group="canary", instance="0", job="app-server"} 700 + +eval instant at 50m (http_requests{group="canary"} + 1) and http_requests{instance="0"} + {group="canary", instance="0", job="api-server"} 301 + {group="canary", instance="0", job="app-server"} 701 + +eval instant at 50m (http_requests{group="canary"} + 1) and on(instance, job) http_requests{instance="0", group="production"} + {group="canary", instance="0", job="api-server"} 301 + {group="canary", instance="0", job="app-server"} 701 + +eval instant at 50m (http_requests{group="canary"} + 1) and on(instance) http_requests{instance="0", group="production"} + {group="canary", instance="0", job="api-server"} 301 + {group="canary", instance="0", job="app-server"} 701 + +eval instant at 50m http_requests{group="canary"} or http_requests{group="production"} + http_requests{group="canary", instance="0", job="api-server"} 300 + http_requests{group="canary", instance="0", job="app-server"} 700 + http_requests{group="canary", instance="1", job="api-server"} 400 + http_requests{group="canary", instance="1", job="app-server"} 800 + http_requests{group="production", instance="0", job="api-server"} 100 + http_requests{group="production", instance="0", job="app-server"} 500 + http_requests{group="production", instance="1", job="api-server"} 200 + http_requests{group="production", instance="1", job="app-server"} 600 + +# On overlap the rhs samples must be dropped. +eval instant at 50m (http_requests{group="canary"} + 1) or http_requests{instance="1"} + {group="canary", instance="0", job="api-server"} 301 + {group="canary", instance="0", job="app-server"} 701 + {group="canary", instance="1", job="api-server"} 401 + {group="canary", instance="1", job="app-server"} 801 + http_requests{group="production", instance="1", job="api-server"} 200 + http_requests{group="production", instance="1", job="app-server"} 600 + +# Matching only on instance excludes everything that has instance=0/1 but includes +# entries without the instance label. +eval instant at 50m (http_requests{group="canary"} + 1) or on(instance) (http_requests or cpu_count or vector_matching_a) + {group="canary", instance="0", job="api-server"} 301 + {group="canary", instance="0", job="app-server"} 701 + {group="canary", instance="1", job="api-server"} 401 + {group="canary", instance="1", job="app-server"} 801 + vector_matching_a{l="x"} 10 + vector_matching_a{l="y"} 20 + +eval instant at 50m http_requests{group="canary"} / on(instance,job) http_requests{group="production"} + {instance="0", job="api-server"} 3 + {instance="0", job="app-server"} 1.4 + {instance="1", job="api-server"} 2 + {instance="1", job="app-server"} 1.3333333333333333 + +# Include labels must guarantee uniquely identifiable time series. +eval_fail instant at 50m http_requests{group="production"} / on(instance) group_left(group) cpu_count{type="smp"} + +# Many-to-many matching is not allowed. +eval_fail instant at 50m http_requests{group="production"} / on(instance) group_left(job,type) cpu_count + +# Many-to-one matching must be explicit. +eval_fail instant at 50m http_requests{group="production"} / on(instance) cpu_count{type="smp"} + +eval instant at 50m http_requests{group="production"} / on(instance) group_left(job) cpu_count{type="smp"} + {instance="1", job="api-server"} 1 + {instance="0", job="app-server"} 5 + {instance="1", job="app-server"} 3 + {instance="0", job="api-server"} 1 + +# Ensure sidedness of grouping preserves operand sides. +eval instant at 50m cpu_count{type="smp"} / on(instance) group_right(job) http_requests{group="production"} + {instance="1", job="app-server"} 0.3333333333333333 + {instance="0", job="app-server"} 0.2 + {instance="1", job="api-server"} 1 + {instance="0", job="api-server"} 1 + +# Include labels from both sides. +eval instant at 50m http_requests{group="production"} / on(instance) group_left(job) cpu_count{type="smp"} + {instance="1", job="api-server"} 1 + {instance="0", job="app-server"} 5 + {instance="1", job="app-server"} 3 + {instance="0", job="api-server"} 1 + +eval instant at 50m http_requests{group="production"} < on(instance,job) http_requests{group="canary"} + {instance="1", job="app-server"} 600 + {instance="0", job="app-server"} 500 + {instance="1", job="api-server"} 200 + {instance="0", job="api-server"} 100 + + +eval instant at 50m http_requests{group="production"} > on(instance,job) http_requests{group="canary"} + # no output + +eval instant at 50m http_requests{group="production"} == on(instance,job) http_requests{group="canary"} + # no output + +eval instant at 50m http_requests > on(instance) group_left(group,job) cpu_count{type="smp"} + {group="canary", instance="0", job="app-server"} 700 + {group="canary", instance="1", job="app-server"} 800 + {group="canary", instance="0", job="api-server"} 300 + {group="canary", instance="1", job="api-server"} 400 + {group="production", instance="0", job="app-server"} 500 + {group="production", instance="1", job="app-server"} 600 + +eval instant at 50m {l="x"} + on(__name__) {l="y"} + vector_matching_a 30 + +eval instant at 50m absent(nonexistent) + {} 1 + + +eval instant at 50m absent(nonexistent{job="testjob", instance="testinstance", method=~".x"}) + {instance="testinstance", job="testjob"} 1 + +eval instant at 50m count_scalar(absent(http_requests)) + 0 + +eval instant at 50m count_scalar(absent(sum(http_requests))) + 0 + +eval instant at 50m absent(sum(nonexistent{job="testjob", instance="testinstance"})) + {} 1 + +eval instant at 50m http_requests{group="production",job="api-server"} offset 5m + http_requests{group="production", instance="0", job="api-server"} 90 + http_requests{group="production", instance="1", job="api-server"} 180 + +eval instant at 50m rate(http_requests{group="production",job="api-server"}[10m] offset 5m) + {group="production", instance="0", job="api-server"} 0.03333333333333333 + {group="production", instance="1", job="api-server"} 0.06666666666666667 + +# Regression test for missing separator byte in labelsToGroupingKey. +eval instant at 50m sum(label_grouping_test) by (a, b) + {a="a", b="abb"} 200 + {a="aa", b="bb"} 100 + +eval instant at 50m http_requests{group="canary", instance="0", job="api-server"} / 0 + {group="canary", instance="0", job="api-server"} +Inf + +eval instant at 50m -1 * http_requests{group="canary", instance="0", job="api-server"} / 0 + {group="canary", instance="0", job="api-server"} -Inf + +eval instant at 50m 0 * http_requests{group="canary", instance="0", job="api-server"} / 0 + {group="canary", instance="0", job="api-server"} NaN + +eval instant at 50m 0 * http_requests{group="canary", instance="0", job="api-server"} % 0 + {group="canary", instance="0", job="api-server"} NaN + +eval instant at 50m exp(vector_matching_a) + {l="x"} 22026.465794806718 + {l="y"} 485165195.4097903 + +eval instant at 50m exp(vector_matching_a - 10) + {l="y"} 22026.465794806718 + {l="x"} 1 + +eval instant at 50m exp(vector_matching_a - 20) + {l="x"} 4.5399929762484854e-05 + {l="y"} 1 + +eval instant at 50m ln(vector_matching_a) + {l="x"} 2.302585092994046 + {l="y"} 2.995732273553991 + +eval instant at 50m ln(vector_matching_a - 10) + {l="y"} 2.302585092994046 + {l="x"} -Inf + +eval instant at 50m ln(vector_matching_a - 20) + {l="y"} -Inf + {l="x"} NaN + +eval instant at 50m exp(ln(vector_matching_a)) + {l="y"} 20 + {l="x"} 10 + +eval instant at 50m sqrt(vector_matching_a) + {l="x"} 3.1622776601683795 + {l="y"} 4.47213595499958 + +eval instant at 50m log2(vector_matching_a) + {l="x"} 3.3219280948873626 + {l="y"} 4.321928094887363 + +eval instant at 50m log2(vector_matching_a - 10) + {l="y"} 3.3219280948873626 + {l="x"} -Inf + +eval instant at 50m log2(vector_matching_a - 20) + {l="x"} NaN + {l="y"} -Inf + +eval instant at 50m log10(vector_matching_a) + {l="x"} 1 + {l="y"} 1.301029995663981 + +eval instant at 50m log10(vector_matching_a - 10) + {l="y"} 1 + {l="x"} -Inf + +eval instant at 50m log10(vector_matching_a - 20) + {l="x"} NaN + {l="y"} -Inf + +eval instant at 50m stddev(http_requests) + {} 229.12878474779 + +eval instant at 50m stddev by (instance)(http_requests) + {instance="0"} 223.60679774998 + {instance="1"} 223.60679774998 + +eval instant at 50m stdvar(http_requests) + {} 52500 + +eval instant at 50m stdvar by (instance)(http_requests) + {instance="0"} 50000 + {instance="1"} 50000