github.com/sandwich-go/boost@v1.3.29/retry/retry.go (about) 1 package retry 2 3 import ( 4 "github.com/sandwich-go/boost/xerror" 5 "math" 6 "math/rand" 7 "time" 8 ) 9 10 // Do 执行 retryableFunc 函数,若执行失败,则会重新尝试执行 11 func Do(retryableFunc func(attempt uint) error, opts ...Option) error { 12 cc := NewOptions(opts...) 13 if err := cc.Context.Err(); err != nil { 14 return err 15 } 16 var limit = cc.Limit 17 if limit <= 0 { 18 limit = 1 19 } 20 var lastErr error 21 var ea xerror.Array 22 for attempt := uint(0); attempt < limit; attempt++ { 23 err := retryableFunc(attempt) 24 if err == nil { 25 return nil 26 } 27 if cc.LastErrorOnly { 28 lastErr = err 29 } else { 30 ea.Push(unpackUnrecoverable(err)) 31 } 32 33 if !cc.RetryIf(err) { 34 break 35 } 36 37 cc.OnRetry(attempt, err) 38 39 // if this is last attempt - don't wait 40 if attempt == cc.Limit-1 { 41 break 42 } 43 44 delayTime := cc.DelayType(attempt, err, cc) 45 if cc.MaxDelay > 0 && delayTime > cc.MaxDelay { 46 delayTime = cc.MaxDelay 47 } 48 49 select { 50 case <-time.After(delayTime): 51 case <-cc.Context.Done(): 52 return cc.Context.Err() 53 } 54 } 55 if cc.LastErrorOnly { 56 return lastErr 57 } 58 return ea.Err() 59 } 60 61 type unrecoverableError struct { 62 error 63 } 64 65 // Unrecoverable wraps an error in `unrecoverableError` struct 66 func Unrecoverable(err error) error { 67 return unrecoverableError{err} 68 } 69 70 // IsRecoverable checks if error is an instance of `unrecoverableError` 71 func IsRecoverable(err error) bool { 72 _, isUnrecoverable := err.(unrecoverableError) 73 return !isUnrecoverable 74 } 75 76 func unpackUnrecoverable(err error) error { 77 if unrecoverable, isUnrecoverable := err.(unrecoverableError); isUnrecoverable { 78 return unrecoverable.error 79 } 80 81 return err 82 } 83 84 type DelayTypeFunc func(n uint, err error, opts *Options) time.Duration 85 86 // BackOffDelay 指数级增长重试的推迟时长 87 func BackOffDelay(n uint, _ error, opts *Options) time.Duration { 88 // 1 << 63 would overflow signed int64 (time.Duration), thus 62. 89 const max uint = 62 90 91 if opts.MaxBackOffNInner == 0 { 92 if opts.Delay <= 0 { 93 opts.Delay = 1 94 } 95 opts.MaxBackOffNInner = max - uint(math.Floor(math.Log2(float64(opts.Delay)))) 96 } 97 98 if n > opts.MaxBackOffNInner { 99 n = opts.MaxBackOffNInner 100 } 101 102 return opts.Delay << n 103 } 104 105 // FixedDelay 固定延时 106 func FixedDelay(_ uint, _ error, config *Options) time.Duration { return config.Delay } 107 108 // RandomDelay 随机延时 109 func RandomDelay(_ uint, _ error, config *Options) time.Duration { 110 return time.Duration(rand.Int63n(int64(config.MaxJitter))) 111 } 112 113 // CombineDelay is a DelayType the combines all of the specified delays into a new DelayTypeFunc 114 func CombineDelay(delays ...DelayTypeFunc) DelayTypeFunc { 115 const maxInt64 = uint64(math.MaxInt64) 116 117 return func(n uint, err error, config *Options) time.Duration { 118 var total uint64 119 for _, delay := range delays { 120 total += uint64(delay(n, err, config)) 121 if total > maxInt64 { 122 total = maxInt64 123 } 124 } 125 return time.Duration(total) 126 } 127 }