forked from mirror/backoff
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,
|
||||
Max: 10 * time.Second,
|
||||
Factor: 2,
|
||||
Jitter: false,
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
Ported from some JavaScript written by [@tj](https://github.com/tj)
|
||||
|
|
|
@ -2,6 +2,7 @@ package backoff
|
|||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -12,6 +13,8 @@ import (
|
|||
type Backoff struct {
|
||||
//Factor is the multiplying factor for each increment step
|
||||
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, Max time.Duration
|
||||
}
|
||||
|
@ -32,6 +35,9 @@ func (b *Backoff) Duration() time.Duration {
|
|||
}
|
||||
//calculate this duration
|
||||
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!
|
||||
if dur > float64(b.Max) {
|
||||
return b.Max
|
||||
|
|
|
@ -50,6 +50,30 @@ func Test3(t *testing.T) {
|
|||
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) {
|
||||
if d1 != d2 {
|
||||
t.Fatalf("Got %s, Expecting %s", d1, d2)
|
||||
|
|
Loading…
Reference in New Issue