github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/dlock/retry.go (about)

     1  package dlock
     2  
     3  import (
     4  	randv2 "math/rand/v2"
     5  	"sync/atomic"
     6  	"time"
     7  )
     8  
     9  type linearBackoff time.Duration
    10  
    11  func (backoff linearBackoff) Next() time.Duration {
    12  	return time.Duration(backoff)
    13  }
    14  
    15  func EndlessRetry(backoff time.Duration) RetryStrategy {
    16  	return linearBackoff(backoff)
    17  }
    18  
    19  func NoRetry() RetryStrategy {
    20  	return linearBackoff(0)
    21  }
    22  
    23  type limitedRetry struct {
    24  	strategy RetryStrategy
    25  	count    int64
    26  	maxCount int64
    27  }
    28  
    29  func (retry *limitedRetry) Next() time.Duration {
    30  	if atomic.LoadInt64(&retry.count) >= retry.maxCount {
    31  		return 0
    32  	}
    33  	atomic.AddInt64(&retry.count, 1)
    34  	return retry.strategy.Next()
    35  }
    36  
    37  func LimitedRetry(backoff time.Duration, maxCount int64) RetryStrategy {
    38  	if backoff.Milliseconds() <= 0 {
    39  		return NoRetry()
    40  	}
    41  	return &limitedRetry{
    42  		strategy: ExponentialBackoffRetry(
    43  			maxCount,
    44  			backoff,
    45  			0,
    46  			1.0,
    47  			0.1,
    48  		),
    49  		maxCount: maxCount,
    50  	}
    51  }
    52  
    53  type exponentialBackoff struct {
    54  	duration time.Duration
    55  	factor   float64
    56  	jitter   float64
    57  	steps    int64
    58  	cap      time.Duration
    59  }
    60  
    61  func (backoff *exponentialBackoff) Next() time.Duration {
    62  	if atomic.LoadInt64(&backoff.steps) < 1 {
    63  		return NoRetry().Next()
    64  	}
    65  	atomic.AddInt64(&backoff.steps, -1)
    66  	duration := backoff.duration
    67  	if backoff.factor != 0 {
    68  		backoff.duration = time.Duration(float64(backoff.duration) * backoff.factor)
    69  		if backoff.cap > 0 && backoff.duration > backoff.cap {
    70  			backoff.duration = backoff.cap
    71  			atomic.SwapInt64(&backoff.steps, 0)
    72  		}
    73  	}
    74  	if backoff.jitter > 0 {
    75  		duration = duration + time.Duration(randv2.Float64()*backoff.jitter*float64(duration))
    76  	}
    77  	return duration
    78  }
    79  
    80  func ExponentialBackoffRetry(maxSteps int64, initBackoff, maxBackoff time.Duration, backoffFactor, jitter float64) RetryStrategy {
    81  	return &exponentialBackoff{
    82  		cap:      maxBackoff,
    83  		duration: initBackoff,
    84  		factor:   backoffFactor,
    85  		jitter:   jitter,
    86  		steps:    maxSteps,
    87  	}
    88  }
    89  
    90  func DefaultExponentialBackoffRetry() RetryStrategy {
    91  	return ExponentialBackoffRetry(
    92  		5,
    93  		10*time.Millisecond,
    94  		0,
    95  		1.0,
    96  		0.1,
    97  	)
    98  }