Merge pull request #1973 from hashicorp/b-random-stagger

Guard against divide by zero in lib.RandomStagger()
This commit is contained in:
Sean Chittenden 2016-04-23 13:21:27 -07:00
commit 7eb85dd268
2 changed files with 138 additions and 12 deletions

View File

@ -10,12 +10,32 @@ import (
// servicing Consul TTL Checks in advance of the TTL. // servicing Consul TTL Checks in advance of the TTL.
func DurationMinusBuffer(intv time.Duration, buffer time.Duration, jitter int64) time.Duration { func DurationMinusBuffer(intv time.Duration, buffer time.Duration, jitter int64) time.Duration {
d := intv - buffer d := intv - buffer
d -= RandomStagger(time.Duration(int64(d) / jitter)) if jitter == 0 {
d -= RandomStagger(d)
} else {
d -= RandomStagger(time.Duration(int64(d) / jitter))
}
return d return d
} }
// DurationMinusBufferDomain returns the domain of valid durations from a
// call to DurationMinusBuffer. This function is used to check user
// specified input values to DurationMinusBuffer.
func DurationMinusBufferDomain(intv time.Duration, buffer time.Duration, jitter int64) (min time.Duration, max time.Duration) {
max = intv - buffer
if jitter == 0 {
min = max
} else {
min = max - time.Duration(int64(max)/jitter)
}
return min, max
}
// Returns a random stagger interval between 0 and the duration // Returns a random stagger interval between 0 and the duration
func RandomStagger(intv time.Duration) time.Duration { func RandomStagger(intv time.Duration) time.Duration {
if intv == 0 {
return 0
}
return time.Duration(uint64(rand.Int63()) % uint64(intv)) return time.Duration(uint64(rand.Int63()) % uint64(intv))
} }

View File

@ -6,17 +6,123 @@ import (
) )
func TestDurationMinusBuffer(t *testing.T) { func TestDurationMinusBuffer(t *testing.T) {
const ( tests := []struct {
buffer = 10 * time.Second Duration time.Duration
jitter = 16 Buffer time.Duration
) Jitter int64
intv := 1 * time.Minute }{
minValue := (intv - buffer) - ((intv - buffer) / jitter) {
maxValue := intv - buffer Duration: 1 * time.Minute,
for i := 0; i < 10; i++ { Buffer: 10 * time.Second,
d := DurationMinusBuffer(intv, buffer, jitter) Jitter: 16,
if d < minValue || d > maxValue { },
t.Fatalf("Bad: %v", d) {
Duration: 1 * time.Second,
Buffer: 500 * time.Millisecond,
Jitter: 4,
},
{
Duration: 1 * time.Second,
Buffer: 1 * time.Second,
Jitter: 4,
},
{
Duration: 1 * time.Second,
Buffer: 1 * time.Second,
Jitter: 0,
},
{
Duration: 1 * time.Second,
Buffer: 1 * time.Second,
Jitter: 1,
},
}
for _, test := range tests {
min, max := DurationMinusBufferDomain(test.Duration, test.Buffer, test.Jitter)
for i := 0; i < 10; i++ {
d := DurationMinusBuffer(test.Duration, test.Buffer, test.Jitter)
if d < min || d > max {
t.Fatalf("Bad: %v", d)
}
}
}
}
func TestDurationMinusBufferDomain(t *testing.T) {
tests := []struct {
Duration time.Duration
Buffer time.Duration
Jitter int64
Min time.Duration
Max time.Duration
}{
{
Duration: 60 * time.Second,
Buffer: 10 * time.Second,
Jitter: 16,
Min: 46*time.Second + 875*time.Millisecond,
Max: 50 * time.Second,
},
{
Duration: 60 * time.Second,
Buffer: 0 * time.Second,
Jitter: 16,
Min: 56*time.Second + 250*time.Millisecond,
Max: 60 * time.Second,
},
{
Duration: 60 * time.Second,
Buffer: 0 * time.Second,
Jitter: 0,
Min: 60 * time.Second,
Max: 60 * time.Second,
},
{
Duration: 60 * time.Second,
Buffer: 0 * time.Second,
Jitter: 1,
Min: 0 * time.Second,
Max: 60 * time.Second,
},
{
Duration: 60 * time.Second,
Buffer: 0 * time.Second,
Jitter: 2,
Min: 30 * time.Second,
Max: 60 * time.Second,
},
{
Duration: 60 * time.Second,
Buffer: 0 * time.Second,
Jitter: 4,
Min: 45 * time.Second,
Max: 60 * time.Second,
},
{
Duration: 0 * time.Second,
Buffer: 0 * time.Second,
Jitter: 0,
Min: 0 * time.Second,
Max: 0 * time.Second,
},
{
Duration: 60 * time.Second,
Buffer: 120 * time.Second,
Jitter: 8,
Min: -1 * (52*time.Second + 500*time.Millisecond),
Max: -1 * 60 * time.Second,
},
}
for _, test := range tests {
min, max := DurationMinusBufferDomain(test.Duration, test.Buffer, test.Jitter)
if min != test.Min {
t.Errorf("Bad min: %v != %v", min, test.Min)
}
if max != test.Max {
t.Errorf("Bad max: %v != %v", max, test.Max)
} }
} }
} }