github.com/xxf098/lite-proxy@v0.15.1-0.20230422081941-12c69f323218/utils/retry.go (about)

     1  package utils
     2  
     3  import (
     4  	"errors"
     5  	"time"
     6  )
     7  
     8  var (
     9  	ErrRetryFailed = errors.New("all retry attempts failed")
    10  )
    11  
    12  // Strategy is a way to retry on a specific function.
    13  type Strategy interface {
    14  	// On performs a retry on a specific function, until it doesn't return any error.
    15  	On(func() error) error
    16  }
    17  
    18  type retryer struct {
    19  	totalAttempt int
    20  	nextDelay    func() uint32
    21  }
    22  
    23  // On implements Strategy.On.
    24  func (r *retryer) On(method func() error) error {
    25  	attempt := 0
    26  	accumulatedError := make([]error, 0, r.totalAttempt)
    27  	for attempt < r.totalAttempt {
    28  		err := method()
    29  		if err == nil {
    30  			return nil
    31  		}
    32  		numErrors := len(accumulatedError)
    33  		if numErrors == 0 || err.Error() != accumulatedError[numErrors-1].Error() {
    34  			accumulatedError = append(accumulatedError, err)
    35  		}
    36  		delay := r.nextDelay()
    37  		time.Sleep(time.Duration(delay) * time.Millisecond)
    38  		attempt++
    39  	}
    40  	return ErrRetryFailed
    41  }
    42  
    43  // Timed returns a retry strategy with fixed interval.
    44  func Timed(attempts int, delay uint32) Strategy {
    45  	return &retryer{
    46  		totalAttempt: attempts,
    47  		nextDelay: func() uint32 {
    48  			return delay
    49  		},
    50  	}
    51  }
    52  
    53  func ExponentialBackoff(attempts int, delay uint32) Strategy {
    54  	nextDelay := uint32(0)
    55  	return &retryer{
    56  		totalAttempt: attempts,
    57  		nextDelay: func() uint32 {
    58  			r := nextDelay
    59  			nextDelay += delay
    60  			return r
    61  		},
    62  	}
    63  }