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

Automatic request retries #428

Merged
merged 2 commits into from
Feb 7, 2018
Merged

Automatic request retries #428

merged 2 commits into from
Feb 7, 2018

Conversation

ob-stripe
Copy link
Contributor

r? @brandur-stripe
cc @stripe/api-libraries

Fixes #394.

Implements automatic request retries, a la stripe-ruby.

As discussed IRL, I'm not totally happy with this implementation as it's not possible to fully test it -- we would need to be able to mock curl_exec() and other curl functions to do so, which isn't really possible in PHP.

Ideally, this feature should be implemented in ApiRequestor and not directly in CurlClient, but doing so would require substantial changes to the HttpClient interface -- for starters, we'd need to have a standard way of raising connection errors so that ApiRequestor could decide whether to retry or not. (Right now CurlClient just raises an Error\ApiConnection exception for all connection errors.)

@timbroder
Copy link

This is awesome! I'll be able to remove some home-grown code that does this in app land

$headers['Idempotency-Key'] = bin2hex(openssl_random_pseudo_bytes(32));
}
}

Copy link

Choose a reason for hiding this comment

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

I think you meant "PUT" here and not "DELETE"?
Your own documentation says it's not necessary for DELETE.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I cargo-culted the implementation from stripe-ruby, but you're absolutely correct: idempotency does not apply to DELETE requests.

The API does not use PUT requests at all, so POST is the only method that can use idempotency. I'll update the PR. Thanks 👍

Copy link

Choose a reason for hiding this comment

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

Don't forget to update the comment too.

@ob-stripe ob-stripe force-pushed the ob-retry-requests branch 2 times, most recently from c1e2a0c to 1ff2463 Compare February 5, 2018 00:02
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.

Doh. Sorry about the delay here @ob-stripe! I just found these two comments that I left literally a week ago, but just forgot to submit the review. Overall, looks great, thanks!


namespace Stripe\Util;

class RandomGenerator
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe add a note in here that this exists for testability by using it with dependency injection.


private function setMaxNetworkRetryDelay($maxNetworkRetryDelay)
{
$property = $this->getPrivateProperty('Stripe\Stripe', 'maxNetworkRetryDelay');
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO, it might be going a little too far by adding helpers to access private members for this. After these are available, the tendency will be to use them more often, and IMO it's not great practice to reach into the private parts of any implementation — even if it's your own.

I don't have an amazing alternate suggestion, but I think it would be better even to just move the helpers back into this test suite so that they're not generalized in TestCase. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, that makes sense. I've removed the helpers from TestCase and the various reflectors are now directly instantiated in CurlClientTest.setUpReflectors.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks!

if ((($method == 'post') || ($method == 'delete')) &&
(Stripe::$maxNetworkRetries > 0)) {
if (!isset($headers['Idempotency-Key'])) {
$headers['Idempotency-Key'] = bin2hex(openssl_random_pseudo_bytes(32));
Copy link
Contributor

Choose a reason for hiding this comment

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

Just going to say that it's kind of weird to have a $randomGenerator injected via dependency, use it in one place, but then use a different method for generating randomness elsewhere in the same class :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. I've actually already updated RandomGenerator with a uuid method that returns a real V4 UUID since that's the best practice recommended in our documentation (and what's done in stripe-ruby too).

Copy link
Contributor

Choose a reason for hiding this comment

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

Nice! Love the UUID generator.

@ob-stripe
Copy link
Contributor Author

ptal @brandur-stripe

@brandur-stripe
Copy link
Contributor

LGTM.

🚢

@ob-stripe ob-stripe merged commit a282638 into integration-v6 Feb 7, 2018
@ob-stripe ob-stripe deleted the ob-retry-requests branch February 7, 2018 22:38
@ob-stripe ob-stripe mentioned this pull request Feb 7, 2018
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants