PYTHON-5767 - Finalize client backpressure implementation for phase 1…#2742
PYTHON-5767 - Finalize client backpressure implementation for phase 1…#2742NoahStapp merged 11 commits intomongodb:backpressurefrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates PyMongo’s phase-1 client backpressure behavior and tests by replacing the previous adaptiveRetries boolean with new overload-related options (maxAdaptiveRetries, enableOverloadRetargeting) and reducing the default maximum overload retry count used by tests/specs.
Changes:
- Introduces/validates new URI + keyword options:
maxAdaptiveRetriesandenableOverloadRetargeting. - Updates retry/backpressure prose + unified JSON specs to use a lower retry limit (5 → 2) and adjusts expected command/event sequences accordingly.
- Updates unit/integration tests to assert parsing of new options and to use the new shared retry limit constant.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| test/uri_options/client-backpressure-options.json | URI option parsing tests updated for maxAdaptiveRetries and enableOverloadRetargeting. |
| test/transactions/unified/backpressure-retryable-writes.json | Unified spec expectations updated for reduced backpressure retry attempts. |
| test/transactions/unified/backpressure-retryable-reads.json | Unified spec expectations updated for reduced backpressure retry attempts. |
| test/transactions/unified/backpressure-retryable-commit.json | Unified spec expectations updated for reduced backpressure retry attempts. |
| test/transactions/unified/backpressure-retryable-abort.json | Unified spec expectations updated for reduced backpressure retry attempts. |
| test/test_client.py | Client option tests updated to assert max_adaptive_retries + enable_overload_retargeting. |
| test/test_client_backpressure.py | Backpressure tests updated to use MAX_ADAPTIVE_RETRIES and revised retry expectations. |
| test/client-backpressure/getMore-retried.json | Spec updated for reduced retry attempts and corresponding event sequences. |
| test/client-backpressure/backpressure-retry-max-attempts.json | Extensive spec updates to reflect reduced retry attempts across many operations. |
| test/client-backpressure/backpressure-retry-loop.json | Spec updated for reduced retry attempts and corresponding event sequences. |
| test/client-backpressure/backpressure-connection-checkin.json | Spec event expectations reduced to match new retry counts. |
| test/asynchronous/test_client.py | Async client option tests updated to assert max_adaptive_retries + enable_overload_retargeting. |
| test/asynchronous/test_client_backpressure.py | Async backpressure tests updated to use MAX_ADAPTIVE_RETRIES and revised retry expectations. |
| pymongo/common.py | Adds defaults/constants and validators for new overload-related options. |
| pymongo/client_options.py | Parses/stores new options and exposes new properties on ClientOptions. |
| pymongo/asynchronous/helpers.py | Updates _RetryPolicy defaults to use MAX_ADAPTIVE_RETRIES; disables adaptive token-bucket mode. |
| pymongo/asynchronous/mongo_client.py | Updates public docstring for new options and changes _RetryPolicy construction. |
| pymongo/synchronous/helpers.py | Sync mirror of async helper changes (synchro-generated). |
| pymongo/synchronous/mongo_client.py | Sync mirror of async client docstring/policy construction (synchro-generated). |
Comments suppressed due to low confidence (1)
pymongo/asynchronous/helpers.py:145
_RetryPolicy’s docstring describes an “adaptive retries” token-bucket mode, butadaptive_retryis now hard-coded toFalseand there’s no longer a constructor parameter or other code path that enables it. Either reintroduce a supported way to enable the token-bucket behavior (if still intended) or update/remove the docstring and related dead code to match current behavior.
class _RetryPolicy:
"""A retry limiter that performs exponential backoff with jitter.
When adaptive retries are enabled, retry attempts are limited by a token bucket to prevent overwhelming the server during
a prolonged outage or high load.
"""
def __init__(
self,
token_bucket: _TokenBucket,
attempts: int = MAX_ADAPTIVE_RETRIES,
backoff_initial: float = _BACKOFF_INITIAL,
backoff_max: float = _BACKOFF_MAX,
):
self.token_bucket = token_bucket
self.attempts = attempts
self.backoff_initial = backoff_initial
self.backoff_max = backoff_max
self.adaptive_retry = False
async def record_success(self, retry: bool) -> None:
"""Record a successful operation."""
if self.adaptive_retry:
await self.token_bucket.deposit(retry)
sleepyStick
left a comment
There was a problem hiding this comment.
small suggestion, feel free to veto it and otherwise approved
| # Ensure command stops retrying after MAX_ADAPTIVE_RETRIES. | ||
| fail_too_many = mock_overload_error.copy() | ||
| fail_too_many["mode"] = {"times": _MAX_RETRIES + 1} | ||
| fail_too_many["mode"] = {"times": MAX_ADAPTIVE_RETRIES + 1} |
There was a problem hiding this comment.
super minor comment but there's a lot of these lines where we copy the overload error and then edit the mode for the times it fails, would it be a tad easier to have a function like get_overload_error(too_many: bool) that just returns a copy of the overload error with the mode correctly set (in the function?)
There was a problem hiding this comment.
Something like this?
get_overload_error(times: int):
return {"times": times}
That wouldn't reduce the repetition, just add an extra utility function that does very little. I agree that it's annoying we have 10 instances of doing almost the same assignment, but in this case we can't avoid that.
There was a problem hiding this comment.
errr i think i was imagining something more like:
def get_overload_error(times: int):
error = mock_overload_error.copy()
error["mode"] = {"times": times}
return error
which i think would turn the 2 lines into 1? which is still very little but yeah, up to you if you think its worth it ultimately
There was a problem hiding this comment.
You're right that is better, good callout!
test/asynchronous/test_client.py
Outdated
| async def test_adaptive_retries(self): | ||
| # Assert that adaptive retries are disabled by default. | ||
| async def test_max_adaptive_retries(self): | ||
| # Assert that max adaptive retries default to 2. |
| listener.reset() | ||
|
|
||
| # 4. Execute a `find` command with `client`. | ||
| await client.t.t.find_one({}) |
There was a problem hiding this comment.
database.collection.
aclark4life
left a comment
There was a problem hiding this comment.
LGTM pending nit fixes
… rollout
PYTHON-5767
Changes in this PR
adaptiveRetriesclient knob and URI option.maxAdaptiveRetriesandenableOverloadRetargeting.Test Plan
Updated existing backpressure prose and spec tests and added new ones to comply with the final phase 1 design.
Checklist
Checklist for Author
Checklist for Reviewer