1
1
// Copyright (c) The go-grpc-middleware Authors.
2
2
// Licensed under the Apache License 2.0.
3
3
4
- package retry_test
4
+ package retry
5
5
6
6
import (
7
7
"context"
@@ -18,7 +18,6 @@ import (
18
18
"google.golang.org/grpc/status"
19
19
20
20
middleware "github.com/grpc-ecosystem/go-grpc-middleware/v2"
21
- "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/retry"
22
21
"github.com/grpc-ecosystem/go-grpc-middleware/v2/testing/testpb"
23
22
)
24
23
@@ -94,15 +93,15 @@ func TestRetrySuite(t *testing.T) {
94
93
service := & failingService {
95
94
TestServiceServer : & testpb.TestPingService {T : t },
96
95
}
97
- unaryInterceptor := retry . UnaryClientInterceptor (
98
- retry . WithCodes (retriableErrors ... ),
99
- retry . WithMax (3 ),
100
- retry . WithBackoff (retry . BackoffLinear (retryTimeout )),
96
+ unaryInterceptor := UnaryClientInterceptor (
97
+ WithCodes (retriableErrors ... ),
98
+ WithMax (3 ),
99
+ WithBackoff (BackoffLinear (retryTimeout )),
101
100
)
102
- streamInterceptor := retry . StreamClientInterceptor (
103
- retry . WithCodes (retriableErrors ... ),
104
- retry . WithMax (3 ),
105
- retry . WithBackoff (retry . BackoffLinear (retryTimeout )),
101
+ streamInterceptor := StreamClientInterceptor (
102
+ WithCodes (retriableErrors ... ),
103
+ WithMax (3 ),
104
+ WithBackoff (BackoffLinear (retryTimeout )),
106
105
)
107
106
s := & RetrySuite {
108
107
srv : service ,
@@ -148,10 +147,10 @@ func (s *RetrySuite) TestCallOptionsDontPanicWithoutInterceptor() {
148
147
s .srv .resetFailingConfiguration (100 , codes .DataLoss , noSleep ) // doesn't matter all requests should fail
149
148
nonMiddlewareClient := s .NewClient ()
150
149
_ , err := nonMiddlewareClient .Ping (s .SimpleCtx (), testpb .GoodPing ,
151
- retry . WithMax (5 ),
152
- retry . WithBackoff (retry . BackoffLinear (1 * time .Millisecond )),
153
- retry . WithCodes (codes .DataLoss ),
154
- retry . WithPerRetryTimeout (1 * time .Millisecond ),
150
+ WithMax (5 ),
151
+ WithBackoff (BackoffLinear (1 * time .Millisecond )),
152
+ WithCodes (codes .DataLoss ),
153
+ WithPerRetryTimeout (1 * time .Millisecond ),
155
154
)
156
155
require .Error (s .T (), err )
157
156
}
@@ -175,7 +174,7 @@ func (s *RetrySuite) TestUnary_SucceedsOnRetriableError() {
175
174
176
175
func (s * RetrySuite ) TestUnary_OverrideFromDialOpts () {
177
176
s .srv .resetFailingConfiguration (5 , codes .ResourceExhausted , noSleep ) // default is 3 and retriable_errors
178
- out , err := s .Client .Ping (s .SimpleCtx (), testpb .GoodPing , retry . WithCodes (codes .ResourceExhausted ), retry . WithMax (5 ))
177
+ out , err := s .Client .Ping (s .SimpleCtx (), testpb .GoodPing , WithCodes (codes .ResourceExhausted ), WithMax (5 ))
179
178
require .NoError (s .T (), err , "the fifth invocation should succeed" )
180
179
require .NotNil (s .T (), out , "Pong must be not nil" )
181
180
require .EqualValues (s .T (), 5 , s .srv .requestCount (), "five requests should have been made" )
@@ -188,8 +187,8 @@ func (s *RetrySuite) TestUnary_PerCallDeadline_Succeeds() {
188
187
// a retry call with a 5 millisecond deadline. The 5th one doesn't sleep and succeeds.
189
188
deadlinePerCall := 5 * time .Millisecond
190
189
s .srv .resetFailingConfiguration (5 , codes .NotFound , 2 * deadlinePerCall )
191
- out , err := s .Client .Ping (s .SimpleCtx (), testpb .GoodPing , retry . WithPerRetryTimeout (deadlinePerCall ),
192
- retry . WithMax (5 ))
190
+ out , err := s .Client .Ping (s .SimpleCtx (), testpb .GoodPing , WithPerRetryTimeout (deadlinePerCall ),
191
+ WithMax (5 ))
193
192
require .NoError (s .T (), err , "the fifth invocation should succeed" )
194
193
require .NotNil (s .T (), out , "Pong must be not nil" )
195
194
require .EqualValues (s .T (), 5 , s .srv .requestCount (), "five requests should have been made" )
@@ -209,8 +208,8 @@ func (s *RetrySuite) TestUnary_PerCallDeadline_FailsOnParent() {
209
208
s .srv .resetFailingConfiguration (5 , codes .NotFound , 2 * deadlinePerCall )
210
209
ctx , cancel := context .WithTimeout (context .TODO (), parentDeadline )
211
210
defer cancel ()
212
- _ , err := s .Client .Ping (ctx , testpb .GoodPing , retry . WithPerRetryTimeout (deadlinePerCall ),
213
- retry . WithMax (5 ))
211
+ _ , err := s .Client .Ping (ctx , testpb .GoodPing , WithPerRetryTimeout (deadlinePerCall ),
212
+ WithMax (5 ))
214
213
require .Error (s .T (), err , "the retries must fail due to context deadline exceeded" )
215
214
require .Equal (s .T (), codes .DeadlineExceeded , status .Code (err ), "failre code must be a gRPC error of Deadline class" )
216
215
}
@@ -220,7 +219,7 @@ func (s *RetrySuite) TestUnary_OnRetryCallbackCalled() {
220
219
221
220
s .srv .resetFailingConfiguration (3 , codes .Unavailable , noSleep ) // see retriable_errors
222
221
out , err := s .Client .Ping (s .SimpleCtx (), testpb .GoodPing ,
223
- retry . WithOnRetryCallback (func (ctx context.Context , attempt uint , err error ) {
222
+ WithOnRetryCallback (func (ctx context.Context , attempt uint , err error ) {
224
223
retryCallbackCount ++
225
224
}),
226
225
)
@@ -240,7 +239,7 @@ func (s *RetrySuite) TestServerStream_SucceedsOnRetriableError() {
240
239
241
240
func (s * RetrySuite ) TestServerStream_OverrideFromContext () {
242
241
s .srv .resetFailingConfiguration (5 , codes .ResourceExhausted , noSleep ) // default is 3 and retriable_errors
243
- stream , err := s .Client .PingList (s .SimpleCtx (), testpb .GoodPingList , retry . WithCodes (codes .ResourceExhausted ), retry . WithMax (5 ))
242
+ stream , err := s .Client .PingList (s .SimpleCtx (), testpb .GoodPingList , WithCodes (codes .ResourceExhausted ), WithMax (5 ))
244
243
require .NoError (s .T (), err , "establishing the connection must always succeed" )
245
244
s .assertPingListWasCorrect (stream )
246
245
require .EqualValues (s .T (), 5 , s .srv .requestCount (), "three requests should have been made" )
@@ -253,8 +252,8 @@ func (s *RetrySuite) TestServerStream_PerCallDeadline_Succeeds() {
253
252
// a retry call with a 50 millisecond deadline. The 5th one doesn't sleep and succeeds.
254
253
deadlinePerCall := 100 * time .Millisecond
255
254
s .srv .resetFailingConfiguration (5 , codes .NotFound , 2 * deadlinePerCall )
256
- stream , err := s .Client .PingList (s .SimpleCtx (), testpb .GoodPingList , retry . WithPerRetryTimeout (deadlinePerCall ),
257
- retry . WithMax (5 ))
255
+ stream , err := s .Client .PingList (s .SimpleCtx (), testpb .GoodPingList , WithPerRetryTimeout (deadlinePerCall ),
256
+ WithMax (5 ))
258
257
require .NoError (s .T (), err , "establishing the connection must always succeed" )
259
258
s .assertPingListWasCorrect (stream )
260
259
require .EqualValues (s .T (), 5 , s .srv .requestCount (), "three requests should have been made" )
@@ -274,8 +273,8 @@ func (s *RetrySuite) TestServerStream_PerCallDeadline_FailsOnParent() {
274
273
s .srv .resetFailingConfiguration (5 , codes .NotFound , 2 * deadlinePerCall )
275
274
parentCtx , cancel := context .WithTimeout (context .TODO (), parentDeadline )
276
275
defer cancel ()
277
- stream , err := s .Client .PingList (parentCtx , testpb .GoodPingList , retry . WithPerRetryTimeout (deadlinePerCall ),
278
- retry . WithMax (5 ))
276
+ stream , err := s .Client .PingList (parentCtx , testpb .GoodPingList , WithPerRetryTimeout (deadlinePerCall ),
277
+ WithMax (5 ))
279
278
require .NoError (s .T (), err , "establishing the connection must always succeed" )
280
279
_ , err = stream .Recv ()
281
280
require .Equal (s .T (), codes .DeadlineExceeded , status .Code (err ), "failre code must be a gRPC error of Deadline class" )
@@ -286,7 +285,7 @@ func (s *RetrySuite) TestServerStream_OnRetryCallbackCalled() {
286
285
287
286
s .srv .resetFailingConfiguration (3 , codes .Unavailable , noSleep ) // see retriable_errors
288
287
stream , err := s .Client .PingList (s .SimpleCtx (), testpb .GoodPingList ,
289
- retry . WithOnRetryCallback (func (ctx context.Context , attempt uint , err error ) {
288
+ WithOnRetryCallback (func (ctx context.Context , attempt uint , err error ) {
290
289
retryCallbackCount ++
291
290
}),
292
291
)
@@ -322,7 +321,7 @@ func (s *RetrySuite) TestServerStream_CallRetrySucceeds() {
322
321
restarted := s .RestartServer (retryTimeout )
323
322
324
323
_ , err := s .Client .PingList (s .SimpleCtx (), testpb .GoodPingList ,
325
- retry . WithMax (40 ),
324
+ WithMax (40 ),
326
325
)
327
326
328
327
assert .NoError (s .T (), err , "establishing the connection should succeed" )
@@ -371,8 +370,8 @@ func TestChainedRetrySuite(t *testing.T) {
371
370
InterceptorTestSuite : & testpb.InterceptorTestSuite {
372
371
TestService : service ,
373
372
ClientOpts : []grpc.DialOption {
374
- grpc .WithUnaryInterceptor (middleware .ChainUnaryClient (preRetryInterceptor .UnaryClientInterceptor , retry . UnaryClientInterceptor (), postRetryInterceptor .UnaryClientInterceptor )),
375
- grpc .WithStreamInterceptor (middleware .ChainStreamClient (preRetryInterceptor .StreamClientInterceptor , retry . StreamClientInterceptor (), postRetryInterceptor .StreamClientInterceptor )),
373
+ grpc .WithUnaryInterceptor (middleware .ChainUnaryClient (preRetryInterceptor .UnaryClientInterceptor , UnaryClientInterceptor (), postRetryInterceptor .UnaryClientInterceptor )),
374
+ grpc .WithStreamInterceptor (middleware .ChainStreamClient (preRetryInterceptor .StreamClientInterceptor , StreamClientInterceptor (), postRetryInterceptor .StreamClientInterceptor )),
376
375
},
377
376
},
378
377
}
@@ -393,7 +392,7 @@ func (s *ChainedRetrySuite) SetupTest() {
393
392
}
394
393
395
394
func (s * ChainedRetrySuite ) TestUnaryWithChainedInterceptors_NoFailure () {
396
- _ , err := s .Client .Ping (s .SimpleCtx (), testpb .GoodPing , retry . WithMax (2 ))
395
+ _ , err := s .Client .Ping (s .SimpleCtx (), testpb .GoodPing , WithMax (2 ))
397
396
require .NoError (s .T (), err , "the invocation should succeed" )
398
397
require .EqualValues (s .T (), 1 , s .srv .requestCount (), "one request should have been made" )
399
398
require .EqualValues (s .T (), 1 , s .preRetryInterceptor .called , "pre-retry interceptor should be called once" )
@@ -402,15 +401,15 @@ func (s *ChainedRetrySuite) TestUnaryWithChainedInterceptors_NoFailure() {
402
401
403
402
func (s * ChainedRetrySuite ) TestUnaryWithChainedInterceptors_WithRetry () {
404
403
s .srv .resetFailingConfiguration (2 , codes .Unavailable , noSleep )
405
- _ , err := s .Client .Ping (s .SimpleCtx (), testpb .GoodPing , retry . WithMax (2 ))
404
+ _ , err := s .Client .Ping (s .SimpleCtx (), testpb .GoodPing , WithMax (2 ))
406
405
require .NoError (s .T (), err , "the second invocation should succeed" )
407
406
require .EqualValues (s .T (), 2 , s .srv .requestCount (), "two requests should have been made" )
408
407
require .EqualValues (s .T (), 1 , s .preRetryInterceptor .called , "pre-retry interceptor should be called once" )
409
408
require .EqualValues (s .T (), 2 , s .postRetryInterceptor .called , "post-retry interceptor should be called twice" )
410
409
}
411
410
412
411
func (s * ChainedRetrySuite ) TestStreamWithChainedInterceptors_NoFailure () {
413
- stream , err := s .Client .PingList (s .SimpleCtx (), testpb .GoodPingList , retry . WithMax (2 ))
412
+ stream , err := s .Client .PingList (s .SimpleCtx (), testpb .GoodPingList , WithMax (2 ))
414
413
require .NoError (s .T (), err , "the invocation should succeed" )
415
414
_ , err = stream .Recv ()
416
415
require .NoError (s .T (), err , "the Recv should succeed" )
@@ -421,11 +420,42 @@ func (s *ChainedRetrySuite) TestStreamWithChainedInterceptors_NoFailure() {
421
420
422
421
func (s * ChainedRetrySuite ) TestStreamWithChainedInterceptors_WithRetry () {
423
422
s .srv .resetFailingConfiguration (2 , codes .Unavailable , noSleep )
424
- stream , err := s .Client .PingList (s .SimpleCtx (), testpb .GoodPingList , retry . WithMax (2 ))
423
+ stream , err := s .Client .PingList (s .SimpleCtx (), testpb .GoodPingList , WithMax (2 ))
425
424
require .NoError (s .T (), err , "the second invocation should succeed" )
426
425
_ , err = stream .Recv ()
427
426
require .NoError (s .T (), err , "the Recv should succeed" )
428
427
require .EqualValues (s .T (), 2 , s .srv .requestCount (), "two requests should have been made" )
429
428
require .EqualValues (s .T (), 1 , s .preRetryInterceptor .called , "pre-retry interceptor should be called once" )
430
429
require .EqualValues (s .T (), 2 , s .postRetryInterceptor .called , "post-retry interceptor should be called twice" )
431
430
}
431
+
432
+ func TestJitterUp (t * testing.T ) {
433
+ // Arguments to jitterup.
434
+ duration := 10 * time .Second
435
+ variance := 0.10
436
+
437
+ // Bound to check.
438
+ max := 11000 * time .Millisecond
439
+ min := 9000 * time .Millisecond
440
+ high := scaleDuration (max , 0.98 )
441
+ low := scaleDuration (min , 1.02 )
442
+
443
+ highCount := 0
444
+ lowCount := 0
445
+
446
+ for i := 0 ; i < 1000 ; i ++ {
447
+ out := jitterUp (duration , variance )
448
+ assert .True (t , out <= max , "value %s must be <= %s" , out , max )
449
+ assert .True (t , out >= min , "value %s must be >= %s" , out , min )
450
+
451
+ if out > high {
452
+ highCount ++
453
+ }
454
+ if out < low {
455
+ lowCount ++
456
+ }
457
+ }
458
+
459
+ assert .True (t , highCount != 0 , "at least one sample should reach to >%s" , high )
460
+ assert .True (t , lowCount != 0 , "at least one sample should to <%s" , low )
461
+ }
0 commit comments