mirror of
https://github.com/zeromicro/go-zero.git
synced 2025-01-23 00:50:20 +08:00
feat: retry with ctx deadline (#3626)
This commit is contained in:
parent
4f22034342
commit
fd070fec91
@ -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,19 +98,51 @@ func TestRetryWithInterval(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRetryCtx(t *testing.T) {
|
func TestRetryCtx(t *testing.T) {
|
||||||
assert.NotNil(t, DoWithRetryCtx(context.Background(), func(ctx context.Context, retryCount int) error {
|
t.Run("with timeout", func(t *testing.T) {
|
||||||
if retryCount == 0 {
|
assert.NotNil(t, DoWithRetryCtx(context.Background(), func(ctx context.Context, retryCount int) error {
|
||||||
return errors.New("any")
|
if retryCount == 0 {
|
||||||
}
|
return errors.New("any")
|
||||||
time.Sleep(time.Millisecond * 150)
|
}
|
||||||
return nil
|
time.Sleep(time.Millisecond * 150)
|
||||||
}, WithTimeout(time.Millisecond*250), WithInterval(time.Millisecond*150)))
|
|
||||||
|
|
||||||
assert.NotNil(t, DoWithRetryCtx(context.Background(), func(ctx context.Context, retryCount int) error {
|
|
||||||
if retryCount == 1 {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}, WithTimeout(time.Millisecond*250), WithInterval(time.Millisecond*150)))
|
||||||
time.Sleep(time.Millisecond * 150)
|
|
||||||
return errors.New("any ")
|
assert.NotNil(t, DoWithRetryCtx(context.Background(), func(ctx context.Context, retryCount int) error {
|
||||||
}, WithTimeout(time.Millisecond*250), WithInterval(time.Millisecond*150)))
|
if retryCount == 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 150)
|
||||||
|
return errors.New("any ")
|
||||||
|
}, 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…
Reference in New Issue
Block a user