-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Skip GitHub fast path when rate-limited #13033
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
Conversation
|
Hi @zanieb, I like to follow up on this PR as it has been awaiting review for a while. Do you have any feedback on this work? Let me know if this is still relevant. Thanks! |
|
I think this might not quite be the right approach. I think what we want is: if we detect a rate limit, skip these "fast-path" attempts in any subsequent operations. So we might need to track some kind of "Are we rate-limited?" global state, and read-from + update it in these locations. |
|
Indeed, that's what I had in mind. GitHub also returns a time to wait until attempting another request, so we can stash that in the global state and invalidate it after that much time (though I think in practice, skipping it until the process exits would be fine). |
|
Thank you both for the feedback; I understand things better now after some studying. I'm assuming that we want the global state to apply to everything within Just to ensure I'm interpreting things correctly, here's the steps I'll implement:
Please let me know if I've misinterpreted anything here. Thanks! |
|
Hi @zanieb, I've updated the PR based on your feedback. I found adding integration tests to not be easy. The base urls for the GitHub fast-paths are hardcoded and not exposed via the CLI / env vars, so I don't know how to mock their response via Thanks! |
It'd be fine to add this as an environment variable, imo. |
|
I can add that env var (tentatively |
78fd5da to
29ce14c
Compare
|
I've made the changes and included integration tests. I manually did some mutation testing (changing the status code from the mock response, and commenting out this PR's functionalities) to verify the tests aren't dummy tests. |
crates/uv/tests/it/edit.rs
Outdated
| use uv_client::DEFAULT_RETRIES; | ||
|
|
||
| let context = TestContext::new("3.12"); | ||
| let token = decode_token(READ_ONLY_GITHUB_TOKEN); | ||
|
|
||
| let server = MockServer::start().await; | ||
| Mock::given(method("GET")) | ||
| .respond_with(ResponseTemplate::new(429)) | ||
| .expect(1 + u64::from(DEFAULT_RETRIES)) |
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.
uv makes 4 attempts (first try + 3 retries) because a HTTP 429 is considered transient in uv-client.
If there are other concurrent fast paths attempt before the first rate-limited request finishes retrying, the skip functionality won't kick in yet. I'm testing the implications of changing the retry semantics for HTTP 429 to Retryable::Fatal.
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.
Local test suite is passing, so I can update these tests to expect zero retries.
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.
Full test suite fails, so not going through with that. See comment below as well.
crates/uv-client/src/base_client.rs
Outdated
| "Considering skipping retry of response HTTP {status} for {}", | ||
| response.url() | ||
| ); | ||
| if status == StatusCode::TOO_MANY_REQUESTS { |
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 this makes a 429 fatal everywhere? We probably don't want that.
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.
Sounds good. Reverting.
This reverts commit 7a7fd11.
|
Good morning @zanieb, the PR is ready for review. |
| #[attr_hidden] | ||
| pub const UV_TEST_INDEX_URL: &'static str = "UV_TEST_INDEX_URL"; | ||
|
|
||
| /// Used to set the GitHub fast-path url for tests. |
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 should document what shape of URL is expected, is there something that GitHub considers as API root that users could exchange?
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 env var's intended use is to mock the GitHub API root during tests. It's not meant for users to override, and is hidden from user-facing docs with #[attr_hidden].
I can rename this to UV_TEST_GITHUB_FAST_PATH_URL to be even more explicit, assuming we are okay with UV_TEST_* appearing in non-test files.
Please let me know what you think.
konstin
left a comment
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.
Two things that should be easy to fix, otherwise it looks good!
crates/uv-git/src/rate_limit.rs
Outdated
| pub(crate) struct GitHubRateLimitStatus(AtomicBool); | ||
|
|
||
| impl GitHubRateLimitStatus { | ||
| pub(crate) const fn new() -> Self { |
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.
Does this method need to be pub(crate)?
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.
No it doesn't. Updated to just const.
crates/uv/tests/it/pip_install.rs
Outdated
|
|
||
| uv_snapshot!(context.filters(), context | ||
| .pip_install() | ||
| .arg("pip-test-package @ git+https://github.com/pypa/pip-test-package@5547fa909e83df8bd743d3978d6667497983a4b7") |
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.
For consistency, can we use https://github.com/astral-test/uv-public-pypackage here too?
| let server = MockServer::start().await; | ||
| Mock::given(method("GET")) | ||
| .respond_with(ResponseTemplate::new(429)) | ||
| .expect(1 + u64::from(DEFAULT_RETRIES)) // Middleware retries on 429 by default | ||
| .mount(&server) | ||
| .await; |
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.
Nice work on the testing strategy here!
|
@konstin changes have been made and CI is passing. Thanks for your quick PR review 😊 |
aa91bde to
3a55cf6
Compare
This MR contains the following updates: | Package | Update | Change | |---|---|---| | [astral-sh/uv](https://github.com/astral-sh/uv) | patch | `0.7.14` -> `0.7.16` | MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot). **Proposed changes to behavior should be submitted there as MRs.** --- ### Release Notes <details> <summary>astral-sh/uv (astral-sh/uv)</summary> ### [`v0.7.16`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#0716) [Compare Source](astral-sh/uv@0.7.15...0.7.16) ##### Python - Add Python 3.14.0b3 See the [`python-build-standalone` release notes](https://github.com/astral-sh/python-build-standalone/releases/tag/20250626) for more details. ##### Enhancements - Include path or URL when failing to convert in lockfile ([#​14292](astral-sh/uv#14292)) - Warn when `~=` is used as a Python version specifier without a patch version ([#​14008](astral-sh/uv#14008)) ##### Preview features - Ensure preview default Python installs are upgradeable ([#​14261](astral-sh/uv#14261)) ##### Performance - Share workspace cache between lock and sync operations ([#​14321](astral-sh/uv#14321)) ##### Bug fixes - Allow local indexes to reference remote files ([#​14294](astral-sh/uv#14294)) - Avoid rendering desugared prefix matches in error messages ([#​14195](astral-sh/uv#14195)) - Avoid using path URL for workspace Git dependencies in `requirements.txt` ([#​14288](astral-sh/uv#14288)) - Normalize index URLs to remove trailing slash ([#​14245](astral-sh/uv#14245)) - Respect URL-encoded credentials in redirect location ([#​14315](astral-sh/uv#14315)) - Lock the source tree when running setuptools, to protect concurrent builds ([#​14174](astral-sh/uv#14174)) ##### Documentation - Note that GCP Artifact Registry download URLs must have `/simple` component ([#​14251](astral-sh/uv#14251)) ### [`v0.7.15`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#0715) [Compare Source](astral-sh/uv@0.7.14...0.7.15) ##### Enhancements - Consistently use `Ordering::Relaxed` for standalone atomic use cases ([#​14190](astral-sh/uv#14190)) - Warn on ambiguous relative paths for `--index` ([#​14152](astral-sh/uv#14152)) - Skip GitHub fast path when rate-limited ([#​13033](astral-sh/uv#13033)) - Preserve newlines in `schema.json` descriptions ([#​13693](astral-sh/uv#13693)) ##### Bug fixes - Add check for using minor version link when creating a venv on Windows ([#​14252](astral-sh/uv#14252)) - Strip query parameters when parsing source URL ([#​14224](astral-sh/uv#14224)) ##### Documentation - Add a link to PyPI FAQ to clarify what per-project token is ([#​14242](astral-sh/uv#14242)) ##### Preview features - Allow symlinks in the build backend ([#​14212](astral-sh/uv#14212)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this MR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box --- This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90Il19-->
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [ghcr.io/astral-sh/uv](https://github.com/astral-sh/uv) | final | patch | `0.7.13` -> `0.7.15` | --- ### Release Notes <details> <summary>astral-sh/uv (ghcr.io/astral-sh/uv)</summary> ### [`v0.7.15`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#0715) [Compare Source](astral-sh/uv@0.7.14...0.7.15) ##### Enhancements - Consistently use `Ordering::Relaxed` for standalone atomic use cases ([#​14190](astral-sh/uv#14190)) - Warn on ambiguous relative paths for `--index` ([#​14152](astral-sh/uv#14152)) - Skip GitHub fast path when rate-limited ([#​13033](astral-sh/uv#13033)) - Preserve newlines in `schema.json` descriptions ([#​13693](astral-sh/uv#13693)) ##### Bug fixes - Add check for using minor version link when creating a venv on Windows ([#​14252](astral-sh/uv#14252)) - Strip query parameters when parsing source URL ([#​14224](astral-sh/uv#14224)) ##### Documentation - Add a link to PyPI FAQ to clarify what per-project token is ([#​14242](astral-sh/uv#14242)) ##### Preview features - Allow symlinks in the build backend ([#​14212](astral-sh/uv#14212)) ### [`v0.7.14`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#0714) [Compare Source](astral-sh/uv@0.7.13...0.7.14) ##### Enhancements - Add XPU to `--torch-backend` ([#​14172](astral-sh/uv#14172)) - Add ROCm backends to `--torch-backend` ([#​14120](astral-sh/uv#14120)) - Remove preview label from `--torch-backend` ([#​14119](astral-sh/uv#14119)) - Add `[tool.uv.dependency-groups].mygroup.requires-python` ([#​13735](astral-sh/uv#13735)) - Add auto-detection for AMD GPUs ([#​14176](astral-sh/uv#14176)) - Show retries for HTTP status code errors ([#​13897](astral-sh/uv#13897)) - Support transparent Python patch version upgrades ([#​13954](astral-sh/uv#13954)) - Warn on empty index directory ([#​13940](astral-sh/uv#13940)) - Publish to DockerHub ([#​14088](astral-sh/uv#14088)) ##### Performance - Make cold resolves about 10% faster ([#​14035](astral-sh/uv#14035)) ##### Bug fixes - Don't use walrus operator in interpreter query script ([#​14108](astral-sh/uv#14108)) - Fix handling of changes to `requires-python` ([#​14076](astral-sh/uv#14076)) - Fix implied `platform_machine` marker for `...
Summary
Fixes #12761 and #12746.
I'm interpreting the "special handling" required to mean erroring so that we skip the subsequent Git fetch operation in
.git_metadataand.resolve_revisionofSourceDistributionBuilder. Please advise if you meant something else instead.Also let me know if you'd like me to generalize this to other HTTP status codes (i.e. 400 and 422, as mentioned in the comments).
Test Plan
None so far.
wiremockto mock a 429 response?