@@ -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,48 @@ 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 ), 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+ }
109+ }
110+ return nil
111+ })
112+ return valid
113+ }
114+
60115func FuzzEnginePromQLSmithRangeQuery (f * testing.F ) {
61116 f .Add (int64 (0 ), uint32 (0 ), uint32 (120 ), uint32 (30 ), 1.0 , 1.0 , 1.0 , 2.0 , 30 )
62117
@@ -114,6 +169,9 @@ func FuzzEnginePromQLSmithRangeQuery(f *testing.F) {
114169 for i := range testRuns {
115170 for {
116171 expr := ps .WalkRangeQuery ()
172+ if ! validateExpr (expr , testTypeFloat ) {
173+ continue
174+ }
117175 validateSamples = shouldValidateSamples (expr )
118176
119177 query = expr .Pretty (0 )
@@ -200,8 +258,9 @@ func FuzzEnginePromQLSmithInstantQuery(f *testing.F) {
200258 ps := promqlsmith .New (rnd , seriesSet , psOpts ... )
201259
202260 var (
203- q1 promql.Query
204- query string
261+ q1 promql.Query
262+ query string
263+ validateSamples bool
205264 )
206265 cases := make ([]* testCase , testRuns )
207266 for i := range testRuns {
@@ -210,9 +269,10 @@ func FuzzEnginePromQLSmithInstantQuery(f *testing.F) {
210269 // Parsing experimental function, like mad_over_time, will lead to a parser.ParseErrors, so we also ignore those.
211270 for {
212271 expr := ps .WalkInstantQuery ()
213- if ! shouldValidateSamples (expr ) {
272+ if ! validateExpr (expr , testTypeFloat ) {
214273 continue
215274 }
275+ validateSamples = shouldValidateSamples (expr )
216276 query = expr .Pretty (0 )
217277 q1 , err = newEngine .NewInstantQuery (context .Background (), storage , qOpts , query , queryTime )
218278 if engine .IsUnimplemented (err ) || errors .As (err , & parser.ParseErrors {}) {
@@ -243,7 +303,7 @@ func FuzzEnginePromQLSmithInstantQuery(f *testing.F) {
243303 loads : []string {load },
244304 start : queryTime ,
245305 end : queryTime ,
246- validateSamples : true ,
306+ validateSamples : validateSamples ,
247307 }
248308 }
249309 validateTestCases (t , cases )
@@ -278,7 +338,6 @@ func validateTestCases(t *testing.T, cases []*testCase) {
278338 for i , c := range cases {
279339 if ! cmp .Equal (c .oldRes , c .newRes , comparer ) {
280340 logQuery (c )
281-
282341 t .Logf ("case %d error mismatch.\n new result: %s\n old result: %s\n " , i , c .newRes .String (), c .oldRes .String ())
283342 failures ++
284343 continue
@@ -308,7 +367,6 @@ func getMaxNativeHistogramSumLimit(schema int8, noOfBuckets int) float64 {
308367}
309368
310369func FuzzNativeHistogramQuery (f * testing.F ) {
311- f .Skip ()
312370 f .Add (int64 (0 ), uint32 (0 ), uint32 (60 ), uint32 (120 ), int8 (0 ), int8 (0 ), uint64 (1 ), uint64 (2 ), uint64 (1 ))
313371
314372 f .Fuzz (func (t * testing.T , seed int64 , startTS , endTS , intervalSeconds uint32 , schema1 int8 , schema2 int8 , bucketValue1 , bucketValue2 , bucketValue3 uint64 ) {
@@ -323,6 +381,9 @@ func FuzzNativeHistogramQuery(f *testing.F) {
323381 }
324382
325383 bucket1 := []uint64 {bucketValue1 , bucketValue2 , bucketValue3 }
384+ if bucketValue1 == 0 || bucketValue2 == 0 || bucketValue3 == 0 {
385+ return
386+ }
326387 bucket2 := []uint64 {2 * bucketValue1 , bucketValue2 , 2 * bucketValue3 }
327388
328389 sum1 := getMaxNativeHistogramSumLimit (schema1 , len (bucket1 ))
@@ -349,12 +410,12 @@ func FuzzNativeHistogramQuery(f *testing.F) {
349410 rnd := rand .New (rand .NewSource (seed ))
350411
351412 load := fmt .Sprintf (`load 2m
352- http_request_duration_seconds{pod="nginx-1"} {{schema:%d count:%d sum:%.2f buckets:[%d %d]}}+{{schema:%d count:%d buckets:[%d %d %d]}}x20
353- http_request_duration_seconds{pod="nginx-2"} {{schema:%d count:%d sum:%.2f buckets:[%d]}}+{{schema:%d count:%d buckets:[%d %d %d]}}x20 ` ,
354- schema1 , count1 - bucket1 [ 2 ] , sum1 , bucket1 [0 ], bucket1 [1 ],
413+ http_request_duration_seconds{pod="nginx-1"} {{schema:%d count:%d sum:%.2f buckets:[%d %d %d ]}}+{{schema:%d count:%d buckets:[%d %d %d]}}x20
414+ http_request_duration_seconds{pod="nginx-2"} {{schema:%d count:%d sum:%.2f buckets:[%d %d %d ]}}+{{schema:%d count:%d buckets:[%d %d %d]}}x30 ` ,
415+ schema1 , count1 , sum1 , bucket1 [0 ], bucket1 [1 ], bucket1 [ 2 ],
355416 schema1 , count1 , bucket1 [0 ], bucket1 [1 ], bucket1 [2 ],
356417
357- schema2 , count2 - bucket2 [1 ] - bucket2 [2 ], sum2 , bucket2 [0 ],
418+ schema2 , count2 , sum2 , bucket2 [0 ], bucket2 [1 ], bucket2 [2 ],
358419 schema2 , count2 , bucket2 [0 ], bucket2 [1 ], bucket2 [2 ],
359420 )
360421
@@ -396,17 +457,21 @@ func FuzzNativeHistogramQuery(f *testing.F) {
396457
397458 for range testRuns / 2 {
398459 var (
399- qInstant promql.Query
400- qRange promql.Query
401- instantQuery string
402- rangeQuery string
460+ qInstant promql.Query
461+ qRange promql.Query
462+ instantQuery string
463+ rangeQuery string
464+ validateSamplesForInstantQuery bool
465+ validateSamplesForRangeQuery bool
403466 )
404467
405468 for {
406469 expr := ps .WalkInstantQuery ()
407- if ! shouldValidateSamples (expr ) {
470+ if ! validateExpr (expr , testTypeNativeHistogram ) {
408471 continue
409472 }
473+
474+ validateSamplesForInstantQuery = shouldValidateSamples (expr )
410475 instantQuery = expr .Pretty (0 )
411476
412477 qInstant , err = newEngine .NewInstantQuery (context .Background (), storage , qOpts , instantQuery , startTime )
@@ -420,9 +485,11 @@ func FuzzNativeHistogramQuery(f *testing.F) {
420485
421486 for {
422487 expr := ps .WalkRangeQuery ()
423- if ! shouldValidateSamples (expr ) {
488+ if ! validateExpr (expr , testTypeNativeHistogram ) {
424489 continue
425490 }
491+
492+ validateSamplesForRangeQuery = shouldValidateSamples (expr )
426493 rangeQuery = expr .Pretty (0 )
427494
428495 qRange , err = newEngine .NewRangeQuery (context .Background (), storage , qOpts , rangeQuery , startTime , endTime , interval )
@@ -463,7 +530,8 @@ func FuzzNativeHistogramQuery(f *testing.F) {
463530 loads : []string {load },
464531 start : startTime ,
465532 end : startTime ,
466- validateSamples : true ,
533+ interval : 0 ,
534+ validateSamples : validateSamplesForInstantQuery ,
467535 })
468536
469537 rangeCases = append (rangeCases , & testCase {
@@ -475,7 +543,8 @@ func FuzzNativeHistogramQuery(f *testing.F) {
475543 loads : []string {load },
476544 start : startTime ,
477545 end : endTime ,
478- validateSamples : true ,
546+ interval : interval ,
547+ validateSamples : validateSamplesForRangeQuery ,
479548 })
480549 }
481550
0 commit comments