Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Added functionality to retry in Invoke-RestMethod and Invoke-WebRequest.#5760

Merged
iSazonov merged 21 commits intoPowerShell:masterfrom
adityapatwardhan:WebCmdletRetry
Jun 29, 2018
Merged

Added functionality to retry in Invoke-RestMethod and Invoke-WebRequest.#5760
iSazonov merged 21 commits intoPowerShell:masterfrom
adityapatwardhan:WebCmdletRetry

Conversation

@adityapatwardhan
Copy link
Member

@adityapatwardhan adityapatwardhan commented Dec 29, 2017

Fixes #5582

PR Summary

The change added two parameters, RetryCount and RetryIntervalSec to enable retry functionality.
When retrying a verbose message is sent out to inform the user.

PR Checklist

Note: Please mark anything not applicable to this PR NA.

Copy link
Contributor

@markekraus markekraus Dec 29, 2017

Choose a reason for hiding this comment

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

Please add a more meaningful summary. Capitalize the first word and end with a period.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Contributor

@markekraus markekraus Dec 29, 2017

Choose a reason for hiding this comment

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

Please add a more meaningful summary. Capitalize the first word and end with a period.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you investigate collapsing this to a single do/while instead of nested?

Copy link
Member

Choose a reason for hiding this comment

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

I think we should have the retry logic in GetResponse() rather than ProcessRecord().

Copy link
Member Author

Choose a reason for hiding this comment

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

Moved logic to GetResponse()

Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably dispose the current HttpResponseMessage before retrying.

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

Copy link
Contributor

Choose a reason for hiding this comment

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

This should move to the end of the previous line.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe it would be better to have RetryCount and RetryIntervalSec set on WebSession in PrepareSession() and then have this area of code retrieve the values from WebSession. It would mean extending WebRequestSession to include new Properties, but I believe that is the correct direction for these settings.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Contributor

Choose a reason for hiding this comment

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

This current logic leads to an interestingly weird combination. If you set -MaximumRedirection 0 -RetryCount 5 -RetryIntervalSec 1 and the response code is 301, this will still retry the same link 5 times because HttpResponseMessage.IsSuccessStatusCode is only true for response codes 200-299 inclusive... not sure how to rectify that.

Copy link
Contributor

Choose a reason for hiding this comment

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

also... since the logic for reducing the current redirection count is handled in GetResponse(), we may need to reset WebSession.MaximumRedirection to the MaximumRedirection property value before continuing.

Copy link
Member

Choose a reason for hiding this comment

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

Best that I can find, the HTTP spec doesn't say enough about rate limiting to make it something we can depend on reliably. However, I think we can say that retry only occurs on a 429 or 403 and document it as such unless there's data showing that web services are using other error codes.

Copy link
Contributor

Choose a reason for hiding this comment

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

@SteveL-MSFT I can see other usecases for this beyond just rate limiting. I could see using this to test 404 errors until a new page comes online after a publish/push and it $x attempts fail alert that the push/publish failed.

Perhaps it we limit it to 400-499 inclusive it would be best. In my experience with APIs I have seen more than 403 and 429 used for busy endpoints (including 404... Reddit... ugh) or rate limiting, but it's always a 400 level error

Copy link
Member

Choose a reason for hiding this comment

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

@markekraus that's a fair point. I think 4xx would be fine.

Copy link
Contributor

Choose a reason for hiding this comment

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

After further consideration, this should work for the following status codes: 304. 400-599 inclusive

304 could be used for monitoring the change of a resource and is not a redirect status even though it in the 300 range. Retries on 5XX errors makes perfect sense to overcome temporary service glitches and cloudflare shenanigans.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Contributor

Choose a reason for hiding this comment

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

See my comment about setting and retrieving RetryIntervalSec and RetryCount from WebSession.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Member

Choose a reason for hiding this comment

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

Perhaps we should default to 1 min instead of 10 secs to be more polite.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Member

Choose a reason for hiding this comment

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

I think we should have the retry logic in GetResponse() rather than ProcessRecord().

Copy link
Member

Choose a reason for hiding this comment

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

Best that I can find, the HTTP spec doesn't say enough about rate limiting to make it something we can depend on reliably. However, I think we can say that retry only occurs on a 429 or 403 and document it as such unless there's data showing that web services are using other error codes.

Copy link
Member

Choose a reason for hiding this comment

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

This won't work once we start localizing the error messages

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't really like this test. I'd rather have a real failure emulated by WebListener. Add a new endpoint which take a session ID (new GUID), a status code, and a fail count. Until the initially provided fail count is reached when using that session ID it produces a response with the provided status code After fail count is reached it returns a normal 200 response. The response body should include the request count for the given session ID. This way we can test how it reacts with various responses and differing fail counts provided by actual endpoint errors instead of relying on fake logic to enter the code path and without relying on verbose messages.

@iSazonov
Copy link
Collaborator

@adityapatwardhan Could you please continue?

@lzybkr lzybkr removed their request for review March 14, 2018 16:35
@iSazonov
Copy link
Collaborator

@adityapatwardhan Please resolve conflicts.

@adityapatwardhan
Copy link
Member Author

I plan to resume work on this next week.

@ffeldhaus
Copy link
Contributor

Did you consider implementing Exponential Backof or different retry strategies? AWS SDKs for instance use an Exponential Backof and it is good practice for many other web services.

@iSazonov
Copy link
Collaborator

iSazonov commented Apr 6, 2018

