-
Notifications
You must be signed in to change notification settings - Fork 156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add retry logic to client's Validate method #150
Conversation
5132778
to
9fa6542
Compare
In order to get around Datadog's API throwing 503 errors with some regularity, adds retry logic for retryable errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of general curiosity, have you looked at https://github.com/hashicorp/go-retryablehttp ? I haven't used it but it looks pretty clean and might fit the use case here.
client.go
Outdated
|
||
defer resp.Body.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not defer closing resp.Body
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was getting errors in unmarshaling JSON during testing, I think it was because I had it in the wrong scope. I've added it back.
I used the expontential backoff retrier package being used in |
Fair enough, that is indeed what we used before. Just stumbled upon go-retryablehttp and thought it looked neat. |
Yeah, for sure, looks like it could make the code significantly easier to reason about. I can take a crack at migrating to it after this is merged, if you'd like. |
Ok this is entertaining; I wrote the initial validate() method, and should have used Would you mind refactoring this to use Re: using |
I updated my branch to use the |
@ojongerius Is there any additional testing you'd like me to do here? Am I missing anything? |
Thanks @GrantSheehan was out due to illness. I'll have a gander tomorrow morning (NZST, UTC+12)! |
Sorry to be a pain, this has been a thorn in our side for a while now. Having finally nailed down a root cause and a fix, I'll admit to being a bit eager to get a fix merged. Glad you're feeling better! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Less code, I love it!
client.go
Outdated
|
||
var resp *http.Response | ||
resp, err = client.HttpClient.Do(req) | ||
resp, err = client.doRequestWithRetries(req, maxTime) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still needs a defer to close the response.
} | ||
return false, fmt.Errorf("API error %s: %s", resp.Status, body) | ||
} | ||
|
||
body, err := ioutil.ReadAll(resp.Body) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please check the value of err
client.go
Outdated
} | ||
return false, fmt.Errorf("API error %s: %s", resp.Status, body) | ||
} | ||
|
||
body, err := ioutil.ReadAll(resp.Body) | ||
err = json.Unmarshal(body, &out) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want extra brownie points, improving my code, this could be something like:
if err = json.Unmarshal(body, &out); err != nil {
return false, err
}
Almost there! 🥇 |
client.go
Outdated
return false, err | ||
} | ||
return false, fmt.Errorf("API error %s: %s", resp.Status, body) | ||
if body, err := ioutil.ReadAll(resp.Body); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this will work, as we'll need body
later on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgive my golang ignorance, how does doing it in the if
change the logic in a way that would effect the content of body
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To answer my own question, it seems to be the :=
inline declaration/assignment operator. TIL.
client.go
Outdated
body, err := ioutil.ReadAll(resp.Body) | ||
err = json.Unmarshal(body, &out) | ||
if err != nil { | ||
if err = json.Unmarshal(body, &out); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
client.go
Outdated
return false, err | ||
} | ||
|
||
defer resp.Body.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move the defer up to just after checking if doRequestWithRetries
return an error, I think below line 82.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my last comments, very close!
Yep, no problem. I'm still trying to grok some of golang's idioms/syntax, be as nit-picky as you want to be. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one error check and we should be good!
client.go
Outdated
if bodyreader != nil { | ||
req.Header.Add("Content-Type", "application/json") | ||
} | ||
req, err := http.NewRequest("GET", uri, nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still needs checking if we got an error here.
@GrantSheehan btw, you were absolutely right about inline error checking. I stand corrected!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉
Nice work @GrantSheehan! Can you confirm you've verified your changes work for you locally? Is there anything we need to check or can improve before we merge this? |
I've been testing by vendoring and installing my forked repo/branch when compiling the Datadog Terraform provider and have had no issues. If there's anything you'd like me to do in addition to that, let me know. |
Thanks @GrantSheehan! I'll cut a release for you so you can pull those changes into the Terraform provider. |
I've cut release 2.8.4. |
Thank you! |
In order to get around Datadog's API throwing 503 errors with some regularity, adds retry logic for retryable errors.
Background:
I use the Datadog Terraform provider and it's written in a way that most of the failed requests will be retried without much issue, but refreshing the credentials on the provider itself will run the
Validate()
method, which is currently not able to be retried. As a result, a respectable percentage of our CI/CD builds end up failing due to a returned 503. This seemed like the most pragmatic way to resolve some of those errors (as Datadog's support has been somewhat less than helpful in resolving the cause of the 503's themselves, cough). If you don't like the way it's implemented here, just let me know what changes you'd like made I'll update it.Thanks!