@@ -39,15 +39,28 @@ type testCase struct {
3939 validateSamples bool
4040}
4141
42+ type testType int
43+
44+ const (
45+ testTypeFloat testType = iota // 0
46+ testTypeNativeHistogram // 1
47+ )
48+
4249// shouldValidateSamples checks if the samples can be compared for the expr.
43- // For certain known cases, prometheus engine and thanos engine returns different samples .
50+ // For certain known cases, Thanos engine returns less samples than Prometheus engine due to optimizations .
4451func shouldValidateSamples (expr parser.Expr ) bool {
4552 valid := true
4653
4754 parser .Inspect (expr , func (node parser.Node , path []parser.Node ) error {
4855 switch n := node .(type ) {
4956 case * parser.Call :
50- if n .Func .Name == "scalar" {
57+ switch n .Func .Name {
58+ case "scalar" :
59+ // Optimized to step_invariant in Thanos engine.
60+ valid = false
61+ return errors .New ("error" )
62+ case "histogram_count" , "histogram_sum" , "histogram_avg" :
63+ // Optimized using DetectHistogramStatsOptimizer and will return smaller samples than Prometheus engine.
5164 valid = false
5265 return errors .New ("error" )
5366 }
@@ -57,6 +70,55 @@ func shouldValidateSamples(expr parser.Expr) bool {
5770 return valid
5871}
5972
73+ // validateExpr checks if the given expression is valid for fuzz tests.
74+ // For certain known cases Thanos engine results do not match with Prometheus engine.
75+ func validateExpr (expr parser.Expr , testType testType ) bool {
76+ expr = promql .PreprocessExpr (expr , time .Unix (0 , 0 ), time .Unix (0 , 0 ))
77+ valid := true
78+
79+ parser .Inspect (expr , func (node parser.Node , path []parser.Node ) error {
80+ switch n := node .(type ) {
81+ case * parser.Call :
82+ switch n .Func .Name {
83+ case "sort" , "sort_desc" , "sort_by_label" , "sort_by_label_desc" :
84+ if testType == testTypeNativeHistogram {
85+ // Prometheus engine filters out native histograms in nested sort().
86+ // Thanos engine implements sorting only at the presentation time and ignores nested sort().
87+ // See: https://github.com/thanos-io/promql-engine/pull/595
88+ valid = false
89+ return errors .New ("error" )
90+ }
91+ case "predict_linear" :
92+ switch t := n .Args [0 ].(type ) {
93+ case * parser.StepInvariantExpr :
94+ // Thanos engine cannot correctly handle a MatrixSelector wrapped by StepInvariant.
95+ // eg: predict_linear({__name__="http_request_duration_seconds"}[5m] @ end(), 0.5)
96+ // See: https://github.com/thanos-io/promql-engine/pull/527
97+ if _ , ok := t .Expr .(* parser.MatrixSelector ); ok {
98+ valid = false
99+ return errors .New ("error" )
100+ }
101+ }
102+ case "timestamp" :
103+ if testType == testTypeNativeHistogram {
104+ // TODO(johrry): Remove after merging https://github.com/thanos-io/promql-engine/pull/598
105+ valid = false
106+ return errors .New ("error" )
107+ }
108+ case "last_over_time" :
109+ if testType == testTypeNativeHistogram {
110+ // Upstream prometheus bug: https://github.com/prometheus/prometheus/pull/16744
111+ // TODO(johrry): Remove after pulling in latest Prometheus
112+ valid = false
113+ return errors .New ("error" )
114+ }
115+ }
116+ }
117+ return nil
118+ })
119+ return valid
120+ }
121+
60122func FuzzEnginePromQLSmithRangeQuery (f * testing.F ) {
61123 f .Add (int64 (0 ), uint32 (0 ), uint32 (120 ), uint32 (30 ), 1.0 , 1.0 , 1.0 , 2.0 , 30 )
62124
@@ -114,6 +176,9 @@ func FuzzEnginePromQLSmithRangeQuery(f *testing.F) {
114176 for i := range testRuns {
115177 for {
116178 expr := ps .WalkRangeQuery ()
179+ if ! validateExpr (expr , testTypeFloat ) {
180+ continue
181+ }
117182 validateSamples = shouldValidateSamples (expr )
118183
119184 query = expr .Pretty (0 )
@@ -200,8 +265,9 @@ func FuzzEnginePromQLSmithInstantQuery(f *testing.F) {
200265 ps := promqlsmith .New (rnd , seriesSet , psOpts ... )
201266
202267 var (
203- q1 promql.Query
204- query string
268+ q1 promql.Query
269+ query string
270+ validateSamples bool
205271 )
206272 cases := make ([]* testCase , testRuns )
207273 for i := range testRuns {
@@ -210,9 +276,10 @@ func FuzzEnginePromQLSmithInstantQuery(f *testing.F) {
210276 // Parsing experimental function, like mad_over_time, will lead to a parser.ParseErrors, so we also ignore those.
211277 for {
212278 expr := ps .WalkInstantQuery ()
213- if ! shouldValidateSamples (expr ) {
279+ if ! validateExpr (expr , testTypeFloat ) {
214280 continue
215281 }
282+ validateSamples = shouldValidateSamples (expr )
216283 query = expr .Pretty (0 )
217284 q1 , err = newEngine .NewInstantQuery (context .Background (), storage , qOpts , query , queryTime )
218285 if engine .IsUnimplemented (err ) || errors .As (err , & parser.ParseErrors {}) {
@@ -243,7 +310,7 @@ func FuzzEnginePromQLSmithInstantQuery(f *testing.F) {
243310 loads : []string {load },
244311 start : queryTime ,
245312 end : queryTime ,
246- validateSamples : true ,
313+ validateSamples : validateSamples ,
247314 }
248315 }
249316 validateTestCases (t , cases )
@@ -308,7 +375,6 @@ func getMaxNativeHistogramSumLimit(schema int8, noOfBuckets int) float64 {
308375}
309376
310377func FuzzNativeHistogramQuery (f * testing.F ) {
311- f .Skip ()
312378 f .Add (int64 (0 ), uint32 (0 ), uint32 (60 ), uint32 (120 ), int8 (0 ), int8 (0 ), uint64 (1 ), uint64 (2 ), uint64 (1 ))
313379
314380 f .Fuzz (func (t * testing.T , seed int64 , startTS , endTS , intervalSeconds uint32 , schema1 int8 , schema2 int8 , bucketValue1 , bucketValue2 , bucketValue3 uint64 ) {
@@ -396,17 +462,21 @@ func FuzzNativeHistogramQuery(f *testing.F) {
396462
397463 for range testRuns / 2 {
398464 var (
399- qInstant promql.Query
400- qRange promql.Query
401- instantQuery string
402- rangeQuery string
465+ qInstant promql.Query
466+ qRange promql.Query
467+ instantQuery string
468+ rangeQuery string
469+ validateSamplesForInstantQuery bool
470+ validateSamplesForRangeQuery bool
403471 )
404472
405473 for {
406474 expr := ps .WalkInstantQuery ()
407- if ! shouldValidateSamples (expr ) {
475+ if ! validateExpr (expr , testTypeNativeHistogram ) {
408476 continue
409477 }
478+
479+ validateSamplesForInstantQuery = shouldValidateSamples (expr )
410480 instantQuery = expr .Pretty (0 )
411481
412482 qInstant , err = newEngine .NewInstantQuery (context .Background (), storage , qOpts , instantQuery , startTime )
@@ -420,9 +490,11 @@ func FuzzNativeHistogramQuery(f *testing.F) {
420490
421491 for {
422492 expr := ps .WalkRangeQuery ()
423- if ! shouldValidateSamples (expr ) {
493+ if ! validateExpr (expr , testTypeNativeHistogram ) {
424494 continue
425495 }
496+
497+ validateSamplesForRangeQuery = shouldValidateSamples (expr )
426498 rangeQuery = expr .Pretty (0 )
427499
428500 qRange , err = newEngine .NewRangeQuery (context .Background (), storage , qOpts , rangeQuery , startTime , endTime , interval )
@@ -463,7 +535,8 @@ func FuzzNativeHistogramQuery(f *testing.F) {
463535 loads : []string {load },
464536 start : startTime ,
465537 end : startTime ,
466- validateSamples : true ,
538+ interval : 0 ,
539+ validateSamples : validateSamplesForInstantQuery ,
467540 })
468541
469542 rangeCases = append (rangeCases , & testCase {
@@ -475,7 +548,8 @@ func FuzzNativeHistogramQuery(f *testing.F) {
475548 loads : []string {load },
476549 start : startTime ,
477550 end : endTime ,
478- validateSamples : true ,
551+ interval : interval ,
552+ validateSamples : validateSamplesForRangeQuery ,
479553 })
480554 }
481555
0 commit comments