diff --git a/promql/promqltest/testdata/fill-modifier.test b/promql/promqltest/testdata/fill-modifier.test new file mode 100644 index 0000000000..08c4396242 --- /dev/null +++ b/promql/promqltest/testdata/fill-modifier.test @@ -0,0 +1,343 @@ +# ==================== fill / fill_left / fill_right modifier tests ==================== + +# Test data for fill modifier tests: vectors with partial overlap. +load 5m + left_vector{label="a"} 10 + left_vector{label="b"} 20 + left_vector{label="c"} 30 + right_vector{label="a"} 100 + right_vector{label="b"} 200 + right_vector{label="d"} 400 + +# ---------- Arithmetic operators with fill modifiers ---------- + +# fill(0): Fill both sides with 0 for addition. +eval instant at 0m left_vector + fill(0) right_vector + {label="a"} 110 + {label="b"} 220 + {label="c"} 30 + {label="d"} 400 + +# fill_left(0): Only fill left side with 0. +eval instant at 0m left_vector + fill_left(0) right_vector + {label="a"} 110 + {label="b"} 220 + {label="d"} 400 + +# fill_right(0): Only fill right side with 0. +eval instant at 0m left_vector + fill_right(0) right_vector + {label="a"} 110 + {label="b"} 220 + {label="c"} 30 + +# fill_left and fill_right with different values. +eval instant at 0m left_vector + fill_left(5) fill_right(7) right_vector + {label="a"} 110 + {label="b"} 220 + {label="c"} 37 + {label="d"} 405 + +# fill with NaN. +eval instant at 0m left_vector + fill(NaN) right_vector + {label="a"} 110 + {label="b"} 220 + {label="c"} NaN + {label="d"} NaN + +# fill with Inf. +eval instant at 0m left_vector + fill(Inf) right_vector + {label="a"} 110 + {label="b"} 220 + {label="c"} +Inf + {label="d"} +Inf + +# fill with -Inf. +eval instant at 0m left_vector + fill(-Inf) right_vector + {label="a"} 110 + {label="b"} 220 + {label="c"} -Inf + {label="d"} -Inf + +# ---------- Comparison operators with fill modifiers ---------- + +# fill with equality comparison. +eval instant at 0m left_vector == fill(30) right_vector + left_vector{label="c"} 30 + +# fill with inequality comparison. +eval instant at 0m left_vector != fill(30) right_vector + left_vector{label="a"} 10 + left_vector{label="b"} 20 + {label="d"} 30 + +# fill with greater than. +eval instant at 0m left_vector > fill(25) right_vector + left_vector{label="c"} 30 + +# ---------- Comparison operators with bool modifier and fill ---------- + +# fill with equality comparison and bool. +eval instant at 0m left_vector == bool fill(30) right_vector + {label="a"} 0 + {label="b"} 0 + {label="c"} 1 + {label="d"} 0 + +# fill with inequality comparison and bool. +eval instant at 0m left_vector != bool fill(30) right_vector + {label="a"} 1 + {label="b"} 1 + {label="c"} 0 + {label="d"} 1 + +# fill with greater than and bool. +eval instant at 0m left_vector > bool fill(25) right_vector + {label="a"} 0 + {label="b"} 0 + {label="c"} 1 + {label="d"} 0 + +# ---------- fill with on() and ignoring() modifiers ---------- + +clear + +load 5m + left_vector{job="foo", instance="a"} 10 + left_vector{job="foo", instance="b"} 20 + left_vector{job="bar", instance="a"} 30 + right_vector{job="foo", instance="a"} 100 + right_vector{job="foo", instance="c"} 300 + +# fill with on(). +eval instant at 0m left_vector + on(job, instance) fill(0) right_vector + {job="foo", instance="a"} 110 + {job="foo", instance="b"} 20 + {job="bar", instance="a"} 30 + {job="foo", instance="c"} 300 + +# fill_right with on(). +eval instant at 0m left_vector + on(job, instance) fill_right(0) right_vector + {job="foo", instance="a"} 110 + {job="foo", instance="b"} 20 + {job="bar", instance="a"} 30 + +# fill_left with on(). +eval instant at 0m left_vector + on(job, instance) fill_left(0) right_vector + {job="foo", instance="a"} 110 + {job="foo", instance="c"} 300 + +# fill with ignoring() - requires group_left since ignoring(job) creates many-to-one matching +# when two left_vector series have same instance but different jobs. +eval instant at 0m left_vector + ignoring(job) group_left fill(0) right_vector + {instance="a", job="foo"} 110 + {instance="a", job="bar"} 130 + {instance="b", job="foo"} 20 + {instance="c"} 300 + +# ---------- fill with group_left / group_right (many-to-one / one-to-many) ---------- + +clear + +load 5m + requests{method="GET", status="200"} 100 + requests{method="POST", status="200"} 200 + requests{method="GET", status="500"} 10 + requests{method="POST", status="500"} 20 + limits{status="200"} 1000 + limits{status="404"} 500 + limits{status="500"} 50 + +# group_left with fill_right: fill missing "one" side series. +eval instant at 0m requests / on(status) group_left fill_right(1) limits + {method="GET", status="200"} 0.1 + {method="POST", status="200"} 0.2 + {method="GET", status="500"} 0.2 + {method="POST", status="500"} 0.4 + +# group_left with fill_left: fill missing "many" side series. +# For status="404", there's no matching requests, so a single series with the match group's labels is filled +eval instant at 0m requests + on(status) group_left fill_left(0) limits + {method="GET", status="200"} 1100 + {method="POST", status="200"} 1200 + {method="GET", status="500"} 60 + {method="POST", status="500"} 70 + {status="404"} 500 + +# group_left with fill on both sides. +eval instant at 0m requests + on(status) group_left fill(0) limits + {method="GET", status="200"} 1100 + {method="POST", status="200"} 1200 + {method="GET", status="500"} 60 + {method="POST", status="500"} 70 + {status="404"} 500 + +# group_right with fill_left: fill missing "one" side series. +clear + +load 5m + cpu_info{instance="a", cpu="0"} 1 + cpu_info{instance="a", cpu="1"} 1 + cpu_info{instance="b", cpu="0"} 1 + node_meta{instance="a"} 100 + node_meta{instance="c"} 300 + +# fill_left fills the "one" side (node_meta) when missing for a "many" side series. +eval instant at 0m node_meta * on(instance) group_right fill_left(1) cpu_info + {instance="a", cpu="0"} 100 + {instance="a", cpu="1"} 100 + {instance="c"} 300 + +# group_right with fill_right: fill missing "many" side series. +eval instant at 0m node_meta * on(instance) group_right fill_right(0) cpu_info + {instance="a", cpu="0"} 100 + {instance="a", cpu="1"} 100 + {instance="b", cpu="0"} 0 + +# group_right with fill on both sides. +eval instant at 0m node_meta * on(instance) group_right fill(1) cpu_info + {instance="a", cpu="0"} 100 + {instance="a", cpu="1"} 100 + {instance="b", cpu="0"} 1 + {instance="c"} 300 + +# ---------- fill with group_left/group_right and extra labels ---------- + +clear + +load 5m + requests{method="GET", status="200"} 100 + requests{method="POST", status="200"} 200 + limits{status="200", owner="team-a"} 1000 + limits{status="500", owner="team-b"} 50 + +# group_left with extra label and fill_right. +# Note: when filling the "one" side, the joined label cannot be filled. +eval instant at 0m requests + on(status) group_left(owner) fill_right(0) limits + {method="GET", status="200", owner="team-a"} 1100 + {method="POST", status="200", owner="team-a"} 1200 + +# ---------- Edge cases ---------- + +clear + +load 5m + only_left{label="a"} 10 + only_left{label="b"} 20 + only_right{label="c"} 30 + only_right{label="d"} 40 + +# No overlap at all - fill creates all results. +eval instant at 0m only_left + fill(0) only_right + {label="a"} 10 + {label="b"} 20 + {label="c"} 30 + {label="d"} 40 + +# No overlap - fill_left only creates right side results. +eval instant at 0m only_left + fill_left(0) only_right + {label="c"} 30 + {label="d"} 40 + +# No overlap - fill_right only creates left side results. +eval instant at 0m only_left + fill_right(0) only_right + {label="a"} 10 + {label="b"} 20 + +# Complete overlap - fill has no effect. +clear + +load 5m + complete_left{label="a"} 10 + complete_left{label="b"} 20 + complete_right{label="a"} 100 + complete_right{label="b"} 200 + +eval instant at 0m complete_left + fill(99) complete_right + {label="a"} 110 + {label="b"} 220 + +# ---------- fill with range queries ---------- + +clear + +load 5m + range_left{label="a"} 1 2 3 4 5 + range_left{label="b"} 10 20 30 40 50 + range_right{label="a"} 100 200 300 400 500 + range_right{label="c"} 1000 2000 3000 4000 5000 + +eval range from 0 to 20m step 5m range_left + fill(0) range_right + {label="a"} 101 202 303 404 505 + {label="b"} 10 20 30 40 50 + {label="c"} 1000 2000 3000 4000 5000 + +eval range from 0 to 20m step 5m range_left + fill_right(0) range_right + {label="a"} 101 202 303 404 505 + {label="b"} 10 20 30 40 50 + +eval range from 0 to 20m step 5m range_left + fill_left(0) range_right + {label="a"} 101 202 303 404 505 + {label="c"} 1000 2000 3000 4000 5000 + +# Range queries with intermittently present series. +clear + +load 5m + intermittent_left{label="a"} 1 _ 3 _ 5 + intermittent_left{label="b"} _ 20 _ 40 _ + intermittent_right{label="a"} _ 200 _ 400 _ + intermittent_right{label="b"} 100 _ 300 _ 500 + intermittent_right{label="c"} 1000 _ _ 4000 5000 + +# When both sides have the same label but are present at different times, +# fill creates results at all timestamps where at least one side is present. +eval range from 0 to 20m step 5m intermittent_left + fill(0) intermittent_right + {label="a"} 1 200 3 400 5 + {label="b"} 100 20 300 40 500 + {label="c"} 1000 _ _ 4000 5000 + +# fill_right only fills the right side when it's missing. +# Output only exists when left side is present (right side filled with 0 if missing). +eval range from 0 to 20m step 5m intermittent_left + fill_right(0) intermittent_right + {label="a"} 1 _ 3 _ 5 + {label="b"} _ 20 _ 40 _ + +# fill_left only fills the left side when it's missing. +# Output only exists when right side is present (left side filled with 0 if missing). +eval range from 0 to 20m step 5m intermittent_left + fill_left(0) intermittent_right + {label="a"} _ 200 _ 400 _ + {label="b"} 100 _ 300 _ 500 + {label="c"} 1000 _ _ 4000 5000 + +# ---------- fill with vectors where one side is empty ---------- + +clear + +load 5m + non_empty{label="a"} 10 + non_empty{label="b"} 20 + +# Empty right side - fill_right has no effect (nothing to add). +eval instant at 0m non_empty + fill_right(0) nonexistent + {label="a"} 10 + {label="b"} 20 + +# Empty right side - fill_left creates nothing (no right side labels to use). +eval instant at 0m non_empty + fill_left(0) nonexistent + +# Empty left side - fill_left has no effect. +eval instant at 0m nonexistent + fill_left(0) non_empty + {label="a"} 10 + {label="b"} 20 + +# Empty left side - fill_right creates nothing. +eval instant at 0m nonexistent + fill_right(0) non_empty + +# fill both sides with one side empty. +eval instant at 0m non_empty + fill(0) nonexistent + {label="a"} 10 + {label="b"} 20 + +eval instant at 0m nonexistent + fill(0) non_empty + {label="a"} 10 + {label="b"} 20