@@ -294,6 +294,136 @@ func (s series) Iterator() chunkenc.Iterator {
294294 return newMockedSeriesIterator (s .samples )
295295}
296296
297+ // TestQuerier_Select_After_promql tests expected results with and without deduplication after passing all data to promql.
298+ // To test with real data:
299+ // Collect the expected results from Prometheus or Thanos through "/api/v1/query_range" and save to a file.
300+ // Collect raw data to be used for local storage:
301+ // scripts/insecure_grpcurl_series.sh queriesGrpcIP:port '[{"name": "__name__", "value":"cluster_version"},{"name":"_id","value":"xxx"}]' 1597823000000 1597824600000 > localStorage.json
302+ // Remove all white space from the file and put each series in a new line.
303+ // When collecting the raw data mint should be Prometheus query time minus the default look back delta(default is 5min or 300000ms)
304+ // For example if the Prometheus query mint is 1597823700000 the grpccurl query mint should be 1597823400000.
305+ // This is because when promql displays data for a given range it looks back 5min before the requested time window.
306+ func TestQuerier_Select_After_promql (t * testing.T ) {
307+ logger := log .NewLogfmtLogger (os .Stderr )
308+
309+ for _ , tcase := range []struct {
310+ name string
311+ storeAPI storepb.StoreServer
312+ replicaLabels []string // Replica label groups chunks by the label value and strips it from the final result.
313+ hints * storage.SelectHints
314+ equivalentQuery string
315+
316+ expected []series
317+ expectedAfterDedup series
318+ expectedWarning string
319+ }{
320+
321+ {
322+ // Simulate Prom with 1m scrape interval scraping 30s apart.
323+ // This should start with replica-1 until a brief outage,
324+ // then switch to replica-2 after not seeing a value for 2 * interval = 120s.
325+ name : "switch to replica 2 after an outage" ,
326+ storeAPI : & storeServer {
327+ resps : []* storepb.SeriesResponse {
328+ storeSeriesResponse (t , labels .FromStrings ("a" , "a" ), []sample {{0 , 1 }, {60000 , 3 }, {120000 , 5 } /* outage for 3 minutes */ , {300000 , 11 }, {360000 , 13 }}),
329+ storeSeriesResponse (t , labels .FromStrings ("a" , "b" ), []sample {{30000 , 2 }, {90000 , 5 }, {150000 , 6 }, {210000 , 8 }, {270000 , 10 }, {330000 , 12 }}),
330+ },
331+ },
332+ hints : & storage.SelectHints {
333+ Start : 0 ,
334+ End : 360000 ,
335+ Step : 60000 ,
336+ },
337+ replicaLabels : []string {"a" },
338+ equivalentQuery : `{a=~"a|b"}` ,
339+ expected : []series {
340+ {
341+ lset : labels .FromStrings ("a" , "a" ),
342+ samples : []sample {{0 , 1 }, {60000 , 3 }, {120000 , 5 }, {t : 180000 , v : 5 }, {t : 240000 , v : 5 }, {t : 300000 , v : 11 }, {t : 360000 , v : 13 }},
343+ },
344+ {
345+ lset : labels .FromStrings ("a" , "b" ),
346+ samples : []sample {{t : 60000 , v : 2 }, {t : 120000 , v : 5 }, {t : 180000 , v : 6 }, {t : 240000 , v : 8 }, {t : 300000 , v : 10 }, {t : 360000 , v : 12 }},
347+ },
348+ },
349+ expectedAfterDedup : series {
350+ lset : labels.Labels {},
351+ samples : []sample {{0 , 1 }, {60000 , 2 }, {120000 , 5 }, {t : 180000 , v : 6 }, {t : 240000 , v : 8 }, {t : 300000 , v : 10 }, {t : 360000 , v : 12 }},
352+ },
353+ },
354+
355+ {
356+ // // Regression test against https://github.com/thanos-io/thanos/issues/2890.
357+ name : "when switching replicas make sure the time window between samples is never bigger then the lookback delta" ,
358+ storeAPI : func () storepb.StoreServer {
359+ s , err := store .NewLocalStoreFromJSONMmappableFile (logger , component .Debug , nil , "./testdata/issue2890-seriesresponses.json" , store .ScanGRPCCurlProtoStreamMessages )
360+ testutil .Ok (t , err )
361+ return s
362+ }(),
363+ equivalentQuery : `cluster_version{}` ,
364+ replicaLabels : []string {"replica" },
365+ hints : & storage.SelectHints {
366+ Start : 1597823700000 ,
367+ End : 1597824600000 ,
368+ Step : 3000 ,
369+ },
370+ expected : jsonToSeries (t , "testdata/issue2890-expected.json" ),
371+ expectedAfterDedup : jsonToSeries (t , "testdata/issue2890-expected-dedup.json" )[0 ],
372+ },
373+ } {
374+ timeout := 5 * time .Minute
375+ e := promql .NewEngine (promql.EngineOpts {
376+ Logger : logger ,
377+ Timeout : timeout ,
378+ MaxSamples : math .MaxInt64 ,
379+ })
380+
381+ t .Run (tcase .name , func (t * testing.T ) {
382+ for _ , sc := range []struct {
383+ dedup bool
384+ expected []series
385+ }{
386+ {dedup : false , expected : tcase .expected },
387+ {dedup : true , expected : []series {tcase .expectedAfterDedup }},
388+ } {
389+
390+ resolution := time .Duration (tcase .hints .Step ) * time .Millisecond
391+ t .Run (fmt .Sprintf ("dedup=%v, resolution=%v" , sc .dedup , resolution .String ()), func (t * testing.T ) {
392+ var actual []series
393+ // Boostrap a local store and pass the data through promql.
394+ {
395+ g := gate .New (2 )
396+ mq := & mockedQueryable {
397+ Creator : func (mint , maxt int64 ) storage.Querier {
398+ return newQuerier (context .Background (), nil , nil , mint , maxt , tcase .replicaLabels , nil , tcase .storeAPI , sc .dedup , 0 , true , false , g , timeout )
399+ },
400+ }
401+ t .Cleanup (func () {
402+ testutil .Ok (t , mq .Close ())
403+ })
404+ q , err := e .NewRangeQuery (mq , tcase .equivalentQuery , timestamp .Time (tcase .hints .Start ), timestamp .Time (tcase .hints .End ), resolution )
405+ testutil .Ok (t , err )
406+ t .Cleanup (q .Close )
407+ res := q .Exec (context .Background ())
408+ testutil .Ok (t , res .Err )
409+ actual = promqlResToSeries (res )
410+ if tcase .expectedWarning != "" {
411+ warns := res .Warnings
412+ testutil .Assert (t , len (warns ) == 1 , "expected only single warnings" )
413+ testutil .Equals (t , tcase .expectedWarning , warns [0 ].Error ())
414+ }
415+ }
416+
417+ testutil .Equals (t , sc .expected , actual , "promql result doesn't match the expected output" )
418+ if sc .dedup {
419+ testutil .Assert (t , len (actual ) == 1 , "expected only single response, subqueries?" )
420+ }
421+ })
422+ }
423+ })
424+ }
425+ }
426+
297427func TestQuerier_Select (t * testing.T ) {
298428 logger := log .NewLogfmtLogger (os .Stderr )
299429
@@ -578,136 +708,6 @@ func testSelectResponse(t *testing.T, expected []series, res storage.SeriesSet)
578708 }
579709}
580710
581- // TestQuerier_Select_After_promql tests expected results with and without deduplication after passing all data to promql.
582- // To test with real data:
583- // Collect the expected results from Prometheus or Thanos through "/api/v1/query_range" and save to a file.
584- // Collect raw data to be used for local storage:
585- // scripts/insecure_grpcurl_series.sh queriesGrpcIP:port '[{"name": "__name__", "value":"cluster_version"},{"name":"_id","value":"xxx"}]' 1597823000000 1597824600000 > localStorage.json
586- // Remove all white space from the file and put each series in a new line.
587- // When collecting the raw data mint should be Prometheus query time minus the default look back delta(default is 5min or 300000ms)
588- // For example if the Prometheus query mint is 1597823700000 the grpccurl query mint should be 1597823400000.
589- // This is because when promql displays data for a given range it looks back 5min before the requested time window.
590- func TestQuerier_Select_After_promql (t * testing.T ) {
591- logger := log .NewLogfmtLogger (os .Stderr )
592-
593- for _ , tcase := range []struct {
594- name string
595- storeAPI storepb.StoreServer
596- replicaLabels []string // Replica label groups chunks by the label value and strips it from the final result.
597- hints * storage.SelectHints
598- equivalentQuery string
599-
600- expected []series
601- expectedAfterDedup series
602- expectedWarning string
603- }{
604-
605- {
606- // Simulate Prom with 1m scrape interval scraping 30s apart.
607- // This should start with replica-1 until a brief outage,
608- // then switch to replica-2 after not seeing a value for 2 * interval = 120s.
609- name : "switch to replica 2 after an outage" ,
610- storeAPI : & storeServer {
611- resps : []* storepb.SeriesResponse {
612- storeSeriesResponse (t , labels .FromStrings ("a" , "a" ), []sample {{0 , 1 }, {60000 , 3 }, {120000 , 5 } /* outage for 3 minutes */ , {300000 , 11 }, {360000 , 13 }}),
613- storeSeriesResponse (t , labels .FromStrings ("a" , "b" ), []sample {{30000 , 2 }, {90000 , 5 }, {150000 , 6 }, {210000 , 8 }, {270000 , 10 }, {330000 , 12 }}),
614- },
615- },
616- hints : & storage.SelectHints {
617- Start : 0 ,
618- End : 360000 ,
619- Step : 60000 ,
620- },
621- replicaLabels : []string {"a" },
622- equivalentQuery : `{a=~"a|b"}` ,
623- expected : []series {
624- {
625- lset : labels .FromStrings ("a" , "a" ),
626- samples : []sample {{0 , 1 }, {60000 , 3 }, {120000 , 5 }, {t : 180000 , v : 5 }, {t : 240000 , v : 5 }, {t : 300000 , v : 11 }, {t : 360000 , v : 13 }},
627- },
628- {
629- lset : labels .FromStrings ("a" , "b" ),
630- samples : []sample {{t : 60000 , v : 2 }, {t : 120000 , v : 5 }, {t : 180000 , v : 6 }, {t : 240000 , v : 8 }, {t : 300000 , v : 10 }, {t : 360000 , v : 12 }},
631- },
632- },
633- expectedAfterDedup : series {
634- lset : labels.Labels {},
635- samples : []sample {{0 , 1 }, {60000 , 2 }, {120000 , 5 }, {t : 180000 , v : 6 }, {t : 240000 , v : 8 }, {t : 300000 , v : 10 }, {t : 360000 , v : 12 }},
636- },
637- },
638-
639- {
640- // // Regression test against https://github.com/thanos-io/thanos/issues/2890.
641- name : "when switching replicas make sure the time window between samples is never bigger then the lookback delta" ,
642- storeAPI : func () storepb.StoreServer {
643- s , err := store .NewLocalStoreFromJSONMmappableFile (logger , component .Debug , nil , "./testdata/issue2890-seriesresponses.json" , store .ScanGRPCCurlProtoStreamMessages )
644- testutil .Ok (t , err )
645- return s
646- }(),
647- equivalentQuery : `cluster_version{}` ,
648- replicaLabels : []string {"replica" },
649- hints : & storage.SelectHints {
650- Start : 1597823700000 ,
651- End : 1597824600000 ,
652- Step : 3000 ,
653- },
654- expected : jsonToSeries (t , "testdata/issue2890-expected.json" ),
655- expectedAfterDedup : jsonToSeries (t , "testdata/issue2890-expected-dedup.json" )[0 ],
656- },
657- } {
658- timeout := 5 * time .Minute
659- e := promql .NewEngine (promql.EngineOpts {
660- Logger : logger ,
661- Timeout : timeout ,
662- MaxSamples : math .MaxInt64 ,
663- })
664-
665- t .Run (tcase .name , func (t * testing.T ) {
666- for _ , sc := range []struct {
667- dedup bool
668- expected []series
669- }{
670- {dedup : false , expected : tcase .expected },
671- {dedup : true , expected : []series {tcase .expectedAfterDedup }},
672- } {
673-
674- resolution := time .Duration (tcase .hints .Step ) * time .Millisecond
675- t .Run (fmt .Sprintf ("dedup=%v, resolution=%v" , sc .dedup , resolution .String ()), func (t * testing.T ) {
676- var actual []series
677- // Boostrap a local store and pass the data through promql.
678- {
679- g := gate .New (2 )
680- mq := & mockedQueryable {
681- Creator : func (mint , maxt int64 ) storage.Querier {
682- return newQuerier (context .Background (), nil , nil , mint , maxt , tcase .replicaLabels , nil , tcase .storeAPI , sc .dedup , 0 , true , false , g , timeout )
683- },
684- }
685- t .Cleanup (func () {
686- testutil .Ok (t , mq .Close ())
687- })
688- q , err := e .NewRangeQuery (mq , tcase .equivalentQuery , timestamp .Time (tcase .hints .Start ), timestamp .Time (tcase .hints .End ), resolution )
689- testutil .Ok (t , err )
690- t .Cleanup (q .Close )
691- res := q .Exec (context .Background ())
692- testutil .Ok (t , res .Err )
693- actual = promqlResToSeries (res )
694- if tcase .expectedWarning != "" {
695- warns := res .Warnings
696- testutil .Assert (t , len (warns ) == 1 , "expected only single warnings" )
697- testutil .Equals (t , tcase .expectedWarning , warns [0 ].Error ())
698- }
699- }
700-
701- testutil .Equals (t , sc .expected , actual , "promql result doesn't match the expected output" )
702- if sc .dedup {
703- testutil .Assert (t , len (actual ) == 1 , "expected only single response, subqueries?" )
704- }
705- })
706- }
707- })
708- }
709- }
710-
711711func jsonToSeries (t * testing.T , filename string ) []series {
712712 file , err := ioutil .ReadFile (filename )
713713 testutil .Ok (t , err )
0 commit comments