feat: retry with ctx deadline (#3626)

master
Kevin Wan 1 year ago committed by GitHub
parent 4f22034342
commit fd070fec91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,3 +1,7 @@
coverage:
status:
patch: true
project: false # disabled because project coverage is not stable
comment: comment:
layout: "flags, files" layout: "flags, files"
behavior: once behavior: once

@ -2,7 +2,6 @@ package fx
import ( import (
"context" "context"
"errors"
"time" "time"
"github.com/zeromicro/go-zero/core/errorx" "github.com/zeromicro/go-zero/core/errorx"
@ -10,8 +9,6 @@ import (
const defaultRetryTimes = 3 const defaultRetryTimes = 3
var errTimeout = errors.New("retry timeout")
type ( type (
// RetryOption defines the method to customize DoWithRetry. // RetryOption defines the method to customize DoWithRetry.
RetryOption func(*retryOptions) RetryOption func(*retryOptions)
@ -28,7 +25,7 @@ type (
// and performs modification operations, it is best to lock them, // and performs modification operations, it is best to lock them,
// otherwise there may be data race issues // 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(func(errChan chan error, retryCount int) { return retry(context.Background(), func(errChan chan error, retryCount int) {
errChan <- fn() errChan <- fn()
}, opts...) }, opts...)
} }
@ -40,12 +37,12 @@ func DoWithRetry(fn func() error, opts ...RetryOption) error {
// otherwise there may be data race issues // otherwise there may be data race issues
func DoWithRetryCtx(ctx context.Context, fn func(ctx context.Context, retryCount int) error, func DoWithRetryCtx(ctx context.Context, fn func(ctx context.Context, retryCount int) error,
opts ...RetryOption) error { opts ...RetryOption) error {
return retry(func(errChan chan error, retryCount int) { return retry(ctx, func(errChan chan error, retryCount int) {
errChan <- fn(ctx, retryCount) errChan <- fn(ctx, retryCount)
}, opts...) }, opts...)
} }
func retry(fn func(errChan chan error, retryCount int), opts ...RetryOption) error { func retry(ctx context.Context, 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)
@ -53,7 +50,6 @@ func retry(fn func(errChan chan error, retryCount int), opts ...RetryOption) err
var berr errorx.BatchError var berr errorx.BatchError
var cancelFunc context.CancelFunc var cancelFunc context.CancelFunc
ctx := context.Background()
if options.timeout > 0 { if options.timeout > 0 {
ctx, cancelFunc = context.WithTimeout(ctx, options.timeout) ctx, cancelFunc = context.WithTimeout(ctx, options.timeout)
defer cancelFunc() defer cancelFunc()
@ -71,14 +67,14 @@ func retry(fn func(errChan chan error, retryCount int), opts ...RetryOption) err
return nil return nil
} }
case <-ctx.Done(): case <-ctx.Done():
berr.Add(errTimeout) berr.Add(ctx.Err())
return berr.Err() return berr.Err()
} }
if options.interval > 0 { if options.interval > 0 {
select { select {
case <-ctx.Done(): case <-ctx.Done():
berr.Add(errTimeout) berr.Add(ctx.Err())
return berr.Err() return berr.Err()
case <-time.After(options.interval): case <-time.After(options.interval):
} }

@ -98,6 +98,7 @@ func TestRetryWithInterval(t *testing.T) {
} }
func TestRetryCtx(t *testing.T) { func TestRetryCtx(t *testing.T) {
t.Run("with timeout", func(t *testing.T) {
assert.NotNil(t, DoWithRetryCtx(context.Background(), 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")
@ -113,4 +114,35 @@ func TestRetryCtx(t *testing.T) {
time.Sleep(time.Millisecond * 150) time.Sleep(time.Millisecond * 150)
return errors.New("any ") return errors.New("any ")
}, WithTimeout(time.Millisecond*250), WithInterval(time.Millisecond*150))) }, WithTimeout(time.Millisecond*250), WithInterval(time.Millisecond*150)))
})
t.Run("with deadline exceeded", func(t *testing.T) {
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Millisecond*250))
defer cancel()
var times int
assert.Error(t, DoWithRetryCtx(ctx, func(ctx context.Context, retryCount int) error {
times++
time.Sleep(time.Millisecond * 150)
return errors.New("any")
}, WithInterval(time.Millisecond*150)))
assert.Equal(t, 1, times)
})
t.Run("with deadline not exceeded", func(t *testing.T) {
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Millisecond*250))
defer cancel()
var times int
assert.NoError(t, DoWithRetryCtx(ctx, func(ctx context.Context, retryCount int) error {
times++
if times == defaultRetryTimes {
return nil
}
time.Sleep(time.Millisecond * 50)
return errors.New("any")
}))
assert.Equal(t, defaultRetryTimes, times)
})
} }

Loading…
Cancel
Save