1
0
Fork 0
mirror of https://github.com/imjasonh/staticmaps synced 2026-07-08 19:05:09 +00:00

Add backoff http.RoundTripper that that handles exponential backoff

This commit is contained in:
Jason Hall 2014-10-09 13:24:52 -04:00
parent 41f9b8b763
commit f9c0e3df19
3 changed files with 85 additions and 1 deletions

47
backoff.go Normal file
View file

@ -0,0 +1,47 @@
package maps
import (
"math/rand"
"net/http"
"time"
)
type backoff struct {
Transport *http.RoundTripper
MaxTries int
tries int
sleep func(time.Duration)
}
func (bt *backoff) RoundTrip(req *http.Request) (*http.Response, error) {
if bt.Transport == nil {
bt.Transport = &http.DefaultTransport
}
if bt.MaxTries == 0 {
bt.MaxTries = 5
}
if bt.sleep == nil {
bt.sleep = time.Sleep
}
wait := time.Second
var err error
for ; bt.tries < bt.MaxTries; bt.tries++ {
var resp *http.Response
resp, err = (*bt.Transport).RoundTrip(req)
if err != nil {
// fallthrough, retry
} else if resp.StatusCode >= http.StatusInternalServerError {
err = HTTPError{resp}
} else {
return resp, nil
}
if bt.tries == bt.MaxTries-1 {
// last try failed, just give up
continue
}
bt.sleep(wait)
jitter := time.Duration(rand.Intn(1000))*time.Millisecond - 500*time.Millisecond // jitter is +/- .5s
wait = wait*2 + jitter
}
return nil, err
}

35
backoff_test.go Normal file
View file

@ -0,0 +1,35 @@
package maps
import (
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestBackoff(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "fail", http.StatusInternalServerError)
}))
defer s.Close()
tries := 10
sleeps := 0
bt := &backoff{
MaxTries: tries,
sleep: func(d time.Duration) {
sleeps += 1
t.Logf("sleeping %s", d)
},
}
c := &http.Client{Transport: bt}
r, _ := http.NewRequest("GET", s.URL, nil)
if _, err := c.Do(r); err == nil {
t.Errorf("expected error")
}
if sleeps != tries-1 {
t.Errorf("unexpected # of calls to sleep, got %d, want %d", tries-1, sleeps)
}
if bt.tries != tries {
t.Errorf("got %d tries, want %d", bt.tries, tries)
}
}

View file

@ -23,7 +23,9 @@ func NewWorkClient(clientID, signature string) Client {
}
func (c Client) do(url string) (*http.Response, error) {
cl := &http.Client{Transport: c.Transport}
cl := &http.Client{Transport: &backoff{
Transport: &c.Transport,
}}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err