Merge pull request #1 from Danwakefield/master

Add Jitter to backoff.
This commit is contained in:
Jaime Pillora 2015-03-07 13:26:28 +11:00
commit 886d12305b
3 changed files with 73 additions and 1 deletions

View File

@ -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)
@ -97,4 +139,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -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

View File

@ -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)