@ffeldhaus Please open new Issue for your great suggestion - we shouldn't lost this.

@ffeldhaus
Copy link
Contributor

I thought about this feature some more and I don't think it should be included in the current form without further discussion to prevent retries to cause issues or unwanted behavior and I don't think a Pull Request is the right place to discuss the design of this feature. As issue #5582 was already created for this feature, we should first discuss the design there and then continue with the Pull Request.

I will add my concerns and design ideas to issue #5582.

@stale
Copy link

stale bot commented May 6, 2018

This PR has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed if no further activity occurs within 10 days.
Thank you for your contributions.
Community members are welcome to grab these works.

@stale stale bot added the Stale label May 6, 2018
@iSazonov
Copy link
Collaborator

We have #6447 - should we close the Issue?

/cc @SteveL-MSFT @markekraus

@stale stale bot removed the Stale label May 10, 2018
@markekraus
Copy link
Contributor

@iSazonov Retry and Resume are separate features. Resume continues downloading a partially downloaded file. Retry tries the URI again if a non-susccess status code was received. This PR is for the Retry feature, #6447 is for the Resume feature.

@stale
Copy link

stale bot commented Jun 9, 2018

This PR has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed if no further activity occurs within 10 days.
Thank you for your contributions.
Community members are welcome to grab these works.

@stale stale bot added the Stale label Jun 9, 2018
@iSazonov
Copy link
Collaborator

I wouldn't want to lose this.

@adityapatwardhan
Copy link
Member Author

@markekraus Updated test and controller with echo for sessionId.

@markekraus
Copy link
Contributor

@adityapatwardhan Two more things and I think this will be done. I just realized there are only tests for Invoke-Webrequest. Please add the same tests for Invoke-RestMethod. Also, instead of wrapping the tests in #region Retry tests, please use a Context block.

@adityapatwardhan
Copy link
Member Author

@markekraus Since the code path is same, I believe adding all the tests will be duplication. I will add one test for Invoke-RestMethod to cover the scenario.

@markekraus
Copy link
Contributor

@adityapatwardhan The convention for the web cmdlets is to test all features on both, even common features. There is always the possibility that the common features clash with the implementation features.

@adityapatwardhan
Copy link
Member Author

@markekraus Please review again. I have added tests for Invoke-RestMethod. I moved them to a new describe block so they can be grouped together. The describes for other Invoke-RestMethod and Invoke-WebRequest tests were separate. Thanks for the feedback, very much appreciated.

@PaulHigin PaulHigin removed their request for review June 26, 2018 17:44
@markekraus
Copy link
Contributor

@adityapatwardhan there is a Describe "Invoke-WebRequest tests" -Tags "Feature" block and a Describe "Invoke-RestMethod tests" -Tags "Feature" and in those are various context blocks

for example,

Context "Invoke-WebRequest SkipHeaderVerification Tests" {

and

Context "Invoke-RestMethod SkipHeaderVerification Tests" {

@adityapatwardhan
Copy link
Member Author

@markekraus I decided to go with a separate Describe block so that we can use TestCase for It and re-use most of the test code. If we have tests for Invoke-RestMethod and Invoke-WebRequest in separate Describe blocks then there will be test code duplication. I prefer a separate Describe but since there is precedence I am not completely against adding them to existing Describe blocks.

Let me know your thoughts?

@markekraus
Copy link
Contributor

@adityapatwardhan For all new features they have been separate context blocks in the top level describe blocks for each cmdlet. I'd prefer to stick to this pattern for now and look at changing things up in one of the open issues I have on refactoring the code and tests.

@adityapatwardhan
Copy link
Member Author

@markekraus Ok, agreed. I will send an update in a few minutes.

@adityapatwardhan
Copy link
Member Author

@markekraus Made the changes to the tests as discussed above.


Context "Invoke-WebRequest retry tests" {

It "<Command> can retry - <Name>" -TestCases @(
Copy link
Contributor

Choose a reason for hiding this comment

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

<Command> -> Invoke-WebRequest since it's no longer in the testcases hash.

$jsonError.error | Should -BeExactly 'Error: HTTP - 400 occurred.'
}

It "<Command> can retry with POST" {
Copy link
Contributor

Choose a reason for hiding this comment

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

<Command> -> Invoke-WebRequest since it's no longer in the testcases hash.

@adityapatwardhan
Copy link
Member Author

@markekraus Good catch! Fixed.

Copy link
Contributor

@markekraus markekraus left a comment

Choose a reason for hiding this comment

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

One final thing. Sorry :(


```json
{
"failureResponsesSent":2
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you update this to include the sessionId that is now also returned.

@adityapatwardhan
Copy link
Member Author

@markekraus Fixed documentation.

Copy link
Contributor

@markekraus markekraus left a comment

Choose a reason for hiding this comment

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

@adityapatwardhan Thanks for you patience and diligence. LGTM

@iSazonov iSazonov closed this Jun 28, 2018
@iSazonov iSazonov reopened this Jun 28, 2018
@iSazonov
Copy link
Collaborator

Reopen to restart CIs.

@adityapatwardhan
Copy link
Member Author

@iSazonov Thanks for restarting CIs. All tests passed, ready to merge?

@iSazonov iSazonov merged commit 15f6abe into PowerShell:master Jun 29, 2018
@iSazonov
Copy link
Collaborator

@adityapatwardhan Thanks for great contribution!

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.

WebCmdlets should support retry

6 participants