@@ -24,8 +24,12 @@ import (
24
24
"testing"
25
25
"time"
26
26
27
+ "github.com/pingcap/failpoint"
28
+ "github.com/pingcap/kvproto/pkg/meta_storagepb"
27
29
rmpb "github.com/pingcap/kvproto/pkg/resource_manager"
30
+ "github.com/stretchr/testify/mock"
28
31
"github.com/stretchr/testify/require"
32
+ pd "github.com/tikv/pd/client"
29
33
"github.com/tikv/pd/client/errs"
30
34
)
31
35
@@ -132,3 +136,138 @@ func TestResourceGroupThrottledError(t *testing.T) {
132
136
re .Error (err )
133
137
re .True (errs .ErrClientResourceGroupThrottled .Equal (err ))
134
138
}
139
+
140
+ // MockResourceGroupProvider is a mock implementation of the ResourceGroupProvider interface.
141
+ type MockResourceGroupProvider struct {
142
+ mock.Mock
143
+ }
144
+
145
+ func (m * MockResourceGroupProvider ) GetResourceGroup (ctx context.Context , resourceGroupName string ) (* rmpb.ResourceGroup , error ) {
146
+ args := m .Called (ctx , resourceGroupName )
147
+ return args .Get (0 ).(* rmpb.ResourceGroup ), args .Error (1 )
148
+ }
149
+
150
+ func (m * MockResourceGroupProvider ) ListResourceGroups (ctx context.Context ) ([]* rmpb.ResourceGroup , error ) {
151
+ args := m .Called (ctx )
152
+ return args .Get (0 ).([]* rmpb.ResourceGroup ), args .Error (1 )
153
+ }
154
+
155
+ func (m * MockResourceGroupProvider ) AddResourceGroup (ctx context.Context , metaGroup * rmpb.ResourceGroup ) (string , error ) {
156
+ args := m .Called (ctx , metaGroup )
157
+ return args .String (0 ), args .Error (1 )
158
+ }
159
+
160
+ func (m * MockResourceGroupProvider ) ModifyResourceGroup (ctx context.Context , metaGroup * rmpb.ResourceGroup ) (string , error ) {
161
+ args := m .Called (ctx , metaGroup )
162
+ return args .String (0 ), args .Error (1 )
163
+ }
164
+
165
+ func (m * MockResourceGroupProvider ) DeleteResourceGroup (ctx context.Context , resourceGroupName string ) (string , error ) {
166
+ args := m .Called (ctx , resourceGroupName )
167
+ return args .String (0 ), args .Error (1 )
168
+ }
169
+
170
+ func (m * MockResourceGroupProvider ) AcquireTokenBuckets (ctx context.Context , request * rmpb.TokenBucketsRequest ) ([]* rmpb.TokenBucketResponse , error ) {
171
+ args := m .Called (ctx , request )
172
+ return args .Get (0 ).([]* rmpb.TokenBucketResponse ), args .Error (1 )
173
+ }
174
+
175
+ func (m * MockResourceGroupProvider ) LoadResourceGroups (ctx context.Context ) ([]* rmpb.ResourceGroup , int64 , error ) {
176
+ args := m .Called (ctx )
177
+ return args .Get (0 ).([]* rmpb.ResourceGroup ), args .Get (1 ).(int64 ), args .Error (2 )
178
+ }
179
+
180
+ func (m * MockResourceGroupProvider ) Watch (ctx context.Context , key []byte , opts ... pd.OpOption ) (chan []* meta_storagepb.Event , error ) {
181
+ args := m .Called (ctx , key , opts )
182
+ return args .Get (0 ).(chan []* meta_storagepb.Event ), args .Error (1 )
183
+ }
184
+
185
+ func (m * MockResourceGroupProvider ) Get (ctx context.Context , key []byte , opts ... pd.OpOption ) (* meta_storagepb.GetResponse , error ) {
186
+ args := m .Called (ctx , key , opts )
187
+ return args .Get (0 ).(* meta_storagepb.GetResponse ), args .Error (1 )
188
+ }
189
+
190
+ func TestControllerWithTwoGroupRequestConcurrency (t * testing.T ) {
191
+ re := require .New (t )
192
+ ctx , cancel := context .WithCancel (context .Background ())
193
+ defer cancel ()
194
+ mockProvider := new (MockResourceGroupProvider )
195
+
196
+ mockProvider .On ("Get" , mock .Anything , mock .Anything , mock .Anything ).Return (& meta_storagepb.GetResponse {}, nil )
197
+ // LoadResourceGroups
198
+ mockProvider .On ("LoadResourceGroups" , mock .Anything ).Return ([]* rmpb.ResourceGroup {}, int64 (0 ), nil )
199
+ // Watch
200
+ mockProvider .On ("Watch" , mock .Anything , mock .Anything , mock .Anything ).Return (make (chan []* meta_storagepb.Event ), nil )
201
+
202
+ re .NoError (failpoint .Enable ("github.com/tikv/pd/client/resource_group/controller/triggerPeriodicReport" , fmt .Sprintf ("return(\" %s\" )" , "default" )))
203
+ defer failpoint .Disable ("github.com/tikv/pd/client/resource_group/controller/triggerPeriodicReport" )
204
+ re .NoError (failpoint .Enable ("github.com/tikv/pd/client/resource_group/controller/triggerLowRUReport" , fmt .Sprintf ("return(\" %s\" )" , "test-group" )))
205
+ defer failpoint .Disable ("github.com/tikv/pd/client/resource_group/controller/triggerLowRUReport" )
206
+
207
+ controller , _ := NewResourceGroupController (ctx , 1 , mockProvider , nil )
208
+ controller .Start (ctx )
209
+
210
+ defaultResourceGroup := & rmpb.ResourceGroup {Name : "default" , Mode : rmpb .GroupMode_RUMode , RUSettings : & rmpb.GroupRequestUnitSettings {RU : & rmpb.TokenBucket {Settings : & rmpb.TokenLimitSettings {FillRate : 1000000 }}}}
211
+ testResourceGroup := & rmpb.ResourceGroup {Name : "test-group" , Mode : rmpb .GroupMode_RUMode , RUSettings : & rmpb.GroupRequestUnitSettings {RU : & rmpb.TokenBucket {Settings : & rmpb.TokenLimitSettings {FillRate : 1000000 }}}}
212
+ mockProvider .On ("GetResourceGroup" , mock .Anything , "default" , mock .Anything ).Return (defaultResourceGroup , nil )
213
+ mockProvider .On ("GetResourceGroup" , mock .Anything , "test-group" , mock .Anything ).Return (testResourceGroup , nil )
214
+
215
+ c1 , err := controller .tryGetResourceGroup (ctx , "default" )
216
+ re .NoError (err )
217
+ re .Equal (defaultResourceGroup , c1 .meta )
218
+
219
+ c2 , err := controller .tryGetResourceGroup (ctx , "test-group" )
220
+ re .NoError (err )
221
+ re .Equal (testResourceGroup , c2 .meta )
222
+
223
+ var expectResp []* rmpb.TokenBucketResponse
224
+ recTestGroupAcquireTokenRequest := make (chan bool )
225
+ mockProvider .On ("AcquireTokenBuckets" , mock .Anything , mock .Anything ).Run (func (args mock.Arguments ) {
226
+ request := args .Get (1 ).(* rmpb.TokenBucketsRequest )
227
+ var responses []* rmpb.TokenBucketResponse
228
+ for _ , req := range request .Requests {
229
+ if req .ResourceGroupName == "default" {
230
+ // no response the default group request, that's mean `len(c.run.currentRequests) != 0` always.
231
+ time .Sleep (100 * time .Second )
232
+ responses = append (responses , & rmpb.TokenBucketResponse {
233
+ ResourceGroupName : "default" ,
234
+ GrantedRUTokens : []* rmpb.GrantedRUTokenBucket {
235
+ {
236
+ GrantedTokens : & rmpb.TokenBucket {
237
+ Tokens : 100000 ,
238
+ },
239
+ },
240
+ },
241
+ })
242
+ } else {
243
+ responses = append (responses , & rmpb.TokenBucketResponse {
244
+ ResourceGroupName : req .ResourceGroupName ,
245
+ GrantedRUTokens : []* rmpb.GrantedRUTokenBucket {
246
+ {
247
+ GrantedTokens : & rmpb.TokenBucket {
248
+ Tokens : 100000 ,
249
+ },
250
+ },
251
+ },
252
+ })
253
+ }
254
+ }
255
+ // receive test-group request
256
+ if len (request .Requests ) == 1 && request .Requests [0 ].ResourceGroupName == "test-group" {
257
+ recTestGroupAcquireTokenRequest <- true
258
+ }
259
+ expectResp = responses
260
+ }).Return (expectResp , nil )
261
+ // wait default group request token by PeriodicReport.
262
+ time .Sleep (2 * time .Second )
263
+ counter := c2 .run .requestUnitTokens [0 ]
264
+ counter .limiter .mu .Lock ()
265
+ counter .limiter .notify ()
266
+ counter .limiter .mu .Unlock ()
267
+ select {
268
+ case res := <- recTestGroupAcquireTokenRequest :
269
+ re .True (res )
270
+ case <- time .After (5 * time .Second ):
271
+ re .Fail ("timeout" )
272
+ }
273
+ }
0 commit comments