github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/f/backoff.go (about)

     1  package f
     2  
     3  import (
     4  	"context"
     5  	"github.com/cenkalti/backoff/v4"
     6  	"github.com/go-resty/resty/v2"
     7  	"time"
     8  )
     9  
    10  // RetryOperation retry operation manager.
    11  type RetryOperation struct {
    12  	backoff.Operation
    13  	*backoff.ExponentialBackOff
    14  	ctx context.Context
    15  }
    16  
    17  var (
    18  	// Call Periods: 1s,2s,4s,8s,16s,32s,1m,1m,1m... 24h=1day
    19  	RetryPeriodOf1s1m1d = &RetryPeriodDuration{
    20  		InitialInterval: time.Second,
    21  		MaxInterval:     time.Minute,
    22  		MaxElapsedTime:  24 * time.Hour,
    23  		RandomFactor:    0.02,
    24  		Multiplier:      2.0,
    25  	}
    26  )
    27  
    28  // RetryPeriodDuration With Periods.
    29  type RetryPeriodDuration struct {
    30  	InitialInterval             time.Duration
    31  	MaxInterval, MaxElapsedTime time.Duration
    32  	RandomFactor, Multiplier    float64
    33  }
    34  
    35  // NewRetryOperationWithPeriod create a retryOperation with the context.
    36  func NewRetryOperationWithPeriod(operation func() error, period *RetryPeriodDuration, ctx ...context.Context) *RetryOperation {
    37  	return NewRetryOperation(operation, period.InitialInterval, period.MaxInterval, period.MaxElapsedTime, period.RandomFactor, period.Multiplier, ctx...)
    38  }
    39  
    40  // NewRetryOperation create a retryOperation with the context.
    41  func NewRetryOperation(operation func() error,
    42  	initialInterval, maxInterval, maxElapsedTime time.Duration, randomizationFactor, multiplier float64,
    43  	ctx ...context.Context) *RetryOperation {
    44  	var o backoff.Operation
    45  	if operation != nil {
    46  		o = operation
    47  	} else {
    48  		o = func() error { return nil }
    49  	}
    50  	if initialInterval == 0 {
    51  		initialInterval = backoff.DefaultInitialInterval
    52  	}
    53  	if maxInterval == 0 {
    54  		maxInterval = backoff.DefaultMaxInterval
    55  	}
    56  	if maxElapsedTime == 0 {
    57  		maxElapsedTime = backoff.DefaultMaxElapsedTime
    58  	}
    59  	if randomizationFactor == 0 {
    60  		randomizationFactor = backoff.DefaultRandomizationFactor
    61  	}
    62  	if multiplier == 0 {
    63  		multiplier = backoff.DefaultMultiplier
    64  	}
    65  	b := &backoff.ExponentialBackOff{
    66  		InitialInterval:     initialInterval,
    67  		RandomizationFactor: randomizationFactor,
    68  		Multiplier:          multiplier,
    69  		MaxInterval:         maxInterval,
    70  		MaxElapsedTime:      maxElapsedTime,
    71  		Stop:                backoff.Stop,
    72  		Clock:               backoff.SystemClock,
    73  	}
    74  	var c context.Context
    75  	if len(ctx) == 1 {
    76  		c = ctx[0]
    77  	} else {
    78  		c = context.Background()
    79  	}
    80  	b.Reset()
    81  	return &RetryOperation{o, b, c}
    82  }
    83  
    84  func (ro *RetryOperation) Context() context.Context {
    85  	return ro.ctx
    86  }
    87  
    88  func (ro *RetryOperation) NextBackOff() time.Duration {
    89  	select {
    90  	case <-ro.ctx.Done():
    91  		return backoff.Stop
    92  	default:
    93  	}
    94  	next := ro.ExponentialBackOff.NextBackOff()
    95  	if deadline, ok := ro.ctx.Deadline(); ok && deadline.Sub(time.Now()) < next {
    96  		return backoff.Stop
    97  	}
    98  	return next
    99  }
   100  
   101  // Retry the operation o until it does not return error or BackOff stops.
   102  func (ro *RetryOperation) Retry() error {
   103  	return backoff.Retry(ro.Operation, ro)
   104  }
   105  
   106  // NewRetryConditionRequest create a retry http request.
   107  // https://github.com/go-resty/resty#retries
   108  func NewRetryConditionRequest(condition func(*resty.Response, error) bool, settings ...func(client *resty.Client)) *resty.Request {
   109  	if condition == nil {
   110  		condition = func(response *resty.Response, err error) bool { return false }
   111  	}
   112  	client := resty.New()
   113  	if len(settings) > 0 {
   114  		settings[0](client)
   115  	}
   116  	return client.AddRetryCondition(condition).R()
   117  }
   118  
   119  // NewRetryTimesRequest create a retry http request.
   120  // https://github.com/go-resty/resty#retries
   121  func NewRetryTimesRequest(maxRetries int, waitTime, maxWaitTime time.Duration, settings ...func(client *resty.Client)) *resty.Request {
   122  	if maxRetries < 1 {
   123  		maxRetries = 1
   124  	}
   125  	if waitTime < time.Millisecond {
   126  		waitTime = 100 * time.Millisecond
   127  	}
   128  	if maxWaitTime < time.Second {
   129  		waitTime = 2 * time.Second
   130  	}
   131  	client := resty.New()
   132  	for _, setting := range settings {
   133  		setting(client)
   134  	}
   135  	return client.SetRetryCount(maxRetries).SetRetryWaitTime(waitTime).SetRetryMaxWaitTime(maxWaitTime).R()
   136  }