go.temporal.io/server@v1.23.0/common/persistence/client/quotas_test.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package client 26 27 import ( 28 "reflect" 29 "testing" 30 "time" 31 32 "github.com/stretchr/testify/require" 33 "github.com/stretchr/testify/suite" 34 "go.temporal.io/server/common/headers" 35 "go.temporal.io/server/common/quotas" 36 "go.temporal.io/server/common/testing/temporalapi" 37 "golang.org/x/exp/slices" 38 39 "go.temporal.io/api/workflowservice/v1" 40 ) 41 42 type ( 43 quotasSuite struct { 44 suite.Suite 45 *require.Assertions 46 } 47 ) 48 49 func TestQuotasSuite(t *testing.T) { 50 s := new(quotasSuite) 51 suite.Run(t, s) 52 } 53 54 func (s *quotasSuite) SetupSuite() { 55 } 56 57 func (s *quotasSuite) TearDownSuite() { 58 } 59 60 func (s *quotasSuite) SetupTest() { 61 s.Assertions = require.New(s.T()) 62 } 63 64 func (s *quotasSuite) TearDownTest() { 65 } 66 67 func (s *quotasSuite) TestCallerTypeDefaultPriorityMapping() { 68 for _, priority := range CallerTypeDefaultPriority { 69 index := slices.Index(RequestPrioritiesOrdered, priority) 70 s.NotEqual(-1, index) 71 } 72 } 73 74 func (s *quotasSuite) TestAPITypeCallOriginPriorityOverrideMapping() { 75 for _, priority := range APITypeCallOriginPriorityOverride { 76 index := slices.Index(RequestPrioritiesOrdered, priority) 77 s.NotEqual(-1, index) 78 } 79 } 80 81 func (s *quotasSuite) TestBackgroundTypeAPIPriorityOverrideMapping() { 82 for _, priority := range BackgroundTypeAPIPriorityOverride { 83 index := slices.Index(RequestPrioritiesOrdered, priority) 84 s.NotEqual(-1, index) 85 } 86 } 87 88 func (s *quotasSuite) TestRequestPrioritiesOrdered() { 89 for idx := range RequestPrioritiesOrdered[1:] { 90 s.True(RequestPrioritiesOrdered[idx] < RequestPrioritiesOrdered[idx+1]) 91 } 92 } 93 94 func (s *quotasSuite) TestCallOriginDefined() { 95 var service workflowservice.WorkflowServiceServer 96 definedAPIs := make(map[string]struct{}) 97 temporalapi.WalkExportedMethods(&service, func(m reflect.Method) { 98 definedAPIs[m.Name] = struct{}{} 99 }) 100 101 for api := range APITypeCallOriginPriorityOverride { 102 _, ok := definedAPIs[api] 103 s.True(ok) 104 } 105 } 106 107 func (s *quotasSuite) TestPriorityNamespaceRateLimiter_DoesLimit() { 108 namespaceMaxRPS := func(namespace string) int { return 1 } 109 hostMaxRPS := func() int { return 1 } 110 operatorRPSRatioFn := func() float64 { return 0.2 } 111 112 limiter := newPriorityNamespaceRateLimiter(namespaceMaxRPS, hostMaxRPS, RequestPriorityFn, operatorRPSRatioFn) 113 114 request := quotas.NewRequest( 115 "test-api", 116 1, 117 "test-namespace", 118 "api", 119 -1, 120 "frontend", 121 ) 122 123 requestTime := time.Now() 124 wasLimited := false 125 126 for i := 0; i < 2; i++ { 127 if !limiter.Allow(requestTime, request) { 128 wasLimited = true 129 } 130 } 131 132 s.True(wasLimited) 133 } 134 135 func (s *quotasSuite) TestPerShardNamespaceRateLimiter_DoesLimit() { 136 perShardNamespaceMaxRPS := func(namespace string) int { return 1 } 137 hostMaxRPS := func() int { return 1 } 138 operatorRPSRatioFn := func() float64 { return 0.2 } 139 140 limiter := newPerShardPerNamespacePriorityRateLimiter(perShardNamespaceMaxRPS, hostMaxRPS, RequestPriorityFn, operatorRPSRatioFn) 141 142 request := quotas.NewRequest( 143 "test-api", 144 1, 145 "test-namespace", 146 "api", 147 1, 148 "frontend", 149 ) 150 151 requestTime := time.Now() 152 wasLimited := false 153 154 for i := 0; i < 2; i++ { 155 if !limiter.Allow(requestTime, request) { 156 wasLimited = true 157 } 158 } 159 160 s.True(wasLimited) 161 } 162 163 func (s *quotasSuite) TestOperatorPrioritized() { 164 rateFn := func() float64 { return 5 } 165 operatorRPSRatioFn := func() float64 { return 0.2 } 166 limiter := newPriorityRateLimiter(rateFn, RequestPriorityFn, operatorRPSRatioFn) 167 168 operatorRequest := quotas.NewRequest( 169 "DescribeWorkflowExecution", 170 1, 171 "test-namespace", 172 headers.CallerTypeOperator, 173 -1, 174 "DescribeWorkflowExecution") 175 176 apiRequest := quotas.NewRequest( 177 "DescribeWorkflowExecution", 178 1, 179 "test-namespace", 180 headers.CallerTypeAPI, 181 -1, 182 "DescribeWorkflowExecution") 183 184 requestTime := time.Now() 185 wasLimited := false 186 187 for i := 0; i < 6; i++ { 188 if !limiter.Allow(requestTime, apiRequest) { 189 wasLimited = true 190 s.True(limiter.Allow(requestTime, operatorRequest)) 191 } 192 } 193 s.True(wasLimited) 194 }