gitee.com/sy_183/go-common@v1.0.5-0.20231205030221-958cfe129b47/lifecycle.v2/retry/retry.go (about) 1 package retry 2 3 import ( 4 "errors" 5 "gitee.com/sy_183/go-common/timer" 6 "math" 7 "time" 8 ) 9 10 var ErrInterrupted = errors.New("retry interrupted") 11 12 type RetryContext struct { 13 Do func() error 14 15 Interval time.Duration 16 17 Delay func(ctx *RetryContext) time.Duration 18 19 Interrupter chan struct{} 20 21 MaxRetry int 22 23 Retrievable func(ctx *RetryContext) bool 24 25 Ignorable func(ctx *RetryContext) bool 26 27 Error error 28 29 Retry int 30 } 31 32 type Retry RetryContext 33 34 func MakeRetry(retry Retry) *RetryContext { 35 c := new(RetryContext) 36 *c = RetryContext(retry) 37 return c 38 } 39 40 func (c *RetryContext) do(retryTimer *timer.Timer) bool { 41 maxRetry := c.MaxRetry 42 if maxRetry < 0 { 43 maxRetry = math.MaxInt 44 } 45 // do func 46 if c.Error = c.Do(); c.Error != nil { 47 // check error ignorable 48 if ignorable := c.Ignorable; ignorable != nil { 49 if ignorable(c) { 50 c.Error = nil 51 return false 52 } 53 } 54 if c.Error == ErrInterrupted { 55 return false 56 } 57 58 // check retrievable 59 if c.Retry >= maxRetry { 60 return false 61 } 62 if retrievable := c.Retrievable; retrievable != nil { 63 if !retrievable(c) { 64 return false 65 } 66 } 67 c.Retry++ 68 69 // get retry delay 70 delay := c.Interval 71 if c.Delay != nil { 72 delay = c.Delay(c) 73 } 74 if delay <= 0 { 75 delay = time.Second 76 } 77 78 retryTimer.After(delay) 79 return true 80 } 81 return false 82 } 83 84 func (c *RetryContext) Todo() error { 85 retryTimer := timer.NewTimer(make(chan struct{})) 86 defer retryTimer.Stop() 87 88 if retry := c.do(retryTimer); c.Error == nil { 89 return nil 90 } else if !retry { 91 return c.Error 92 } 93 94 for { 95 select { 96 case <-retryTimer.C: 97 if retry := c.do(retryTimer); c.Error == nil { 98 return nil 99 } else if !retry { 100 return c.Error 101 } 102 case <-c.Interrupter: 103 return ErrInterrupted 104 } 105 } 106 }