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  }