oss/retry/backoff.go (65 lines of code) (raw):
package retry
import (
"math"
"math/rand"
"time"
)
type EqualJitterBackoff struct {
baseDelay time.Duration
maxBackoff time.Duration
attemptCelling int
}
func NewEqualJJitterBackoff(baseDelay time.Duration, maxBackoff time.Duration) *EqualJitterBackoff {
return &EqualJitterBackoff{
baseDelay: baseDelay,
maxBackoff: maxBackoff,
attemptCelling: int(math.Log2(float64(math.MaxInt64 / baseDelay))),
}
}
func (j *EqualJitterBackoff) BackoffDelay(attempt int, err error) (time.Duration, error) {
// ceil = min(2 ^ attempts * baseDealy, maxBackoff)
// ceil/2 + [0.0, 1.0) *(ceil/2 + 1)
if attempt > j.attemptCelling {
attempt = j.attemptCelling
}
delayDuration := j.baseDelay * (1 << attempt)
if delayDuration > j.maxBackoff {
delayDuration = j.maxBackoff
}
half := delayDuration.Seconds() / 2
return floatSecondsDuration(half + rand.Float64()*float64(half+1)), nil
}
type FullJitterBackoff struct {
baseDelay time.Duration
maxBackoff time.Duration
attemptCelling int
}
func NewFullJitterBackoff(baseDelay time.Duration, maxBackoff time.Duration) *FullJitterBackoff {
return &FullJitterBackoff{
baseDelay: baseDelay,
maxBackoff: maxBackoff,
attemptCelling: int(math.Log2(float64(math.MaxInt64 / baseDelay))),
}
}
func (j *FullJitterBackoff) BackoffDelay(attempt int, err error) (time.Duration, error) {
// [0.0, 1.0) * min(2 ^ attempts * baseDealy, maxBackoff)
if attempt > j.attemptCelling {
attempt = j.attemptCelling
}
delayDuration := j.baseDelay * (1 << attempt)
if delayDuration > j.maxBackoff {
delayDuration = j.maxBackoff
}
return floatSecondsDuration(rand.Float64() * float64(delayDuration.Seconds())), nil
}
type FixedDelayBackoff struct {
fixedBackoff time.Duration
}
func NewFixedDelayBackoff(fixedBackoff time.Duration) *FixedDelayBackoff {
return &FixedDelayBackoff{
fixedBackoff: fixedBackoff,
}
}
func (j *FixedDelayBackoff) BackoffDelay(attempt int, err error) (time.Duration, error) {
return j.fixedBackoff, nil
}
func floatSecondsDuration(v float64) time.Duration {
return time.Duration(v * float64(time.Second))
}