github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/backoff/backoff.go (about)

     1  package backoff
     2  
     3  import (
     4  	"math"
     5  	"time"
     6  
     7  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xrand"
     8  )
     9  
    10  // Backoff is the interface that contains logic of delaying operation retry.
    11  type Backoff interface {
    12  	// Delay returns mapping of i to Delay.
    13  	Delay(i int) time.Duration
    14  }
    15  
    16  // Default parameters used by Retry() functions within different sub packages.
    17  const (
    18  	fastSlot = 5 * time.Millisecond
    19  	slowSlot = 1 * time.Second
    20  )
    21  
    22  var (
    23  	Fast = New(
    24  		WithSlotDuration(fastSlot),
    25  		WithCeiling(6),
    26  	)
    27  	Slow = New(
    28  		WithSlotDuration(slowSlot),
    29  		WithCeiling(6),
    30  	)
    31  )
    32  
    33  var _ Backoff = (*logBackoff)(nil)
    34  
    35  // logBackoff contains logarithmic Backoff policy.
    36  type logBackoff struct {
    37  	// slotDuration is a size of a single time slot used in Backoff Delay
    38  	// calculation.
    39  	// If slotDuration is less or equal to zero, then the time.Second value is
    40  	// used.
    41  	slotDuration time.Duration
    42  
    43  	// ceiling is a maximum degree of Backoff Delay growth.
    44  	// If ceiling is less or equal to zero, then the default ceiling of 1 is
    45  	// used.
    46  	ceiling uint
    47  
    48  	// jitterLimit controls fixed and random portions of Backoff Delay.
    49  	// Its value can be in range [0, 1].
    50  	// If jitterLimit is non zero, then the Backoff Delay will be equal to (F + R),
    51  	// where F is a result of multiplication of this value and calculated Delay
    52  	// duration D; and R is a random sized part from [0,(D - F)].
    53  	jitterLimit float64
    54  
    55  	// generator of jitter
    56  	r xrand.Rand
    57  }
    58  
    59  type option func(b *logBackoff)
    60  
    61  func WithSlotDuration(slotDuration time.Duration) option {
    62  	return func(b *logBackoff) {
    63  		b.slotDuration = slotDuration
    64  	}
    65  }
    66  
    67  func WithCeiling(ceiling uint) option {
    68  	return func(b *logBackoff) {
    69  		b.ceiling = ceiling
    70  	}
    71  }
    72  
    73  func WithJitterLimit(jitterLimit float64) option {
    74  	return func(b *logBackoff) {
    75  		b.jitterLimit = jitterLimit
    76  	}
    77  }
    78  
    79  func WithSeed(seed int64) option {
    80  	return func(b *logBackoff) {
    81  		b.r = xrand.New(xrand.WithLock(), xrand.WithSeed(seed))
    82  	}
    83  }
    84  
    85  func New(opts ...option) logBackoff {
    86  	b := logBackoff{
    87  		r: xrand.New(xrand.WithLock()),
    88  	}
    89  	for _, o := range opts {
    90  		if o != nil {
    91  			o(&b)
    92  		}
    93  	}
    94  
    95  	return b
    96  }
    97  
    98  // Delay returns mapping of i to Delay.
    99  func (b logBackoff) Delay(i int) time.Duration {
   100  	s := b.slotDuration
   101  	if s <= 0 {
   102  		s = time.Second
   103  	}
   104  	n := 1 << min(uint(i), max(1, b.ceiling))
   105  	d := s * time.Duration(n)
   106  	f := time.Duration(math.Min(1, math.Abs(b.jitterLimit)) * float64(d))
   107  	if f == d {
   108  		return f
   109  	}
   110  
   111  	return f + time.Duration(b.r.Int64(int64(d-f)+1))
   112  }
   113  
   114  func min(a, b uint) uint {
   115  	if a < b {
   116  		return a
   117  	}
   118  
   119  	return b
   120  }
   121  
   122  func max(a, b uint) uint {
   123  	if a > b {
   124  		return a
   125  	}
   126  
   127  	return b
   128  }