Merge pull request #129 from jonboulle/exp

fix(pkg): simplify exponential backoff to avoid overflows
This commit is contained in:
Jonathan Boulle 2014-05-29 14:02:47 -07:00
commit cd322863e9
2 changed files with 32 additions and 14 deletions

View File

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"math"
"net" "net"
"net/http" "net/http"
neturl "net/url" neturl "net/url"
@ -43,6 +42,14 @@ func NewHttpClient() *HttpClient {
} }
} }
func expBackoff(interval, max time.Duration) time.Duration {
interval = interval * 2
if interval > max {
interval = max
}
return interval
}
// Fetches a given URL with support for exponential backoff and maximum retries // Fetches a given URL with support for exponential backoff and maximum retries
func (h *HttpClient) Get(rawurl string) ([]byte, error) { func (h *HttpClient) Get(rawurl string) ([]byte, error) {
if rawurl == "" { if rawurl == "" {
@ -84,6 +91,7 @@ func (h *HttpClient) Get(rawurl string) ([]byte, error) {
Transport: transport, Transport: transport,
} }
duration := 50 * time.Millisecond
for retry := 1; retry <= h.MaxRetries; retry++ { for retry := 1; retry <= h.MaxRetries; retry++ {
log.Printf("Fetching data from %s. Attempt #%d", dataURL, retry) log.Printf("Fetching data from %s. Attempt #%d", dataURL, retry)
@ -106,13 +114,8 @@ func (h *HttpClient) Get(rawurl string) ([]byte, error) {
log.Printf("Unable to fetch data: %s", err.Error()) log.Printf("Unable to fetch data: %s", err.Error())
} }
duration := time.Millisecond * time.Duration((math.Pow(float64(2), float64(retry)) * 100)) duration = expBackoff(duration, h.MaxBackoff)
if duration > h.MaxBackoff {
duration = h.MaxBackoff
}
log.Printf("Sleeping for %v...", duration) log.Printf("Sleeping for %v...", duration)
time.Sleep(duration) time.Sleep(duration)
} }

View File

@ -3,11 +3,30 @@ package pkg
import ( import (
"fmt" "fmt"
"io" "io"
"math"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"time"
) )
func TestExpBackoff(t *testing.T) {
duration := time.Millisecond
max := time.Hour
for i := 0; i < math.MaxUint16; i++ {
duration = expBackoff(duration, max)
if duration < 0 {
t.Fatalf("duration too small: %v %v", duration, i)
}
if duration > max {
t.Fatalf("duration too large: %v %v", duration, i)
}
}
}
// Test exponential backoff and that it continues retrying if a 5xx response is
// received
func TestGetURLExpBackOff(t *testing.T) {
var expBackoffTests = []struct { var expBackoffTests = []struct {
count int count int
body string body string
@ -16,10 +35,6 @@ var expBackoffTests = []struct {
{1, "number of attempts: 1"}, {1, "number of attempts: 1"},
{2, "number of attempts: 2"}, {2, "number of attempts: 2"},
} }
// Test exponential backoff and that it continues retrying if a 5xx response is
// received
func TestGetURLExpBackOff(t *testing.T) {
client := NewHttpClient() client := NewHttpClient()
for i, tt := range expBackoffTests { for i, tt := range expBackoffTests {