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

Honor 429 Retry-After #3718

Merged
merged 18 commits into from
Oct 4, 2023
Merged

Conversation

msftrubengu
Copy link
Contributor

@msftrubengu msftrubengu commented Sep 30, 2023

Based on JohnMcPMS/honor-response

Change

This pull request honors 429 Retry-After for pre indexed sources and throws ServiceUnavailableException when the response is 429 or 503.

  • If there's a Retry-After header and its value is more than 60s or the date is more than 60s into the future
    • Save the next time it can be updated in the source details and not try again for that command.
    • Subsequent updates or open that source will be skipped if the time lapsed hasn't passed.
  • If there's a Retry-After header and its value is less than 60s or the date value is less than 60 seconds into the future
    • Retry once after that amount of time.
  • If no Retry-After header
    • Retry after a random amount of time.

For open, the failed to update source message will show up.
For update, a warning with Unavailable will show up.
The next available time will show up in the logs.

Test

I ran manual validation using Fiddler to intercept winget's source.msix and return 429 or 503 with different Retry-Header

Microsoft Reviewers: Open in CodeFlow

@msftrubengu msftrubengu requested a review from a team as a code owner September 30, 2023 17:14
@github-actions

This comment has been minimized.

Copy link
Member

@JohnMcPMS JohnMcPMS left a comment

Choose a reason for hiding this comment

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

Code that could still use generating the exceptions:

  1. Repo\Rest\Schema\HttpClientHelper.cpp :: ValidateAndExtractResponse
  2. Common\HttpStream\HttpClientWrapper.cpp :: both PopulateInfoAsync and SendHttpRequestAsync
  3. Common\DODownloader.cpp :: Somewhere in here after figuring out how they tell us about a 429

Code that could still be updated to handle the exception:

  1. Manifest retrieval
  2. Installer download

For this change I would request exception generation 1 and 2 be implemented. The DO downloader is probably already handling this reasonably well, so we can wait until we have actual issues. I would also request that we handle it in the installer download, in the event that the user has set us to use wininet. The manifest retrieval has no retry, nor would it be easy or make much sense to integrate manifest retrieval with the index update.

src/AppInstallerCLICore/Workflows/SourceFlow.cpp Outdated Show resolved Hide resolved
src/AppInstallerCommonCore/Downloader.cpp Outdated Show resolved Hide resolved
src/AppInstallerCommonCore/Downloader.cpp Show resolved Hide resolved
src/AppInstallerCommonCore/Downloader.cpp Outdated Show resolved Hide resolved
AddOrUpdateResult result;

// Do not update if we are still before the update block time.
if (IsUpdateSuppressed(details))
Copy link
Member

Choose a reason for hiding this comment

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

I had intentionally left it open for a manual update to still attempt this. We are caught in the middle of respecting the user's request and the server's. I wouldn't mind it being "harder" for the client to do (aka --force), but I feel like we should leave that option open.

Copy link
Member

Choose a reason for hiding this comment

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

This is still here...

src/AppInstallerRepositoryCore/RepositorySource.cpp Outdated Show resolved Hide resolved
src/AppInstallerRepositoryCore/RepositorySource.cpp Outdated Show resolved Hide resolved
src/AppInstallerRepositoryCore/RepositorySource.cpp Outdated Show resolved Hide resolved
msftrubengu and others added 5 commits October 2, 2023 12:08
Co-authored-by: JohnMcPMS <johnmcp@microsoft.com>
Co-authored-by: JohnMcPMS <johnmcp@microsoft.com>
Co-authored-by: JohnMcPMS <johnmcp@microsoft.com>
@msftrubengu
Copy link
Contributor Author

msftrubengu commented Oct 3, 2023

Updated for

  1. Repo\Rest\Schema\HttpClientHelper.cpp :: ValidateAndExtractResponse
  2. Common\HttpStream\HttpClientWrapper.cpp :: both PopulateInfoAsync and SendHttpRequestAsync

For SendHttpRequestAsync is going to be more complicated to get the RetryCount without weird things.

I believe the installer download was already covered, no? If the setting is wininet it will go to WinINetDownloadToStream which I already implemented.

Missing manifest retrieval and verify ValidateAndExtractResponse

Copy link
Member

@JohnMcPMS JohnMcPMS left a comment

Choose a reason for hiding this comment

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

I meant catching the exception in DownloadInstallerFile and waiting for the appropriate amount of time rather than the arbitrary (and short) time being waited for in that retry.

if (hre.code() == APPINSTALLER_CLI_ERROR_SERVICE_UNAVAILABLE)
{
auto retryAfter = httpRandomAccessStream->RetryAfter();
THROW_EXCEPTION(AppInstaller::Utility::ServiceUnavailableException(retryAfter));
Copy link
Member

Choose a reason for hiding this comment

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

Did you verify this one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes

src/AppInstallerCommonCore/MsixInfo.cpp Outdated Show resolved Hide resolved
src/AppInstallerCommonCore/Public/AppInstallerProgress.h Outdated Show resolved Hide resolved
AddOrUpdateResult result;

// Do not update if we are still before the update block time.
if (IsUpdateSuppressed(details))
Copy link
Member

Choose a reason for hiding this comment

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

This is still here...

@JohnMcPMS
Copy link
Member

For SendHttpRequestAsync is going to be more complicated to get the RetryCount without weird things.

Ok, the HEAD request handling it should hopefully cover it, and it is no longer the primary case anyway.

ryfu-msft
ryfu-msft previously approved these changes Oct 4, 2023
@msftrubengu msftrubengu merged commit 21de160 into microsoft:master Oct 4, 2023
8 checks passed
msftrubengu added a commit to msftrubengu/winget-cli that referenced this pull request Oct 4, 2023
Change
This pull request honors 429 Retry-After for pre indexed sources and throws ServiceUnavailableException when the response is 429 or 503.

If there's a Retry-After header and its value is more than 60s or the date is more than 60s into the future
Save the next time it can be updated in the source details and not try again for that command.
Subsequent updates or open that source will be skipped if the time lapsed hasn't passed.
If there's a Retry-After header and its value is less than 60s or the date value is less than 60 seconds into the future
Retry once after that amount of time.
If no Retry-After header
Retry after a random amount of time.
For open, the failed to update source message will show up.
For update, a warning with Unavailable will show up.
The next available time will show up in the logs.
msftrubengu added a commit that referenced this pull request Oct 4, 2023
Change
This pull request honors 429 Retry-After for pre indexed sources and throws ServiceUnavailableException when the response is 429 or 503.

If there's a Retry-After header and its value is more than 60s or the date is more than 60s into the future
Save the next time it can be updated in the source details and not try again for that command.
Subsequent updates or open that source will be skipped if the time lapsed hasn't passed.
If there's a Retry-After header and its value is less than 60s or the date value is less than 60 seconds into the future
Retry once after that amount of time.
If no Retry-After header
Retry after a random amount of time.
For open, the failed to update source message will show up.
For update, a warning with Unavailable will show up.
The next available time will show up in the logs.
@msftrubengu msftrubengu deleted the honor-response branch November 14, 2023 00:31
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