go-zero/core/fx/retry.go

111 lines
2.6 KiB
Go
Raw Normal View History

2020-07-26 17:09:05 +08:00
package fx
import (
"context"
"time"
"github.com/zeromicro/go-zero/core/errorx"
)
2020-07-26 17:09:05 +08:00
const defaultRetryTimes = 3
type (
2021-02-19 17:49:39 +08:00
// RetryOption defines the method to customize DoWithRetry.
2020-07-26 17:09:05 +08:00
RetryOption func(*retryOptions)
retryOptions struct {
times int
interval time.Duration
timeout time.Duration
2020-07-26 17:09:05 +08:00
}
)
2021-02-19 17:49:39 +08:00
// DoWithRetry runs fn, and retries if failed. Default to retry 3 times.
2023-05-28 12:11:55 +08:00
// Note that if the fn function accesses global variables outside the function
// and performs modification operations, it is best to lock them,
// otherwise there may be data race issues
2021-02-19 17:49:39 +08:00
func DoWithRetry(fn func() error, opts ...RetryOption) error {
2023-10-15 21:39:44 +08:00
return retry(context.Background(), func(errChan chan error, retryCount int) {
2023-05-28 12:11:55 +08:00
errChan <- fn()
}, opts...)
}
// DoWithRetryCtx runs fn, and retries if failed. Default to retry 3 times.
2023-05-28 12:11:55 +08:00
// 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, it is best to lock them,
// otherwise there may be data race issues
func DoWithRetryCtx(ctx context.Context, fn func(ctx context.Context, retryCount int) error,
opts ...RetryOption) error {
2023-10-15 21:39:44 +08:00
return retry(ctx, func(errChan chan error, retryCount int) {
2023-05-28 12:11:55 +08:00
errChan <- fn(ctx, retryCount)
}, opts...)
}
2023-10-15 21:39:44 +08:00
func retry(ctx context.Context, fn func(errChan chan error, retryCount int), opts ...RetryOption) error {
2021-04-15 19:49:17 +08:00
options := newRetryOptions()
2020-07-26 17:09:05 +08:00
for _, opt := range opts {
opt(options)
}
var berr errorx.BatchError
var cancelFunc context.CancelFunc
if options.timeout > 0 {
ctx, cancelFunc = context.WithTimeout(ctx, options.timeout)
defer cancelFunc()
}
2023-05-28 12:11:55 +08:00
errChan := make(chan error, 1)
2020-07-26 17:09:05 +08:00
for i := 0; i < options.times; i++ {
2023-05-28 12:11:55 +08:00
go fn(errChan, i)
select {
2023-05-28 12:11:55 +08:00
case err := <-errChan:
if err != nil {
berr.Add(err)
} else {
return nil
}
case <-ctx.Done():
2023-10-15 21:39:44 +08:00
berr.Add(ctx.Err())
return berr.Err()
}
if options.interval > 0 {
select {
case <-ctx.Done():
2023-10-15 21:39:44 +08:00
berr.Add(ctx.Err())
return berr.Err()
case <-time.After(options.interval):
}
2020-07-26 17:09:05 +08:00
}
}
return berr.Err()
}
2021-02-19 17:49:39 +08:00
// WithRetry customize a DoWithRetry call with given retry times.
func WithRetry(times int) RetryOption {
2020-07-26 17:09:05 +08:00
return func(options *retryOptions) {
options.times = times
}
}
func WithInterval(interval time.Duration) RetryOption {
return func(options *retryOptions) {
options.interval = interval
}
}
func WithTimeout(timeout time.Duration) RetryOption {
return func(options *retryOptions) {
options.timeout = timeout
}
}
2020-07-26 17:09:05 +08:00
func newRetryOptions() *retryOptions {
return &retryOptions{
2023-05-28 12:11:55 +08:00
times: defaultRetryTimes,
2020-07-26 17:09:05 +08:00
}
}