chore: refactor retry (#3291)

master
Kevin Wan 2 years ago committed by GitHub
parent 8d48e34eed
commit 2f2ddd373b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -24,27 +24,33 @@ type (
) )
// DoWithRetry runs fn, and retries if failed. Default to retry 3 times. // DoWithRetry runs fn, and retries if failed. Default to retry 3 times.
// Note that if the fn function accesses global variables outside the function and performs modification operations, // Note that if the fn function accesses global variables outside the function
// it is best to lock them, otherwise there may be data race issues // and performs modification operations, it is best to lock them,
// otherwise there may be data race issues
func DoWithRetry(fn func() error, opts ...RetryOption) error { func DoWithRetry(fn func() error, opts ...RetryOption) error {
return retry(fn, opts...) return retry(func(errChan chan error, retryCount int) {
errChan <- fn()
}, opts...)
} }
// DoWithRetryCtx runs fn, and retries if failed. Default to retry 3 times. // DoWithRetryCtx runs fn, and retries if failed. Default to retry 3 times.
// fn retryCount indicates the current number of retries, starting from 0 // fn retryCount indicates the current number of retries, starting from 0
// Note that if the fn function accesses global variables outside the function and performs modification operations, // Note that if the fn function accesses global variables outside the function
// it is best to lock them, otherwise there may be data race issues // and performs modification operations, it is best to lock them,
func DoWithRetryCtx(fn func(ctx context.Context, retryCount int) error, opts ...RetryOption) error { // otherwise there may be data race issues
return retry(fn, opts...) func DoWithRetryCtx(ctx context.Context, fn func(ctx context.Context, retryCount int) error,
opts ...RetryOption) error {
return retry(func(errChan chan error, retryCount int) {
errChan <- fn(ctx, retryCount)
}, opts...)
} }
func retry(fn interface{}, opts ...RetryOption) error { func retry(fn func(errChan chan error, retryCount int), opts ...RetryOption) error {
options := newRetryOptions() options := newRetryOptions()
for _, opt := range opts { for _, opt := range opts {
opt(options) opt(options)
} }
sign := make(chan error, 1)
var berr errorx.BatchError var berr errorx.BatchError
var cancelFunc context.CancelFunc var cancelFunc context.CancelFunc
ctx := context.Background() ctx := context.Background()
@ -53,18 +59,12 @@ func retry(fn interface{}, opts ...RetryOption) error {
defer cancelFunc() defer cancelFunc()
} }
errChan := make(chan error, 1)
for i := 0; i < options.times; i++ { for i := 0; i < options.times; i++ {
go func(retryCount int) { go fn(errChan, i)
switch f := fn.(type) {
case func() error:
sign <- f()
case func(ctx context.Context, retryCount int) error:
sign <- f(ctx, retryCount)
}
}(i)
select { select {
case err := <-sign: case err := <-errChan:
if err != nil { if err != nil {
berr.Add(err) berr.Add(err)
} else { } else {
@ -110,7 +110,5 @@ func WithTimeout(timeout time.Duration) RetryOption {
func newRetryOptions() *retryOptions { func newRetryOptions() *retryOptions {
return &retryOptions{ return &retryOptions{
times: defaultRetryTimes, times: defaultRetryTimes,
interval: 0,
timeout: 0,
} }
} }

@ -46,7 +46,7 @@ func TestRetry(t *testing.T) {
func TestRetryWithTimeout(t *testing.T) { func TestRetryWithTimeout(t *testing.T) {
assert.Nil(t, DoWithRetry(func() error { assert.Nil(t, DoWithRetry(func() error {
return nil return nil
}, WithTimeout(time.Second*10))) }, WithTimeout(time.Millisecond*500)))
times1 := 0 times1 := 0
assert.Nil(t, DoWithRetry(func() error { assert.Nil(t, DoWithRetry(func() error {
@ -54,9 +54,9 @@ func TestRetryWithTimeout(t *testing.T) {
if times1 == 1 { if times1 == 1 {
return errors.New("any ") return errors.New("any ")
} }
time.Sleep(time.Second * 3) time.Sleep(time.Millisecond * 150)
return nil return nil
}, WithTimeout(time.Second*5))) }, WithTimeout(time.Millisecond*250)))
total := defaultRetryTimes total := defaultRetryTimes
times2 := 0 times2 := 0
@ -65,13 +65,13 @@ func TestRetryWithTimeout(t *testing.T) {
if times2 == total { if times2 == total {
return nil return nil
} }
time.Sleep(time.Second) time.Sleep(time.Millisecond * 50)
return errors.New("any") return errors.New("any")
}, WithTimeout(time.Second*(time.Duration(total)+2)))) }, WithTimeout(time.Millisecond*50*(time.Duration(total)+2))))
assert.NotNil(t, DoWithRetry(func() error { assert.NotNil(t, DoWithRetry(func() error {
return errors.New("any") return errors.New("any")
}, WithTimeout(time.Second*5))) }, WithTimeout(time.Millisecond*250)))
} }
func TestRetryWithInterval(t *testing.T) { func TestRetryWithInterval(t *testing.T) {
@ -81,9 +81,9 @@ func TestRetryWithInterval(t *testing.T) {
if times1 == 1 { if times1 == 1 {
return errors.New("any") return errors.New("any")
} }
time.Sleep(time.Second * 3) time.Sleep(time.Millisecond * 150)
return nil return nil
}, WithTimeout(time.Second*5), WithInterval(time.Second*3))) }, WithTimeout(time.Millisecond*250), WithInterval(time.Millisecond*150)))
times2 := 0 times2 := 0
assert.NotNil(t, DoWithRetry(func() error { assert.NotNil(t, DoWithRetry(func() error {
@ -91,26 +91,26 @@ func TestRetryWithInterval(t *testing.T) {
if times2 == 2 { if times2 == 2 {
return nil return nil
} }
time.Sleep(time.Second * 3) time.Sleep(time.Millisecond * 150)
return errors.New("any ") return errors.New("any ")
}, WithTimeout(time.Second*5), WithInterval(time.Second*3))) }, WithTimeout(time.Millisecond*250), WithInterval(time.Millisecond*150)))
} }
func TestRetryCtx(t *testing.T) { func TestRetryCtx(t *testing.T) {
assert.NotNil(t, DoWithRetryCtx(func(ctx context.Context, retryCount int) error { assert.NotNil(t, DoWithRetryCtx(context.Background(), func(ctx context.Context, retryCount int) error {
if retryCount == 0 { if retryCount == 0 {
return errors.New("any") return errors.New("any")
} }
time.Sleep(time.Second * 3) time.Sleep(time.Millisecond * 150)
return nil return nil
}, WithTimeout(time.Second*5), WithInterval(time.Second*3))) }, WithTimeout(time.Millisecond*250), WithInterval(time.Millisecond*150)))
assert.NotNil(t, DoWithRetryCtx(func(ctx context.Context, retryCount int) error { assert.NotNil(t, DoWithRetryCtx(context.Background(), func(ctx context.Context, retryCount int) error {
if retryCount == 1 { if retryCount == 1 {
return nil return nil
} }
time.Sleep(time.Second * 3) time.Sleep(time.Millisecond * 150)
return errors.New("any ") return errors.New("any ")
}, WithTimeout(time.Second*5), WithInterval(time.Second*3))) }, WithTimeout(time.Millisecond*250), WithInterval(time.Millisecond*150)))
} }

Loading…
Cancel
Save