mirror of https://github.com/jpillora/backoff.git
Add Jitter to backoff.
Amazon recently wrote about performance gains of using jitter in concurrent systems. http://www.awsarchitectureblog.com/2015/03/backoff.html Seemed simple enough to add and wont change any code that may already be using Backoff.
This commit is contained in:
parent
ff2ca325ba
commit
491389a93e
42
README.md
42
README.md
|
@ -25,6 +25,7 @@ b := &backoff.Backoff{
|
||||||
Min: 100 * time.Millisecond,
|
Min: 100 * time.Millisecond,
|
||||||
Max: 10 * time.Second,
|
Max: 10 * time.Second,
|
||||||
Factor: 2,
|
Factor: 2,
|
||||||
|
Jitter: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s\n", b.Duration())
|
fmt.Printf("%s\n", b.Duration())
|
||||||
|
@ -72,6 +73,47 @@ for {
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Exmaple using `Jitter`**
|
||||||
|
|
||||||
|
Setting `Jitter` adds some randomization to the backoff durations.
|
||||||
|
[See amazon's writeup of performance gains using jitter](http://www.awsarchitectureblog.com/2015/03/backoff.html).
|
||||||
|
Seeding is not necessary but doing so gives repeatable results.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "math/rand"
|
||||||
|
|
||||||
|
b := &backoff.Backoff{
|
||||||
|
//These are the defaults
|
||||||
|
Min: 100 * time.Millisecond,
|
||||||
|
Max: 10 * time.Second,
|
||||||
|
Factor: 2,
|
||||||
|
Jitter: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
rand.Seed(42)
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", b.Duration())
|
||||||
|
fmt.Printf("%s\n", b.Duration())
|
||||||
|
fmt.Printf("%s\n", b.Duration())
|
||||||
|
|
||||||
|
fmt.Printf("Reset!\n")
|
||||||
|
b.Reset()
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", b.Duration())
|
||||||
|
fmt.Printf("%s\n", b.Duration())
|
||||||
|
fmt.Printf("%s\n", b.Duration())
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
100ms
|
||||||
|
106.600049ms
|
||||||
|
281.228155ms
|
||||||
|
Reset!
|
||||||
|
100ms
|
||||||
|
104.381845ms
|
||||||
|
214.957989ms
|
||||||
|
```
|
||||||
|
|
||||||
#### Credits
|
#### Credits
|
||||||
|
|
||||||
Ported from some JavaScript written by [@tj](https://github.com/tj)
|
Ported from some JavaScript written by [@tj](https://github.com/tj)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package backoff
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,6 +13,8 @@ import (
|
||||||
type Backoff struct {
|
type Backoff struct {
|
||||||
//Factor is the multiplying factor for each increment step
|
//Factor is the multiplying factor for each increment step
|
||||||
attempts, Factor float64
|
attempts, Factor float64
|
||||||
|
//Jitter eases contention by randomizing backoff steps
|
||||||
|
Jitter bool
|
||||||
//Min and Max are the minimum and maximum values of the counter
|
//Min and Max are the minimum and maximum values of the counter
|
||||||
Min, Max time.Duration
|
Min, Max time.Duration
|
||||||
}
|
}
|
||||||
|
@ -32,6 +35,9 @@ func (b *Backoff) Duration() time.Duration {
|
||||||
}
|
}
|
||||||
//calculate this duration
|
//calculate this duration
|
||||||
dur := float64(b.Min) * math.Pow(b.Factor, b.attempts)
|
dur := float64(b.Min) * math.Pow(b.Factor, b.attempts)
|
||||||
|
if b.Jitter == true {
|
||||||
|
dur = rand.Float64()*(dur-float64(b.Min)) + float64(b.Min)
|
||||||
|
}
|
||||||
//cap!
|
//cap!
|
||||||
if dur > float64(b.Max) {
|
if dur > float64(b.Max) {
|
||||||
return b.Max
|
return b.Max
|
||||||
|
|
|
@ -50,6 +50,30 @@ func Test3(t *testing.T) {
|
||||||
equals(t, b.Duration(), 100*time.Nanosecond)
|
equals(t, b.Duration(), 100*time.Nanosecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestJitter(t *testing.T) {
|
||||||
|
b := &Backoff{
|
||||||
|
Min: 100 * time.Millisecond,
|
||||||
|
Max: 10 * time.Second,
|
||||||
|
Factor: 2,
|
||||||
|
Jitter: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
equals(t, b.Duration(), 100*time.Millisecond)
|
||||||
|
between(t, b.Duration(), 100*time.Millisecond, 200*time.Millisecond)
|
||||||
|
between(t, b.Duration(), 100*time.Millisecond, 400*time.Millisecond)
|
||||||
|
b.Reset()
|
||||||
|
equals(t, b.Duration(), 100*time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func between(t *testing.T, actual, low, high time.Duration) {
|
||||||
|
if actual < low {
|
||||||
|
t.Fatalf("Got %s, Expecting >= %s", actual, low)
|
||||||
|
}
|
||||||
|
if actual > high {
|
||||||
|
t.Fatalf("Got %s, Expecting <= %s", actual, high)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func equals(t *testing.T, d1, d2 time.Duration) {
|
func equals(t *testing.T, d1, d2 time.Duration) {
|
||||||
if d1 != d2 {
|
if d1 != d2 {
|
||||||
t.Fatalf("Got %s, Expecting %s", d1, d2)
|
t.Fatalf("Got %s, Expecting %s", d1, d2)
|
||||||
|
|
Loading…
Reference in New Issue