@@ -705,14 +705,14 @@ func TestServiceGlobalQuotaMode(test *testing.T) {
705705 })
706706 response , err := service .ShouldRateLimit (context .Background (), request )
707707
708- // OK overall code even if limit response was OVER_LIMIT, because global quota mode is enabled
708+ // OVER_LIMIT overall code since all quota limits were OVER_LIMIT
709709 common .AssertProtoEqual (
710710 t .assert ,
711711 & pb.RateLimitResponse {
712- OverallCode : pb .RateLimitResponse_OK ,
712+ OverallCode : pb .RateLimitResponse_OVER_LIMIT ,
713713 Statuses : []* pb.RateLimitResponse_DescriptorStatus {
714- {Code : pb .RateLimitResponse_OK , CurrentLimit : limits [0 ].Limit , LimitRemaining : 0 },
715- {Code : pb .RateLimitResponse_OK , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
714+ {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [0 ].Limit , LimitRemaining : 0 },
715+ {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
716716 },
717717 },
718718 response )
@@ -809,11 +809,11 @@ func TestServicePerDescriptorQuotaMode(test *testing.T) {
809809 t .cache .EXPECT ().DoLimit (context .Background (), request , limits ).Return (
810810 []* pb.RateLimitResponse_DescriptorStatus {
811811 {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [0 ].Limit , LimitRemaining : 0 },
812- {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
812+ {Code : pb .RateLimitResponse_OK , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
813813 })
814814 response , err := service .ShouldRateLimit (context .Background (), request )
815815
816- // Regular limit should cause OVER_LIMIT overall, but quota mode limit should be converted to OK
816+ // Regular limit should cause OVER_LIMIT overall, even though quota mode is under the limit
817817 common .AssertProtoEqual (
818818 t .assert ,
819819 & pb.RateLimitResponse {
@@ -827,7 +827,111 @@ func TestServicePerDescriptorQuotaMode(test *testing.T) {
827827 t .assert .Nil (err )
828828}
829829
830- func TestServiceQuotaModeOnly (test * testing.T ) {
830+ func TestServiceMixedPerDescriptorModes (test * testing.T ) {
831+ t := commonSetup (test )
832+ defer t .controller .Finish ()
833+
834+ // No Global Quota mode
835+ service := t .setupBasicService ()
836+
837+ request := common .NewRateLimitRequest (
838+ "quota-domain" , [][][2 ]string {{{"regular" , "limit" }}, {{"quota" , "limit" }}}, 1 )
839+
840+ // Create limits with one having quota mode enabled per-descriptor
841+ // In this configuration the limits will be evaluated as rate limits.
842+ limits := []* config.RateLimit {
843+ // Regular limit
844+ {
845+ FullKey : "regular_limit" ,
846+ Limit : & pb.RateLimitResponse_RateLimit {RequestsPerUnit : 5 , Unit : pb .RateLimitResponse_RateLimit_MINUTE },
847+ QuotaMode : false ,
848+ ShadowMode : false ,
849+ },
850+ // Quota mode limit
851+ {
852+ FullKey : "quota_limit" ,
853+ Limit : & pb.RateLimitResponse_RateLimit {RequestsPerUnit : 3 , Unit : pb .RateLimitResponse_RateLimit_MINUTE },
854+ QuotaMode : true ,
855+ ShadowMode : false ,
856+ },
857+ }
858+
859+ t .config .EXPECT ().GetLimit (context .Background (), "quota-domain" , request .Descriptors [0 ]).Return (limits [0 ])
860+ t .config .EXPECT ().GetLimit (context .Background (), "quota-domain" , request .Descriptors [1 ]).Return (limits [1 ])
861+ t .cache .EXPECT ().DoLimit (context .Background (), request , limits ).Return (
862+ []* pb.RateLimitResponse_DescriptorStatus {
863+ {Code : pb .RateLimitResponse_OK , CurrentLimit : limits [0 ].Limit , LimitRemaining : 0 },
864+ {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
865+ })
866+ response , err := service .ShouldRateLimit (context .Background (), request )
867+
868+ // Overall result is OVER_LIMIT, since all quota limits were exceeded
869+ common .AssertProtoEqual (
870+ t .assert ,
871+ & pb.RateLimitResponse {
872+ OverallCode : pb .RateLimitResponse_OVER_LIMIT ,
873+ Statuses : []* pb.RateLimitResponse_DescriptorStatus {
874+ {Code : pb .RateLimitResponse_OK , CurrentLimit : limits [0 ].Limit , LimitRemaining : 0 },
875+ {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
876+ },
877+ },
878+ response )
879+ t .assert .Nil (err )
880+ }
881+
882+ func TestServiceMixedPerDescriptorModesUnderLimit (test * testing.T ) {
883+ t := commonSetup (test )
884+ defer t .controller .Finish ()
885+
886+ // No Global Quota mode
887+ service := t .setupBasicService ()
888+
889+ request := common .NewRateLimitRequest (
890+ "quota-domain" , [][][2 ]string {{{"regular" , "limit" }}, {{"quota" , "limit" }}}, 1 )
891+
892+ // Create limits with one having quota mode enabled per-descriptor
893+ // In this configuration the limits will be evaluated as rate limits.
894+ limits := []* config.RateLimit {
895+ // Regular limit
896+ {
897+ FullKey : "regular_limit" ,
898+ Limit : & pb.RateLimitResponse_RateLimit {RequestsPerUnit : 5 , Unit : pb .RateLimitResponse_RateLimit_MINUTE },
899+ QuotaMode : false ,
900+ ShadowMode : false ,
901+ },
902+ // Quota mode limit
903+ {
904+ FullKey : "quota_limit" ,
905+ Limit : & pb.RateLimitResponse_RateLimit {RequestsPerUnit : 3 , Unit : pb .RateLimitResponse_RateLimit_MINUTE },
906+ QuotaMode : true ,
907+ ShadowMode : false ,
908+ },
909+ }
910+
911+ t .config .EXPECT ().GetLimit (context .Background (), "quota-domain" , request .Descriptors [0 ]).Return (limits [0 ])
912+ t .config .EXPECT ().GetLimit (context .Background (), "quota-domain" , request .Descriptors [1 ]).Return (limits [1 ])
913+ t .cache .EXPECT ().DoLimit (context .Background (), request , limits ).Return (
914+ []* pb.RateLimitResponse_DescriptorStatus {
915+ {Code : pb .RateLimitResponse_OK , CurrentLimit : limits [0 ].Limit , LimitRemaining : 0 },
916+ {Code : pb .RateLimitResponse_OK , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
917+ })
918+ response , err := service .ShouldRateLimit (context .Background (), request )
919+
920+ // Overall result is OVER_LIMIT, since all quota limits were exceeded
921+ common .AssertProtoEqual (
922+ t .assert ,
923+ & pb.RateLimitResponse {
924+ OverallCode : pb .RateLimitResponse_OK ,
925+ Statuses : []* pb.RateLimitResponse_DescriptorStatus {
926+ {Code : pb .RateLimitResponse_OK , CurrentLimit : limits [0 ].Limit , LimitRemaining : 0 },
927+ {Code : pb .RateLimitResponse_OK , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
928+ },
929+ },
930+ response )
931+ t .assert .Nil (err )
932+ }
933+
934+ func TestServiceQuotaModeOnlyAllOverTheLimit (test * testing.T ) {
831935 t := commonSetup (test )
832936 defer t .controller .Finish ()
833937
@@ -861,13 +965,61 @@ func TestServiceQuotaModeOnly(test *testing.T) {
861965 })
862966 response , err := service .ShouldRateLimit (context .Background (), request )
863967
864- // All quota mode limits should result in OK overall code
968+ // Since quota limits were exceeded overall result in OVER_LIMIT
969+ common .AssertProtoEqual (
970+ t .assert ,
971+ & pb.RateLimitResponse {
972+ OverallCode : pb .RateLimitResponse_OVER_LIMIT ,
973+ Statuses : []* pb.RateLimitResponse_DescriptorStatus {
974+ {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [0 ].Limit , LimitRemaining : 0 },
975+ {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
976+ },
977+ },
978+ response )
979+ t .assert .Nil (err )
980+ }
981+
982+ func TestServiceQuotaModeOnlySomeOverTheLimit (test * testing.T ) {
983+ t := commonSetup (test )
984+ defer t .controller .Finish ()
985+
986+ service := t .setupBasicService ()
987+
988+ request := common .NewRateLimitRequest (
989+ "quota-domain" , [][][2 ]string {{{"quota1" , "limit" }}, {{"quota2" , "limit" }}}, 1 )
990+
991+ // Both limits are in quota mode
992+ limits := []* config.RateLimit {
993+ {
994+ FullKey : "quota_limit_1" ,
995+ Limit : & pb.RateLimitResponse_RateLimit {RequestsPerUnit : 5 , Unit : pb .RateLimitResponse_RateLimit_MINUTE },
996+ QuotaMode : true ,
997+ ShadowMode : false ,
998+ },
999+ {
1000+ FullKey : "quota_limit_2" ,
1001+ Limit : & pb.RateLimitResponse_RateLimit {RequestsPerUnit : 3 , Unit : pb .RateLimitResponse_RateLimit_MINUTE },
1002+ QuotaMode : true ,
1003+ ShadowMode : false ,
1004+ },
1005+ }
1006+
1007+ t .config .EXPECT ().GetLimit (context .Background (), "quota-domain" , request .Descriptors [0 ]).Return (limits [0 ])
1008+ t .config .EXPECT ().GetLimit (context .Background (), "quota-domain" , request .Descriptors [1 ]).Return (limits [1 ])
1009+ t .cache .EXPECT ().DoLimit (context .Background (), request , limits ).Return (
1010+ []* pb.RateLimitResponse_DescriptorStatus {
1011+ {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [0 ].Limit , LimitRemaining : 0 },
1012+ {Code : pb .RateLimitResponse_OK , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
1013+ })
1014+ response , err := service .ShouldRateLimit (context .Background (), request )
1015+
1016+ // Since only some quota limits were exceeded overall result is OK
8651017 common .AssertProtoEqual (
8661018 t .assert ,
8671019 & pb.RateLimitResponse {
8681020 OverallCode : pb .RateLimitResponse_OK ,
8691021 Statuses : []* pb.RateLimitResponse_DescriptorStatus {
870- {Code : pb .RateLimitResponse_OK , CurrentLimit : limits [0 ].Limit , LimitRemaining : 0 },
1022+ {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [0 ].Limit , LimitRemaining : 0 },
8711023 {Code : pb .RateLimitResponse_OK , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
8721024 },
8731025 },
@@ -895,6 +1047,71 @@ func TestServiceQuotaModeWithShadowMode(test *testing.T) {
8951047 t .configUpdateEventChan <- t .configUpdateEvent
8961048 barrier .wait ()
8971049
1050+ request := common .NewRateLimitRequest (
1051+ "quota-domain" , [][][2 ]string {{{"regular" , "limit" }}, {{"quota" , "limit" }}}, 1 )
1052+
1053+ // Mix of regular and quota mode limits with global shadow mode
1054+ limits := []* config.RateLimit {
1055+ {
1056+ FullKey : "regular_limit" ,
1057+ Limit : & pb.RateLimitResponse_RateLimit {RequestsPerUnit : 5 , Unit : pb .RateLimitResponse_RateLimit_MINUTE },
1058+ QuotaMode : true ,
1059+ ShadowMode : false ,
1060+ },
1061+ {
1062+ FullKey : "quota_limit" ,
1063+ Limit : & pb.RateLimitResponse_RateLimit {RequestsPerUnit : 3 , Unit : pb .RateLimitResponse_RateLimit_MINUTE },
1064+ QuotaMode : true ,
1065+ ShadowMode : false ,
1066+ },
1067+ }
1068+
1069+ t .config .EXPECT ().GetLimit (context .Background (), "quota-domain" , request .Descriptors [0 ]).Return (limits [0 ])
1070+ t .config .EXPECT ().GetLimit (context .Background (), "quota-domain" , request .Descriptors [1 ]).Return (limits [1 ])
1071+ t .cache .EXPECT ().DoLimit (context .Background (), request , limits ).Return (
1072+ []* pb.RateLimitResponse_DescriptorStatus {
1073+ {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [0 ].Limit , LimitRemaining : 0 },
1074+ {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
1075+ })
1076+ response , err := service .ShouldRateLimit (context .Background (), request )
1077+
1078+ // Global shadow mode should override everything and result in OK
1079+ common .AssertProtoEqual (
1080+ t .assert ,
1081+ & pb.RateLimitResponse {
1082+ OverallCode : pb .RateLimitResponse_OK ,
1083+ Statuses : []* pb.RateLimitResponse_DescriptorStatus {
1084+ {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [0 ].Limit , LimitRemaining : 0 },
1085+ {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
1086+ },
1087+ },
1088+ response )
1089+ t .assert .Nil (err )
1090+
1091+ // Verify global shadow mode counter is incremented
1092+ t .assert .EqualValues (1 , t .statStore .NewCounter ("global_shadow_mode" ).Value ())
1093+ }
1094+
1095+ func TestServiceMixedModeWithShadowMode (test * testing.T ) {
1096+ os .Setenv ("SHADOW_MODE" , "true" )
1097+ defer func () {
1098+ os .Unsetenv ("SHADOW_MODE" )
1099+ }()
1100+
1101+ t := commonSetup (test )
1102+ defer t .controller .Finish ()
1103+
1104+ service := t .setupBasicService ()
1105+
1106+ // Force a config reload to pick up environment variables.
1107+ barrier := newBarrier ()
1108+ t .configUpdateEvent .EXPECT ().GetConfig ().DoAndReturn (func () (config.RateLimitConfig , any ) {
1109+ barrier .signal ()
1110+ return t .config , nil
1111+ })
1112+ t .configUpdateEventChan <- t .configUpdateEvent
1113+ barrier .wait ()
1114+
8981115 request := common .NewRateLimitRequest (
8991116 "quota-domain" , [][][2 ]string {{{"regular" , "limit" }}, {{"quota" , "limit" }}}, 1 )
9001117
@@ -930,7 +1147,7 @@ func TestServiceQuotaModeWithShadowMode(test *testing.T) {
9301147 OverallCode : pb .RateLimitResponse_OK ,
9311148 Statuses : []* pb.RateLimitResponse_DescriptorStatus {
9321149 {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [0 ].Limit , LimitRemaining : 0 },
933- {Code : pb .RateLimitResponse_OK , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
1150+ {Code : pb .RateLimitResponse_OVER_LIMIT , CurrentLimit : limits [1 ].Limit , LimitRemaining : 0 },
9341151 },
9351152 },
9361153 response )
0 commit comments