Skip to content

Commit

Permalink
Use int64 for computeJitterUpperBoundMs (#426)
Browse files Browse the repository at this point in the history
  • Loading branch information
nathan-artie authored Apr 10, 2024
1 parent bedadf0 commit 7f9ab89
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 19 deletions.
14 changes: 8 additions & 6 deletions lib/jitter/sleep.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,28 @@ import (

const DefaultMaxMs = 3500

func computeJitterUpperBoundMs(baseMs, maxMs, attempts int) int {
// https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
// sleep = random_between(0, min(cap, base * 2 ** attempt))
// 2 ** x == 1 << x
// computeJitterUpperBoundMs calculates min(maxMs, baseMs * 2 ** attempt).
func computeJitterUpperBoundMs(baseMs, maxMs, attempts int64) int64 {
if maxMs <= 0 {
return 0
}

// Check for overflows when computing base * 2 ** attempts.
// 2 ** x == 1 << x
if attemptsMaxMs := baseMs * (1 << attempts); attemptsMaxMs > 0 {
maxMs = min(maxMs, attemptsMaxMs)
}

return maxMs
}

// Jitter implements exponential backoff + jitter.
// See: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
// Algorithm: sleep = random_between(0, min(cap, base * 2 ** attempt))
func Jitter(baseMs, maxMs, attempts int) time.Duration {
upperBoundMs := computeJitterUpperBoundMs(baseMs, maxMs, attempts)
upperBoundMs := computeJitterUpperBoundMs(int64(baseMs), int64(maxMs), int64(attempts))
if upperBoundMs <= 0 {
return time.Duration(0)
}
return time.Duration(rand.Intn(upperBoundMs)) * time.Millisecond
return time.Duration(rand.Int63n(upperBoundMs)) * time.Millisecond
}
26 changes: 13 additions & 13 deletions lib/jitter/sleep_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ import (

func TestComputeJitterUpperBoundMs(t *testing.T) {
// A maxMs that is <= 0 returns 0.
assert.Equal(t, 0, computeJitterUpperBoundMs(0, 0, 0))
assert.Equal(t, 0, computeJitterUpperBoundMs(10, 0, 0))
assert.Equal(t, 0, computeJitterUpperBoundMs(10, 0, 100))
assert.Equal(t, 0, computeJitterUpperBoundMs(10, -1, 0))
assert.Equal(t, 0, computeJitterUpperBoundMs(10, -1, 100))
assert.Equal(t, int64(0), computeJitterUpperBoundMs(0, 0, 0))
assert.Equal(t, int64(0), computeJitterUpperBoundMs(10, 0, 0))
assert.Equal(t, int64(0), computeJitterUpperBoundMs(10, 0, 100))
assert.Equal(t, int64(0), computeJitterUpperBoundMs(10, -1, 0))
assert.Equal(t, int64(0), computeJitterUpperBoundMs(10, -1, 100))

// Increasing attempts with a baseMs of 10 and essentially no maxMs.
assert.Equal(t, 10, computeJitterUpperBoundMs(10, math.MaxInt, 0))
assert.Equal(t, 20, computeJitterUpperBoundMs(10, math.MaxInt, 1))
assert.Equal(t, 40, computeJitterUpperBoundMs(10, math.MaxInt, 2))
assert.Equal(t, 80, computeJitterUpperBoundMs(10, math.MaxInt, 3))
assert.Equal(t, 160, computeJitterUpperBoundMs(10, math.MaxInt, 4))
assert.Equal(t, int64(10), computeJitterUpperBoundMs(10, math.MaxInt64, 0))
assert.Equal(t, int64(20), computeJitterUpperBoundMs(10, math.MaxInt64, 1))
assert.Equal(t, int64(40), computeJitterUpperBoundMs(10, math.MaxInt64, 2))
assert.Equal(t, int64(80), computeJitterUpperBoundMs(10, math.MaxInt64, 3))
assert.Equal(t, int64(160), computeJitterUpperBoundMs(10, math.MaxInt64, 4))

// Large inputs do not panic.
assert.Equal(t, 100, computeJitterUpperBoundMs(10, 100, 200))
assert.Equal(t, 100, computeJitterUpperBoundMs(10, 100, math.MaxInt))
assert.Equal(t, math.MaxInt, computeJitterUpperBoundMs(math.MaxInt, math.MaxInt, math.MaxInt))
assert.Equal(t, int64(100), computeJitterUpperBoundMs(10, 100, 200))
assert.Equal(t, int64(100), computeJitterUpperBoundMs(10, 100, math.MaxInt64))
assert.Equal(t, int64(math.MaxInt64), computeJitterUpperBoundMs(math.MaxInt64, math.MaxInt64, math.MaxInt64))
}

func TestJitter(t *testing.T) {
Expand Down

0 comments on commit 7f9ab89

Please sign in to comment.