Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use error information to calculate backoff #15

Closed
wants to merge 8 commits into from
Prev Previous commit
Next Next commit
feat!: add option addOnly to WithJitter and WithJitterPercent
BREAKING CHANGE: WithJitter and WithJitterPercent now take an additional
`addOnly` and panic if the specified jitter is invalid.
  • Loading branch information
aisbergg committed Jun 8, 2022
commit 05401c4c5d215b4fa69db51216e9578d242a6b58
50 changes: 34 additions & 16 deletions backoff.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,43 +35,61 @@ func IsStopped(delay time.Duration) bool {
return delay < 0
}

// WithJitter wraps a backoff function and adds the specified jitter. j can be
// interpreted as "+/- j". For example, if j were 5 seconds and the backoff
// returned 20s, the value could be between 15 and 25 seconds. The value can
// never be less than 0.
func WithJitter(j time.Duration, next Backoff) Backoff {
// WithJitter wraps a backoff function and adds the specified jitter.
// If addOnly is specified, then a jitter up to +j will be added on top of the
// backoff; otherwise a jitter up to ±j will be applied. For example, if j is
// 5s, addOnly is false and the backoff returned is 20s, then the resulting
// value could be between 15 and 25 seconds. Panics if j is less than 0.
func WithJitter(j time.Duration, addOnly bool, next Backoff) Backoff {
if j < 0 {
panic("jitter must be >= 0")
}
return BackoffFunc(func(err error) (time.Duration, error) {
delay, err := next.Next(err)
if IsStopped(delay) {
return Stop, err
}

diff := time.Duration(rand.Int63n(int64(j)*2) - int64(j))
delay = delay + diff
if IsStopped(delay) {
delay = 0
if addOnly {
delay += time.Duration(rand.Int63n(int64(j)))
} else {
diff := time.Duration(rand.Int63n(int64(j)*2) - int64(j))
delay = delay + diff
if delay < 0 {
delay = 0
}
}
return delay, err
})
}

// WithJitterPercent wraps a backoff function and adds the specified jitter
// percentage. j can be interpreted as "+/- j%". For example, if j were 5 and
// the backoff returned 20s, the value could be between 19 and 21 seconds. The
// value can never be less than 0 or greater than 100.
func WithJitterPercent(j uint64, next Backoff) Backoff {
// percentage.
// If addOnly is specified, then a jitter up to +j% will be added on top of the
// backoff; otherwise a jitter up to ±j% will be applied. For example, if j is
// 5, addOnly is false and the backoff returned is 20s, then the resulting
// value could be between 19 and 21 seconds. Panics if j is less than 0 or greater than 100.
func WithJitterPercent(j uint64, addOnly bool, next Backoff) Backoff {
if j < 0 && j > 100 {
panic("jitter must be between 0 and 100")
}
return BackoffFunc(func(err error) (time.Duration, error) {
delay, err := next.Next(err)
if IsStopped(delay) {
return Stop, err
}

// Get a value between -j and j, the convert to a percentage
top := rand.Int63n(int64(j)*2) - int64(j)
var top int64
if addOnly {
top = rand.Int63n(int64(j))
} else {
// get random value between -j and +j
top = rand.Int63n(int64(j)*2) - int64(j)
}
pct := 1 - float64(top)/100.0

delay = time.Duration(float64(delay) * pct)
if IsStopped(delay) {
if delay < 0 {
delay = 0
}
return delay, err
Expand Down