Skip to content

Commit

Permalink
Fix panics in DurationWithJitter utils when computed variance is <= z…
Browse files Browse the repository at this point in the history
…ero. (#10507)

Fixes a panic in DurationWithJitter, DurationWithPositiveJitter, DurationWithNegativeJitter when small inputs result in a variance of zero, because rand.Int63n panics on zero.
  • Loading branch information
seizethedave authored Jan 24, 2025
1 parent 8f44959 commit ad6778e
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
* [BUGFIX] Distributor: return HTTP status 415 Unsupported Media Type instead of 200 Success for Remote Write 2.0 until we support it. #10423
* [BUGFIX] Query-frontend: Add flag `-query-frontend.prom2-range-compat` and corresponding YAML to rewrite queries with ranges that worked in Prometheus 2 but are invalid in Prometheus 3. #10445 #10461 #10502
* [BUGFIX] Distributor: Fix edge case at the HA-tracker with memberlist as KVStore, where when a replica in the KVStore is marked as deleted but not yet removed, it fails to update the KVStore. #10443
* [BUGFIX] Distributor: Fix panics in `DurationWithJitter` util functions when computed variance is zero. #10507
* [BUGFIX] Ingester: Fixed a race condition in the `PostingsForMatchers` cache that may have infrequently returned expired cached postings. #10500

### Mixin
Expand Down
15 changes: 6 additions & 9 deletions pkg/util/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,38 +66,35 @@ func ParseDurationMS(s string) (int64, error) {

// DurationWithJitter returns random duration from "input - input*variance" to "input + input*variance" interval.
func DurationWithJitter(input time.Duration, variancePerc float64) time.Duration {
// No duration? No jitter.
if input == 0 {
variance := int64(float64(input) * variancePerc)
if variance <= 0 {
return 0
}

variance := int64(float64(input) * variancePerc)
jitter := rand.Int63n(variance*2) - variance

return input + time.Duration(jitter)
}

// DurationWithPositiveJitter returns random duration from "input" to "input + input*variance" interval.
func DurationWithPositiveJitter(input time.Duration, variancePerc float64) time.Duration {
// No duration? No jitter.
if input == 0 {
variance := int64(float64(input) * variancePerc)
if variance <= 0 {
return 0
}

variance := int64(float64(input) * variancePerc)
jitter := rand.Int63n(variance)

return input + time.Duration(jitter)
}

// DurationWithNegativeJitter returns random duration from "input - input*variance" to "input" interval.
func DurationWithNegativeJitter(input time.Duration, variancePerc float64) time.Duration {
// No duration? No jitter.
if input == 0 {
variance := int64(float64(input) * variancePerc)
if variance <= 0 {
return 0
}

variance := int64(float64(input) * variancePerc)
jitter := rand.Int63n(variance)

return input - time.Duration(jitter)
Expand Down
20 changes: 10 additions & 10 deletions pkg/util/time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ func TestDurationWithJitter(t *testing.T) {
}
}

func TestDurationWithJitter_ZeroInputDuration(t *testing.T) {
assert.Equal(t, time.Duration(0), DurationWithJitter(time.Duration(0), 0.5))
}

func TestDurationWithPositiveJitter(t *testing.T) {
const numRuns = 1000

Expand All @@ -82,10 +78,6 @@ func TestDurationWithPositiveJitter(t *testing.T) {
}
}

func TestDurationWithPositiveJitter_ZeroInputDuration(t *testing.T) {
assert.Equal(t, time.Duration(0), DurationWithPositiveJitter(time.Duration(0), 0.5))
}

func TestDurationWithNegativeJitter(t *testing.T) {
const numRuns = 1000

Expand All @@ -96,8 +88,16 @@ func TestDurationWithNegativeJitter(t *testing.T) {
}
}

func TestDurationWithNegativeJitter_ZeroInputDuration(t *testing.T) {
assert.Equal(t, time.Duration(0), DurationWithNegativeJitter(time.Duration(0), 0.5))
func TestDurationWithJitterFamily_ZeroOutputs(t *testing.T) {
// Make sure all of these functions return zero when the computed variance is <= 0.

type durFunc func(time.Duration, float64) time.Duration
for _, f := range []durFunc{DurationWithJitter, DurationWithNegativeJitter, DurationWithPositiveJitter} {
assert.Equal(t, time.Duration(0), f(time.Duration(0), 0.5))
assert.Equal(t, time.Duration(0), f(time.Duration(7), 0.1))
assert.Equal(t, time.Duration(0), f(time.Duration(7), -0.1))
assert.Equal(t, time.Duration(0), f(10*time.Minute, -0.1))
}
}

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

0 comments on commit ad6778e

Please sign in to comment.