package retry import ( "context" "errors" "fmt" "reflect" "runtime" "strings" "time" ) const ( // DefaultRetryTimes times of retry DefaultRetryTimes = 3 // DefaultRetryDuration time duration of two retries DefaultRetryDuration = time.Second * 1 ) // Config is config for retry type Config struct { context context.Context retryTimes uint retryDuration time.Duration } // Func is function that retry executes type Func func() error // Option is for adding retry config type Option func(*Config) // WithTimes set times of retry. func WithTimes(n uint) Option { return func(rc *Config) { rc.retryTimes = n } } // WithDuration set duration of retries. func WithDuration(d time.Duration) Option { return func(rc *Config) { rc.retryDuration = d } } // WithContext set retry context config. func WithContext(ctx context.Context) Option { return func(rc *Config) { rc.context = ctx } } // Retry executes the retryFunc repeatedly until it was successful or canceled by the context // The default times of retries is 3 and the default duration between retries is 1 seconds. func Retry(retryFunc Func, opts ...Option) error { config := &Config{ retryTimes: DefaultRetryTimes, retryDuration: DefaultRetryDuration, context: context.TODO(), } for _, opt := range opts { opt(config) } var i uint for i < config.retryTimes { err := retryFunc() if err != nil { select { case <-time.After(config.retryDuration): case <-config.context.Done(): return errors.New("retry is cancelled") } } else { return nil } i++ } funcPath := runtime.FuncForPC(reflect.ValueOf(retryFunc).Pointer()).Name() lastSlash := strings.LastIndex(funcPath, "/") funcName := funcPath[lastSlash+1:] return fmt.Errorf("function %s run failed after %d times retry", funcName, i) }