-
Notifications
You must be signed in to change notification settings - Fork 150
Improve Retry Behaviour. #146
Conversation
hey @vadimdemedes, @stephenmathieson; wanted to get your thoughts on this. I think the behaviour is functionally correct, but not sure on how to test it. https://github.com/softonic/axios-retry doesn't seem to have tests. My initial idea was to test the custom retry function directly (something simple like |
index.js
Outdated
@@ -249,4 +252,26 @@ class Analytics { | |||
} | |||
} | |||
|
|||
var retryCondition = function (error) { |
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.
Should be an arrow function + const
instead of var
.
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 also might suggest adding this method to the Analytics class so we can more easily write tests for it.
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.
what's an arrow function?
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.
Updated to be const
.
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 also might suggest adding this method to the Analytics class so we can more easily write tests for it.
Why would that be easier? This seems like a stateless function that can be tested without depending on the analytics class.
Also per my understanding, adding it to the analytics class will automatically expose this function to the public API.
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.
In order to test it directly, we'd have to export it. Attaching it directly to exports
(or module.exports
) make it look like part of the public API. If we add it as a method of the class, it will be exposed, but won't look like it's part of the public API.
Consider the following:
// exporting directly
exports.somePrivateMethod = () => {}
// exporting as part of the class
module.exports = class Analytics {
track () {}
somePrivateMethod () {}
}
IMO the first looks like it's exposed for consumption by the end user, while the second just looks like a regular class.
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.
what's an arrow function?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
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.
Thanks! Makes sense about testing, chatted with Roland and he suggested using _ as a prefix to denote it as a private, so I'll do that.
index.js
Outdated
// Retry if rate limited. | ||
if (error.response.status === 429) { | ||
return true | ||
} |
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.
This function should return false
at the end.
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.
Updated.
index.js
Outdated
@@ -249,4 +252,28 @@ class Analytics { | |||
} | |||
} | |||
|
|||
const retryCondition = function (error) { |
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.
const retryCondition = error => {
// ...
}
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.
thanks! roland just explained what this was to me :)
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.
Roland actually suggested not assigning and using it as a function variable at all, which makes sense to me.
function _retryCondition(error) {
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 follow, but either way, we need to expose this logic somehow in order to test it.
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.
Roland actually suggested not assigning and using it as a function variable at all, which makes sense to me.
Why not?
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 me function _retryCondition(error) {
looks cleaner than const _retryCondition = error => {
. 🤷♂️
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.
🚲 🏠
reminder to myself, don't merge without tests! |
Ok, should be ready for review now! Added tests and made some changes from the original approach:
@Rowno showed me how I could make the function static too. It wasn't clear if that was fully compatible with what we support today and if it cause issues, so I left it as a simple class function for now. |
index.js
Outdated
@@ -247,6 +250,30 @@ class Analytics { | |||
done(err) | |||
}) | |||
} | |||
|
|||
_retryCondition (error) { |
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.
Might rename this shouldRetry
or isErrorRetryable
for clarity.
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.
Renamed to isErrorRetryable
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 think you might have forgotten to push?
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.
Oops! Pushed now.
test.js
Outdated
t.false(client._retryCondition({ code: 'ECONNABORTED' })) | ||
|
||
t.true(client._retryCondition({ response: { status: 500 } })) | ||
t.true(client._retryCondition({ response: { status: 429 } })) |
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.
We should add one last test to verify the default return false
behavior.
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.
Added one, though it's pretty contrived; we'd never really expect to see and error for a 2xx response.
c48865a
to
b4dc6ad
Compare
Previously we were only retrying network errors or a 5xx error on an idempotent request (GET, HEAD, OPTIONS, PUT or DELETE). The latter doesn't really guard/apply to us since we use POST for uploading messages. Our POST requests are effectively idempotent since we send messages with a unique message ID that is guarded by our dedupe layer (https://segment.com/blog/exactly-once-delivery/). For us, our retry policy should be (as per https://paper.dropbox.com/doc/analytics-foo-library-guidelines-2trBhLKQD4Soz4VatvuGR#:uid=189560039280582553736180&h2=Handling-Network-Errors): 1. Retry network errors (socket timed out, etc.) 2. Retry server errors (HTTP 5xx) 3. Retry HTTP 429.
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.
LGTM!
Previously we were only retrying network errors or a 5xx error on an idempotent request (GET, HEAD, OPTIONS, PUT or DELETE).
The latter doesn't really guard/apply to us since we use POST for uploading messages. Our POST requests are idempotent since we send messages with a unique message ID that is guarded by our dedupe layer (https://segment.com/blog/exactly-once-delivery/).
For us, our retry policy should be (as per https://paper.dropbox.com/doc/analytics-foo-library-guidelines-2trBhLKQD4Soz4VatvuGR#:uid=189560039280582553736180&h2=Handling-Network-Errors):