4
\$\begingroup\$

I believe that clean and elegant retry code is notoriously difficult, so I'm wondering how the following code could be improved. The retryGet function should have the same interface and contract as http.Get, except for the number of attempts argument, of course.

func retryGet(url string, maxAttempts int) (*http.Response, error) {
 attempts := 0
 if maxAttempts < 1 {
 return nil, errors.New("maxAttempts must be at least 1")
 }
 for {
 attempts++
 response, err := http.Get(url)
 if err == nil && response.StatusCode == http.StatusOK {
 return response, nil
 }
 delay, retry := shouldRetry(maxAttempts, attempts, response)
 if !retry {
 return response, err
 }
 if err == nil {
 defer response.Body.Close()
 }
 time.Sleep(delay)
 }
}
func shouldRetry(maxAttempts, attempts int, response *http.Response) (time.Duration, bool) {
 if attempts >= maxAttempts {
 return time.Duration(0), false
 }
 delay := time.Duration(attempts) * time.Second
 if response != nil && response.Header.Get("Retry-After") != "" {
 after, err := strconv.ParseInt(response.Header.Get("Retry-After"), 10, 64)
 if err != nil && after > 0 {
 delay = time.Duration(after) * time.Second
 }
 }
 return delay, true
}
asked Aug 20, 2017 at 16:10
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

I would recommend you to actually strictly match the http.Get signature using a struct:

type Getter func(url string) (*http.Response, error)
type RetryingClient struct {
 Getter Getter
 MaxAttempts int
}
func (rc RetryingClient) Get(url string) (*http.Response, error) {
 ...
}

And instead of directly calling http.Get, the embedded Getter interface gives you much more flexibility:

  • for testing
  • for using a proxy

Why do you defer the response.Body.Close() ? Since it won't be use, you can close it immediately.


Using the interface, you could make a function embedded in the struct to compute a default delay instead of hard-coded time.Duration(attempts) * time.Second. For instance: defaultDelay(attempt int) time.Duration

answered Aug 24, 2017 at 14:44
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.