From c2efd5bf961b6fe63c7a9a7ec2d12bb1cc60411e Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 20 Oct 2021 15:03:02 +0100 Subject: [PATCH] promql: copy data when short-circuiting (#9552) * promql: copy data when short-circuiting Because the range query loop re-uses the output buffer each time round, we must copy results into the buffer rather than using input as output. Signed-off-by: Bryan Boreham (cherry picked from commit a278ea4b58226c3db271fb78703cd110089cba9b) --- promql/engine.go | 9 ++++++--- promql/engine_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/promql/engine.go b/promql/engine.go index e5dbcd2d77..7887e19f4a 100644 --- a/promql/engine.go +++ b/promql/engine.go @@ -1792,9 +1792,11 @@ func (ev *evaluator) VectorOr(lhs, rhs Vector, matching *parser.VectorMatching, panic("set operations must only use many-to-many matching") } if len(lhs) == 0 { // Short-circuit. - return rhs + enh.Out = append(enh.Out, rhs...) + return enh.Out } else if len(rhs) == 0 { - return lhs + enh.Out = append(enh.Out, lhs...) + return enh.Out } leftSigs := map[string]struct{}{} @@ -1819,7 +1821,8 @@ func (ev *evaluator) VectorUnless(lhs, rhs Vector, matching *parser.VectorMatchi // Short-circuit: empty rhs means we will return everything in lhs; // empty lhs means we will return empty - don't need to build a map. if len(lhs) == 0 || len(rhs) == 0 { - return lhs + enh.Out = append(enh.Out, lhs...) + return enh.Out } rightSigs := map[string]struct{}{} diff --git a/promql/engine_test.go b/promql/engine_test.go index c4eb07d523..7ac6ae1d2f 100644 --- a/promql/engine_test.go +++ b/promql/engine_test.go @@ -2410,6 +2410,32 @@ func TestRangeQuery(t *testing.T) { End: time.Unix(120, 0), Interval: 1 * time.Minute, }, + { + Name: "short-circuit", + Load: `load 30s + foo{job="1"} 1+1x4 + bar{job="2"} 1+1x4`, + Query: `foo > 2 or bar`, + Result: Matrix{ + Series{ + Points: []Point{{V: 1, T: 0}, {V: 3, T: 60000}, {V: 5, T: 120000}}, + Metric: labels.Labels{ + labels.Label{Name: "__name__", Value: "bar"}, + labels.Label{Name: "job", Value: "2"}, + }, + }, + Series{ + Points: []Point{{V: 3, T: 60000}, {V: 5, T: 120000}}, + Metric: labels.Labels{ + labels.Label{Name: "__name__", Value: "foo"}, + labels.Label{Name: "job", Value: "1"}, + }, + }, + }, + Start: time.Unix(0, 0), + End: time.Unix(120, 0), + Interval: 1 * time.Minute, + }, } for _, c := range cases { t.Run(c.Name, func(t *testing.T) {