Skip to content

Commit

Permalink
Use math/rand global random source and deprecate PRNG global variable
Browse files Browse the repository at this point in the history
  • Loading branch information
justinrixx committed Mar 2, 2024
1 parent 8f425fd commit e0a54ae
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 1 deletion.
24 changes: 23 additions & 1 deletion rehttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ import (
// PRNG is the *math.Rand value to use to add jitter to the backoff
// algorithm used in ExpJitterDelay. By default it uses a *rand.Rand
// initialized with a source based on the current time in nanoseconds.
//
// Deprecated: math/rand sources can panic if used concurrently without
// synchronization. PRNG is no longer used by this package and its use
// outside this package is discouraged.
// https://github.com/PuerkitoBio/rehttp/issues/12
var PRNG = rand.New(rand.NewSource(time.Now().UnixNano()))

// terribly named interface to detect errors that support Temporary.
Expand Down Expand Up @@ -264,7 +269,24 @@ func ExpJitterDelay(base, max time.Duration) DelayFn {
exp := math.Pow(2, float64(attempt.Index))
top := float64(base) * exp
return time.Duration(
PRNG.Int63n(int64(math.Min(float64(max), top))),
rand.Int63n(int64(math.Min(float64(max), top))),
)
}
}

// ExpJitterDelayWithRand returns a DelayFn that returns a delay
// between 0 and base * 2^attempt capped at max (an exponential
// backoff delay with jitter). The provided generator will be used
// in place of rand.Int63n.
//
// See the full jitter algorithm in:
// http://www.awsarchitectureblog.com/2015/03/backoff.html
func ExpJitterDelayWithRand(base, max time.Duration, generator func(n int64) int64) DelayFn {
return func(attempt Attempt) time.Duration {
exp := math.Pow(2, float64(attempt.Index))
top := float64(base) * exp
return time.Duration(
generator(int64(math.Min(float64(max), top))),
)
}
}
Expand Down
11 changes: 11 additions & 0 deletions rehttp_delayfn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,14 @@ func TestExpJitterDelay(t *testing.T) {
assert.True(t, delay <= actual, "%d: %s > %s", i, delay, actual)
}
}

func TestExpJitterDelayWithRand(t *testing.T) {
fn := ExpJitterDelayWithRand(time.Second, 5*time.Second, func(n int64) int64 { return 999_999_999 % n })
for i := 0; i < 10; i++ {
delay := fn(Attempt{Index: i})
top := math.Pow(2, float64(i)) * float64(time.Second)
actual := time.Duration(math.Min(float64(5*time.Second), top))
assert.True(t, delay > 0, "%d: %s <= 0", i, delay)
assert.True(t, delay <= actual, "%d: %s > %s", i, delay, actual)
}
}
15 changes: 15 additions & 0 deletions rng_pre120.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build !go1.20
// +build !go1.20

package rehttp

import (
"math/rand"
"time"
)

// Only seed the global random source on versions prior to 1.20 since
// it's pre-seeded starting in 1.20
func init() {
rand.Seed(time.Now().UnixNano())
}

0 comments on commit e0a54ae

Please sign in to comment.