@@ -973,6 +973,95 @@ func TestStoreGateway_SeriesQueryingShouldEnforceMaxChunksPerQueryLimit(t *testi
973
973
}
974
974
}
975
975
976
+ func TestStoreGateway_SeriesQueryingShouldEnforceMaxSeriesPerQueryLimit (t * testing.T ) {
977
+ const seriesQueried = 10
978
+
979
+ tests := map [string ]struct {
980
+ limit int
981
+ expectedErr error
982
+ }{
983
+ "no limit enforced if zero" : {
984
+ limit : 0 ,
985
+ expectedErr : nil ,
986
+ },
987
+ "should return NO error if the actual number of queried series is <= limit" : {
988
+ limit : seriesQueried ,
989
+ expectedErr : nil ,
990
+ },
991
+ "should return error if the actual number of queried series is > limit" : {
992
+ limit : seriesQueried - 1 ,
993
+ expectedErr : status .Error (http .StatusUnprocessableEntity , fmt .Sprintf ("exceeded series limit: rpc error: code = Code(422) desc = limit %d violated (got %d)" , seriesQueried - 1 , seriesQueried )),
994
+ },
995
+ }
996
+
997
+ ctx := context .Background ()
998
+ logger := log .NewNopLogger ()
999
+ userID := "user-1"
1000
+
1001
+ storageDir , err := ioutil .TempDir (os .TempDir (), "" )
1002
+ require .NoError (t , err )
1003
+ defer os .RemoveAll (storageDir ) //nolint:errcheck
1004
+
1005
+ // Generate 1 TSDB block with chunksQueried series. Since each mocked series contains only 1 sample,
1006
+ // it will also only have 1 chunk.
1007
+ now := time .Now ()
1008
+ minT := now .Add (- 1 * time .Hour ).Unix () * 1000
1009
+ maxT := now .Unix () * 1000
1010
+ mockTSDB (t , path .Join (storageDir , userID ), seriesQueried , 0 , minT , maxT )
1011
+
1012
+ bucketClient , err := filesystem .NewBucketClient (filesystem.Config {Directory : storageDir })
1013
+ require .NoError (t , err )
1014
+
1015
+ // Prepare the request to query back all series (1 chunk per series in this test).
1016
+ req := & storepb.SeriesRequest {
1017
+ MinTime : minT ,
1018
+ MaxTime : maxT ,
1019
+ Matchers : []storepb.LabelMatcher {
1020
+ {Type : storepb .LabelMatcher_RE , Name : "__name__" , Value : ".*" },
1021
+ },
1022
+ }
1023
+
1024
+ for testName , testData := range tests {
1025
+ t .Run (testName , func (t * testing.T ) {
1026
+ // Customise the limits.
1027
+ limits := defaultLimitsConfig ()
1028
+ limits .MaxFetchedSeriesPerQuery = testData .limit
1029
+ overrides , err := validation .NewOverrides (limits , nil )
1030
+ require .NoError (t , err )
1031
+
1032
+ // Create a store-gateway used to query back the series from the blocks.
1033
+ gatewayCfg := mockGatewayConfig ()
1034
+ gatewayCfg .ShardingEnabled = false
1035
+ storageCfg := mockStorageConfig (t )
1036
+
1037
+ g , err := newStoreGateway (gatewayCfg , storageCfg , bucketClient , nil , overrides , mockLoggingLevel (), logger , nil )
1038
+ require .NoError (t , err )
1039
+ require .NoError (t , services .StartAndAwaitRunning (ctx , g ))
1040
+ defer services .StopAndAwaitTerminated (ctx , g ) //nolint:errcheck
1041
+
1042
+ // Query back all the series (1 chunk per series in this test).
1043
+ srv := newBucketStoreSeriesServer (setUserIDToGRPCContext (ctx , userID ))
1044
+ err = g .Series (req , srv )
1045
+
1046
+ if testData .expectedErr != nil {
1047
+ fmt .Println ("Error: " , err .Error ())
1048
+ require .Error (t , err )
1049
+ assert .IsType (t , testData .expectedErr , err )
1050
+ s1 , ok := status .FromError (errors .Cause (err ))
1051
+ assert .True (t , ok )
1052
+ s2 , ok := status .FromError (errors .Cause (testData .expectedErr ))
1053
+ assert .True (t , ok )
1054
+ assert .True (t , strings .Contains (s1 .Message (), s2 .Message ()))
1055
+ assert .Equal (t , s1 .Code (), s2 .Code ())
1056
+ } else {
1057
+ require .NoError (t , err )
1058
+ assert .Empty (t , srv .Warnings )
1059
+ assert .Len (t , srv .SeriesSet , seriesQueried )
1060
+ }
1061
+ })
1062
+ }
1063
+ }
1064
+
976
1065
func mockGatewayConfig () Config {
977
1066
cfg := Config {}
978
1067
flagext .DefaultValues (& cfg )
0 commit comments