diff --git a/lib/cluster.go b/lib/cluster.go index 5df39251d..a95232c57 100644 --- a/lib/cluster.go +++ b/lib/cluster.go @@ -10,12 +10,32 @@ import ( // servicing Consul TTL Checks in advance of the TTL. func DurationMinusBuffer(intv time.Duration, buffer time.Duration, jitter int64) time.Duration { 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 } +// 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 func RandomStagger(intv time.Duration) time.Duration { + if intv == 0 { + return 0 + } return time.Duration(uint64(rand.Int63()) % uint64(intv)) } diff --git a/lib/cluster_test.go b/lib/cluster_test.go index acd862f6f..6747a9698 100644 --- a/lib/cluster_test.go +++ b/lib/cluster_test.go @@ -6,17 +6,123 @@ import ( ) func TestDurationMinusBuffer(t *testing.T) { - const ( - buffer = 10 * time.Second - jitter = 16 - ) - intv := 1 * time.Minute - minValue := (intv - buffer) - ((intv - buffer) / jitter) - maxValue := intv - buffer - for i := 0; i < 10; i++ { - d := DurationMinusBuffer(intv, buffer, jitter) - if d < minValue || d > maxValue { - t.Fatalf("Bad: %v", d) + tests := []struct { + Duration time.Duration + Buffer time.Duration + Jitter int64 + }{ + { + Duration: 1 * time.Minute, + Buffer: 10 * time.Second, + Jitter: 16, + }, + { + 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) } } }