Skip to content

Instantly share code, notes, and snippets.

@jonasfj
Created February 24, 2016 19:17
Show Gist options
  • Save jonasfj/4c88d69a0e23214d3321 to your computer and use it in GitHub Desktop.
Save jonasfj/4c88d69a0e23214d3321 to your computer and use it in GitHub Desktop.
// Create a new object that doesn't have anything modifed...
type BackOffSettings {
InitialInterval time.Duration
RandomizationFactor float64
Multiplier float64
MaxInterval time.Duration
MaxElapsedTime time.Duration
}
type Client struct {
BackOffSettings
Client *http.Client
IsTransient func(error) bool
}
// Let's embed http.Response, creating a duplicate doesn't seem like a lot of
// work, it's a small struct so copying it and when retry is fairly sane.
type Response struct {
http.Response
// Attempts to make a successful request
Attempts int
// If readAll was given this will be populated with the response payload
Payload []byte
}
func defaultIsTransient(err error) bool {
// return true, if err is 5xx
}
/** Create a new */
func New(client *http.Client, settings BackOffSettings, isTransient func(error) bool) *Client {
if client == nil {
client = http.DefaultClient
}
if settings == nil {
settings = //default value...
}
if isTransient == nil {
isTransient = defaultIsTransient
}
return &Client{
InitialInterval: settings.InitialInterval,
...
Client: client,
IsTransient: isTransient,
}
}
func (c *Client) Retry(operation func() (*Response, error), readAll bool) (*Response, error) {
persistentError := error(nil)
response := Response{}
err := backoff.Retry(func() error {
response.Attempts++
resp, err := operation()
if err != nil {
if c.IsTransient(err) {
return err
} else {
persistentError = err
return nil
}
}
response = *resp
if !readAll {
return nil
}
payload, err := ioutil.ReadAll(resp.Body)
closeErr := res.Body.close()
response.Payload = payload
if err != nil {
if c.IsTransient(err) {
return err
} else {
persistentError = err
return nil
}
}
if closeErr != nil {
if c.IsTransient(closeErr) {
return closeErr
} else {
persistentError = closeErr
return nil
}
}
}, backoff.ExponentialBackOff{
InitialInterval: c.InitialInterval,
...
})
if err != nil {
return nil, err
}
if persistentError != nil {
return nil, persistentError
}
return response, nil
}
func (c *Client) Do(req *Request) (*Response, error) {
return c.Retry(func() (*http.Response, error) {
return c.client.Do(req)
}, false);
}
func (c *Client) Get(url string) (*Response, error) {
...
}
func (c *Client) Head(url string) (*Response, error) {
...
}
func (c *Client) DoReadAll(req *Request) (*Response, error) {
return c.Retry(func() (*http.Response, error) {
return c.client.Do(req)
}, true);
}
// ------------- Usage example
client := httpretry.New(nil, nil, nil)
resp, err := client.DoReadAll(req)
if err != nil {
panic(err)
}
err = json.Unmarshal(resp.Payload, &payload)
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment