github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/utils/retry/retry.go (about) 1 // Package retry implements frequently used retry strategies and options. 2 package retry 3 4 import "time" 5 6 // Stop is used to indicate the retry function to stop retry. 7 type Stop struct { 8 Err error 9 } 10 11 func (e Stop) Error() string { 12 return e.Err.Error() 13 } 14 15 // Default will call param function f at most 3 times before returning error. 16 // Between each retry will sleep an exponential time starts at 500ms. 17 // In case of all retry fails, the total sleep time will be about 750ms - 2250ms. 18 // 19 // It is shorthand for Retry(3, 500*time.Millisecond, f). 20 func Default(f func() error, opts ...Option) Result { 21 return Retry(3, 500*time.Millisecond, f, opts...) 22 } 23 24 // Retry retry the target function with exponential sleep time. 25 // It implements algorithm described in https://upgear.io/blog/simple-golang-retry-function/. 26 func Retry(attempts int, sleep time.Duration, f func() error, opts ...Option) Result { 27 opt := defaultOptions 28 opt.Attempts = attempts 29 opt.Sleep = sleep 30 return retry(opt, f, opts...) 31 } 32 33 // Const retry the target function with constant sleep time. 34 // It is shorthand for Retry(attempts, sleep, f, C()). 35 func Const(attempts int, sleep time.Duration, f func() error, opts ...Option) Result { 36 opt := defaultOptions 37 opt.Attempts = attempts 38 opt.Sleep = sleep 39 opt.Strategy = constant 40 return retry(opt, f, opts...) 41 } 42 43 // Linear retry the target function with linear sleep time. 44 // It is shorthand for Retry(attempts, sleep, f, L(sleep)). 45 func Linear(attempts int, sleep time.Duration, f func() error, opts ...Option) Result { 46 opt := defaultOptions 47 opt.Attempts = attempts 48 opt.Sleep = sleep 49 opt.Strategy = linear{sleep}.next 50 return retry(opt, f, opts...) 51 } 52 53 // Forever retry the target function endlessly if it returns error. 54 // To stop the the retry loop on error, the target function should return Stop. 55 // 56 // The caller should take care of dead loop. 57 func Forever(sleep, maxSleep time.Duration, f func() error, opts ...Option) Result { 58 opt := defaultOptions 59 opt.Sleep = sleep 60 opt.MaxSleep = maxSleep 61 return retry(opt, f, opts...) 62 } 63 64 // retry do the retry job according given options. 65 func retry(opt options, f func() error, opts ...Option) (r Result) { 66 for _, o := range opts { 67 opt = o(opt) 68 } 69 70 var err error 71 r.Attempts++ 72 if err = f(); err == nil { 73 r.Ok = true 74 if opt.Breaker != nil { 75 opt.Breaker.succ.incr(time.Now().Unix()) 76 } 77 return r 78 } 79 80 var merr = NewSizedError(opt.MaxErrors) 81 var sleep = opt.Sleep 82 for { 83 if _, ok := err.(Stop); ok { 84 break 85 } 86 // attempts <= 0 means retry forever. 87 if opt.Attempts > 0 && r.Attempts >= opt.Attempts { 88 break 89 } 90 // check sliding window breaker 91 if opt.Breaker != nil && !opt.Breaker.shouldRetry() { 92 break 93 } 94 95 merr.Append(err) 96 opt.Hook(r.Attempts, err) 97 if opt.Breaker != nil { 98 opt.Breaker.fail.incr(time.Now().Unix()) 99 } 100 101 if opt.MaxSleep > 0 && sleep > opt.MaxSleep { 102 sleep = opt.MaxSleep 103 } 104 if opt.Jitter == nil { 105 time.Sleep(sleep) 106 } else { 107 time.Sleep(opt.Jitter(sleep)) 108 } 109 r.Attempts++ 110 if err = f(); err == nil { 111 r.Ok = true 112 if opt.Breaker != nil { 113 opt.Breaker.succ.incr(time.Now().Unix()) 114 } 115 break 116 } 117 sleep = opt.Strategy(sleep) 118 } 119 if err != nil { 120 if s, ok := err.(Stop); ok { 121 // Return the original error for later checking. 122 merr.Append(s.Err) 123 opt.Hook(r.Attempts, s.Err) 124 125 // Stop error from caller don't count for circuit breaker. 126 } else { 127 merr.Append(err) 128 opt.Hook(r.Attempts, err) 129 if opt.Breaker != nil { 130 opt.Breaker.fail.incr(time.Now().Unix()) 131 } 132 } 133 } 134 r.Error = merr.ErrOrNil() 135 return r 136 } 137 138 type Result struct { 139 Ok bool 140 Attempts int 141 Error error 142 }