@@ -20,6 +20,7 @@ import (
2020 "github.com/prometheus/prometheus/promql/parser"
2121 "github.com/prometheus/prometheus/promql/promqltest"
2222 "github.com/prometheus/prometheus/storage"
23+ "github.com/prometheus/prometheus/util/stats"
2324 "github.com/prometheus/prometheus/util/teststorage"
2425 "github.com/stretchr/testify/require"
2526
@@ -32,9 +33,31 @@ import (
3233const testRuns = 100
3334
3435type testCase struct {
35- query string
36- loads []string
37- oldRes , newRes * promql.Result
36+ query string
37+ loads []string
38+ oldRes , newRes * promql.Result
39+ oldStats , newStats * stats.Statistics
40+ start , end time.Time
41+ interval time.Duration
42+ validateSamples bool
43+ }
44+
45+ // shouldValidateSamples checks if the samples can be compared for the expr.
46+ // For certain known cases, prometheus engine and thanos engine returns different samples.
47+ func shouldValidateSamples (expr parser.Expr ) bool {
48+ valid := true
49+
50+ parser .Inspect (expr , func (node parser.Node , path []parser.Node ) error {
51+ switch n := node .(type ) {
52+ case * parser.Call :
53+ if n .Func .Name == "scalar" {
54+ valid = false
55+ return errors .New ("error" )
56+ }
57+ }
58+ return nil
59+ })
60+ return valid
3861}
3962
4063func FuzzEnginePromQLSmithRangeQuery (f * testing.F ) {
@@ -61,7 +84,9 @@ func FuzzEnginePromQLSmithRangeQuery(f *testing.F) {
6184 MaxSamples : 1e10 ,
6285 EnableNegativeOffset : true ,
6386 EnableAtModifier : true ,
87+ EnablePerStepStats : true ,
6488 }
89+ qOpts := promql .NewPrometheusQueryOpts (true , 0 )
6590
6691 storage := promqltest .LoadedStorage (t , load )
6792 defer storage .Close ()
@@ -80,22 +105,22 @@ func FuzzEnginePromQLSmithRangeQuery(f *testing.F) {
80105 }
81106 ps := promqlsmith .New (rnd , seriesSet , psOpts ... )
82107
83- newEngine := engine .New (engine.Opts {EngineOpts : opts , DisableFallback : true })
108+ newEngine := engine .New (engine.Opts {EngineOpts : opts , DisableFallback : true , EnableAnalysis : true })
84109 oldEngine := promql .NewEngine (opts )
85110
86111 var (
87- q1 promql.Query
88- query string
112+ q1 promql.Query
113+ query string
114+ validateSamples bool
89115 )
90116 cases := make ([]* testCase , testRuns )
91117 for i := 0 ; i < testRuns ; i ++ {
92- // Since we disabled fallback, keep trying until we find a query
93- // that can be natively executed by the engine.
94- // Parsing experimental function, like mad_over_time, will lead to a parser.ParseErrors, so we also ignore those.
95118 for {
96119 expr := ps .WalkRangeQuery ()
120+ validateSamples = shouldValidateSamples (expr )
121+
97122 query = expr .Pretty (0 )
98- q1 , err = newEngine .NewRangeQuery (context .Background (), storage , nil , query , start , end , interval )
123+ q1 , err = newEngine .NewRangeQuery (context .Background (), storage , qOpts , query , start , end , interval )
99124 if errors .Is (err , parse .ErrNotSupportedExpr ) || errors .Is (err , parse .ErrNotImplemented ) || errors .As (err , & parser.ParseErrors {}) {
100125 continue
101126 } else {
@@ -105,17 +130,27 @@ func FuzzEnginePromQLSmithRangeQuery(f *testing.F) {
105130
106131 testutil .Ok (t , err )
107132 newResult := q1 .Exec (context .Background ())
133+ newStats := q1 .Stats ()
134+ stats .NewQueryStats (newStats )
108135
109- q2 , err := oldEngine .NewRangeQuery (context .Background (), storage , nil , query , start , end , interval )
136+ q2 , err := oldEngine .NewRangeQuery (context .Background (), storage , qOpts , query , start , end , interval )
110137 testutil .Ok (t , err )
111138
112139 oldResult := q2 .Exec (context .Background ())
140+ oldStats := q2 .Stats ()
141+ stats .NewQueryStats (oldStats )
113142
114143 cases [i ] = & testCase {
115- query : query ,
116- newRes : newResult ,
117- oldRes : oldResult ,
118- loads : []string {load },
144+ query : query ,
145+ newRes : newResult ,
146+ newStats : newStats ,
147+ oldRes : oldResult ,
148+ oldStats : oldStats ,
149+ loads : []string {load },
150+ start : start ,
151+ end : end ,
152+ interval : interval ,
153+ validateSamples : validateSamples ,
119154 }
120155 }
121156 validateTestCases (t , cases )
@@ -141,7 +176,9 @@ func FuzzEnginePromQLSmithInstantQuery(f *testing.F) {
141176 MaxSamples : 1e10 ,
142177 EnableNegativeOffset : true ,
143178 EnableAtModifier : true ,
179+ EnablePerStepStats : true ,
144180 }
181+ qOpts := promql .NewPrometheusQueryOpts (true , 0 )
145182
146183 storage := promqltest .LoadedStorage (t , load )
147184 defer storage .Close ()
@@ -151,6 +188,7 @@ func FuzzEnginePromQLSmithInstantQuery(f *testing.F) {
151188 EngineOpts : opts ,
152189 DisableFallback : true ,
153190 LogicalOptimizers : logicalplan .AllOptimizers ,
191+ EnableAnalysis : true ,
154192 })
155193 oldEngine := promql .NewEngine (opts )
156194
@@ -176,8 +214,11 @@ func FuzzEnginePromQLSmithInstantQuery(f *testing.F) {
176214 // Parsing experimental function, like mad_over_time, will lead to a parser.ParseErrors, so we also ignore those.
177215 for {
178216 expr := ps .WalkInstantQuery ()
217+ if ! shouldValidateSamples (expr ) {
218+ continue
219+ }
179220 query = expr .Pretty (0 )
180- q1 , err = newEngine .NewInstantQuery (context .Background (), storage , nil , query , queryTime )
221+ q1 , err = newEngine .NewInstantQuery (context .Background (), storage , qOpts , query , queryTime )
181222 if errors .Is (err , parse .ErrNotSupportedExpr ) || errors .Is (err , parse .ErrNotImplemented ) || errors .As (err , & parser.ParseErrors {}) {
182223 continue
183224 } else {
@@ -187,17 +228,26 @@ func FuzzEnginePromQLSmithInstantQuery(f *testing.F) {
187228
188229 testutil .Ok (t , err )
189230 newResult := q1 .Exec (context .Background ())
231+ newStats := q1 .Stats ()
232+ stats .NewQueryStats (newStats )
190233
191- q2 , err := oldEngine .NewInstantQuery (context .Background (), storage , nil , query , queryTime )
234+ q2 , err := oldEngine .NewInstantQuery (context .Background (), storage , qOpts , query , queryTime )
192235 testutil .Ok (t , err )
193236
194237 oldResult := q2 .Exec (context .Background ())
238+ oldStats := q2 .Stats ()
239+ stats .NewQueryStats (oldStats )
195240
196241 cases [i ] = & testCase {
197- query : query ,
198- newRes : newResult ,
199- oldRes : oldResult ,
200- loads : []string {load },
242+ query : query ,
243+ newRes : newResult ,
244+ newStats : newStats ,
245+ oldRes : oldResult ,
246+ oldStats : oldStats ,
247+ loads : []string {load },
248+ start : queryTime ,
249+ end : queryTime ,
250+ validateSamples : true ,
201251 }
202252 }
203253 validateTestCases (t , cases )
@@ -444,14 +494,27 @@ func getSeries(ctx context.Context, q storage.Queryable) ([]labels.Labels, error
444494
445495func validateTestCases (t * testing.T , cases []* testCase ) {
446496 failures := 0
497+ logQuery := func (c * testCase ) {
498+ for _ , load := range c .loads {
499+ t .Logf (load )
500+ }
501+ t .Logf ("query: %s, start: %d, end: %d, interval: %v" , c .query , c .start .UnixMilli (), c .end .UnixMilli (), c .interval )
502+ }
447503 for i , c := range cases {
448504 if ! cmp .Equal (c .oldRes , c .newRes , comparer ) {
449- for _ , load := range c .loads {
450- t .Logf (load )
451- }
452- t .Logf (c .query )
505+ logQuery (c )
453506
454507 t .Logf ("case %d error mismatch.\n new result: %s\n old result: %s\n " , i , c .newRes .String (), c .oldRes .String ())
508+ //failures++
509+ continue
510+ }
511+ if ! c .validateSamples || c .oldRes .Err != nil {
512+ // Skip sample comparison
513+ continue
514+ }
515+ if ! cmp .Equal (c .oldStats .Samples , c .newStats .Samples , samplesComparer ) {
516+ logQuery (c )
517+ t .Logf ("case: %d, samples mismatch. total samples: old: %v, new: %v. samples per step: old: %v, new: %v" , i , c .oldStats .Samples .TotalSamples , c .newStats .Samples .TotalSamples , c .oldStats .Samples .TotalSamplesPerStep , c .newStats .Samples .TotalSamplesPerStep )
455518 failures ++
456519 }
457520 }
0 commit comments