Skip to content
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

Retry based on Stripe-Should-Retry and Retry-After headers #692

Merged
merged 6 commits into from
Sep 18, 2019

Conversation

rattrayalex-stripe
Copy link
Contributor

r? @ob-stripe
cc @stripe/api-libraries @paulasjes-stripe @jlomas-stripe

This adds support for the Stripe-Should-Retry and Retry-After headers.

This allows the API to specify specific cases when an automatic retry is recommended (such as lock_timeout errors) or not recommended (such as when our idempotency framework would replay the request anyway).

It also allows the API to encourage slower retries.

lib/StripeResource.js Outdated Show resolved Hide resolved
Copy link
Contributor

@paulasjes-stripe paulasjes-stripe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor nits, but otherwise 🚢 🇮🇹

lib/StripeResource.js Show resolved Hide resolved
Copy link
Contributor

@ob-stripe ob-stripe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

cc @brandur-stripe to check the logic and make sure it's the one we want to apply to all client libraries.

Copy link
Contributor

@brandur-stripe brandur-stripe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks good.

ptal @rattrayalex-stripe

@@ -228,21 +228,31 @@ StripeResource.prototype = {
return true;
}

// Retry on conflict and availability errors.
if (res.statusCode === 409 || res.statusCode === 503) {
if (res.headers && res.headers['stripe-should-retry'] === 'false') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we put some docs on this to describe generally what Stripe-Should-Retry is?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, will do

if (res.headers && res.headers['stripe-should-retry'] === 'false') {
return false;
}
if (res.headers && res.headers['stripe-should-retry'] === 'true') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the interaction between Stripe-Should-Retry and Retry-After work?

I guess based on this condition being placed so early, if we're going to send Retry-After, then Stripe-Should-Retry is set to false or not included? If so, doesn't that mean we have to fall back on other heuristics to decide whether to retry (like status code)?

I wonder if we should have it so that if there's a Retry-After, Stripe-Should-Retry is still set to an appropriate value that indicates whether a retry is appropriate. If we went down that route, we'd have to include a && !res.headers['stripe-retry-after'] in this check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we're going to send Retry-After, then Stripe-Should-Retry is set to false or not included?

I assume you meant "true" not "false, and yes: I can share the internal doc, but our API should hold the invariant that if Retry-After is set, Stripe-Should-Retry should not be set to false.

Were that to happen, though, I think Stripe-Should-Retry: false should take precedence and block a Retry-After: X, given that the likely intent of the latter is to ask clients to wait a little longer before retrying, and the intent of the former is to ask them not to retry at all (eg; because it will be futile or damaging).

In general, they're pretty orthogonal; technically, these should be separate PR's (I'm just lumping them together for convenience, since they came to mind at the same time).

Note that I think we'll ~always want to fall back to status code checks for at least some cases (eg; 500, 503, 504) in case the parts of our api stack that set Stripe-Should-Retry are not hit.

I could see a case for dropping the code for 409 handling here and moving that into the API...

However, in general, we should expect the client to retry in some cases that the server does not make a recommendation, for example, 429's, where we do not retry but the client may choose to (but would hopefully respect Retry-After).

@@ -261,6 +271,11 @@ StripeResource.prototype = {
// But never sleep less than the base sleep seconds.
sleepSeconds = Math.max(initialNetworkRetryDelay, sleepSeconds);

// And never sleep less than the time the API asks us to wait, assuming it's a reasonable ask.
if (Number.isInteger(retryAfter) && retryAfter <= 60) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we constant-ize 60 here to make it more literate? Maybe MAX_RETRY_AFTER_WAIT?

@rattrayalex-stripe
Copy link
Contributor Author

Self-approving after adopting recommended changes

@rattrayalex-stripe rattrayalex-stripe deleted the ralex/smarter-retries branch September 18, 2019 00:13
brandur-stripe pushed a commit to stripe/stripe-ruby that referenced this pull request Sep 20, 2019
As seen in stripe-node, adds support for the `Stripe-Should-Retry`
header which is sent by the API when it explicitly would like us to
either retry or _not_ retry.

I'll add `Retry-After` separately at some point, but I punted it on it
for now given that we're not using it yet.

See: stripe/stripe-node#692
brandur-stripe pushed a commit to stripe/stripe-go that referenced this pull request Sep 20, 2019
As seen in stripe-node, adds support for the `Stripe-Should-Retry`
header which is sent by the API when it explicitly would like us to
either retry or _not_ retry.

I'll add `Retry-After` separately at some point, but I punted it on it
for now given that we're not using it yet.

See: stripe/stripe-node#692
brandur-stripe pushed a commit to stripe/stripe-ruby that referenced this pull request Sep 20, 2019
As seen in stripe-node, adds support for the `Stripe-Should-Retry`
header which is sent by the API when it explicitly would like us to
either retry or _not_ retry.

I'll add `Retry-After` separately at some point, but I punted it on it
for now given that we're not using it yet.

See: stripe/stripe-node#692
brandur-stripe pushed a commit to stripe/stripe-ruby that referenced this pull request Oct 1, 2019
As seen in stripe-node, adds support for the `Stripe-Should-Retry`
header which is sent by the API when it explicitly would like us to
either retry or _not_ retry.

I'll add `Retry-After` separately at some point, but I punted it on it
for now given that we're not using it yet.

See: stripe/stripe-node#692
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants