176 lines
3.7 KiB
Go
176 lines
3.7 KiB
Go
package backoff
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// If given New with 0's and no jitter, ensure that certain invariants are met:
|
|
//
|
|
// - the default max duration and interval should be used
|
|
// - noJitter should be true
|
|
// - the RNG should not be initialised
|
|
// - the first duration should be equal to the default interval
|
|
func TestDefaults(t *testing.T) {
|
|
b := NewWithoutJitter(0, 0)
|
|
|
|
if b.maxDuration != DefaultMaxDuration {
|
|
t.Fatalf("expected new backoff to use the default max duration (%s), but have %s", DefaultMaxDuration, b.maxDuration)
|
|
}
|
|
|
|
if b.interval != DefaultInterval {
|
|
t.Fatalf("exepcted new backoff to use the default interval (%s), but have %s", DefaultInterval, b.interval)
|
|
}
|
|
|
|
if b.noJitter != true {
|
|
t.Fatal("backoff should have been initialised without jitter")
|
|
}
|
|
|
|
dur := b.Duration()
|
|
if dur != DefaultInterval {
|
|
t.Fatalf("expected first duration to be %s, have %s", DefaultInterval, dur)
|
|
}
|
|
}
|
|
|
|
// Given a zero-value initialised Backoff, it should be transparently
|
|
// setup.
|
|
func TestSetup(t *testing.T) {
|
|
b := new(Backoff)
|
|
dur := b.Duration()
|
|
if dur < 0 || dur > (5*time.Minute) {
|
|
t.Fatalf("want duration between 0 and 5 minutes, have %s", dur)
|
|
}
|
|
}
|
|
|
|
// Ensure that tries incremenets as expected.
|
|
func TestTries(t *testing.T) {
|
|
b := NewWithoutJitter(5, 1)
|
|
|
|
for i := uint64(0); i < 3; i++ {
|
|
if b.n != i {
|
|
t.Fatalf("want tries=%d, have tries=%d", i, b.n)
|
|
}
|
|
|
|
pow := 1 << i
|
|
expected := time.Duration(pow)
|
|
dur := b.Duration()
|
|
if dur != expected {
|
|
t.Fatalf("want duration=%d, have duration=%d at i=%d", expected, dur, i)
|
|
}
|
|
}
|
|
|
|
for i := uint(3); i < 5; i++ {
|
|
dur := b.Duration()
|
|
if dur != 5 {
|
|
t.Fatalf("want duration=5, have %d at i=%d", dur, i)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ensure that a call to Reset will actually reset the Backoff.
|
|
func TestReset(t *testing.T) {
|
|
const iter = 10
|
|
b := New(1000, 1)
|
|
for i := 0; i < iter; i++ {
|
|
_ = b.Duration()
|
|
}
|
|
|
|
if b.n != iter {
|
|
t.Fatalf("expected tries=%d, have tries=%d", iter, b.n)
|
|
}
|
|
|
|
b.Reset()
|
|
if b.n != 0 {
|
|
t.Fatalf("expected tries=0 after reset, have tries=%d", b.n)
|
|
}
|
|
}
|
|
|
|
const decay = 5 * time.Millisecond
|
|
const max = 10 * time.Millisecond
|
|
const interval = time.Millisecond
|
|
|
|
func TestDecay(t *testing.T) {
|
|
const iter = 10
|
|
|
|
b := NewWithoutJitter(max, 1)
|
|
b.SetDecay(decay)
|
|
|
|
var backoff time.Duration
|
|
for i := 0; i < iter; i++ {
|
|
backoff = b.Duration()
|
|
}
|
|
|
|
if b.n != iter {
|
|
t.Fatalf("expected tries=%d, have tries=%d", iter, b.n)
|
|
}
|
|
|
|
// Don't decay below backoff
|
|
b.lastTry = time.Now().Add(-backoff + 1)
|
|
backoff = b.Duration()
|
|
if b.n != iter+1 {
|
|
t.Fatalf("expected tries=%d, have tries=%d", iter+1, b.n)
|
|
}
|
|
|
|
// Reset after backoff + decay
|
|
b.lastTry = time.Now().Add(-backoff - decay)
|
|
b.Duration()
|
|
if b.n != 1 {
|
|
t.Fatalf("expected tries=%d, have tries=%d", 1, b.n)
|
|
}
|
|
}
|
|
|
|
// Ensure that decay works even if the retry counter is saturated.
|
|
func TestDecaySaturation(t *testing.T) {
|
|
b := NewWithoutJitter(1<<2, 1)
|
|
b.SetDecay(decay)
|
|
|
|
var duration time.Duration
|
|
for i := 0; i <= 2; i++ {
|
|
duration = b.Duration()
|
|
}
|
|
|
|
if duration != 1<<2 {
|
|
t.Fatalf("expected duration=%v, have duration=%v", 1<<2, duration)
|
|
}
|
|
|
|
b.lastTry = time.Now().Add(-duration - decay)
|
|
b.n = math.MaxUint64
|
|
|
|
duration = b.Duration()
|
|
if duration != 1 {
|
|
t.Errorf("expected duration=%v, have duration=%v", 1, duration)
|
|
}
|
|
}
|
|
|
|
func ExampleBackoff_SetDecay() {
|
|
b := NewWithoutJitter(max, interval)
|
|
b.SetDecay(decay)
|
|
|
|
// try 0
|
|
fmt.Println(b.Duration())
|
|
|
|
// try 1
|
|
fmt.Println(b.Duration())
|
|
|
|
// try 2
|
|
duration := b.Duration()
|
|
fmt.Println(duration)
|
|
|
|
// try 3, below decay
|
|
time.Sleep(duration)
|
|
duration = b.Duration()
|
|
fmt.Println(duration)
|
|
|
|
// try 4, resets
|
|
time.Sleep(duration + decay)
|
|
fmt.Println(b.Duration())
|
|
|
|
// Output: 1ms
|
|
// 2ms
|
|
// 4ms
|
|
// 8ms
|
|
// 1ms
|
|
}
|