Skip to content

Commit 425f44f

Browse files
committed
PYTHON-5767 - Finalize client backpressure implementation for phase 1 rollout
1 parent 3f64de3 commit 425f44f

14 files changed

+234
-1710
lines changed

pymongo/asynchronous/helpers.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
)
3131

3232
from pymongo import _csot
33+
from pymongo.common import MAX_ADAPTIVE_RETRIES
3334
from pymongo.errors import (
3435
OperationFailure,
3536
)
@@ -76,7 +77,6 @@ async def inner(*args: Any, **kwargs: Any) -> Any:
7677
return cast(F, inner)
7778

7879

79-
_MAX_RETRIES = 5
8080
_BACKOFF_INITIAL = 0.1
8181
_BACKOFF_MAX = 10
8282
DEFAULT_RETRY_TOKEN_CAPACITY = 1000.0
@@ -128,16 +128,15 @@ class _RetryPolicy:
128128
def __init__(
129129
self,
130130
token_bucket: _TokenBucket,
131-
attempts: int = _MAX_RETRIES,
131+
attempts: int = MAX_ADAPTIVE_RETRIES,
132132
backoff_initial: float = _BACKOFF_INITIAL,
133133
backoff_max: float = _BACKOFF_MAX,
134-
adaptive_retry: bool = False,
135134
):
136135
self.token_bucket = token_bucket
137136
self.attempts = attempts
138137
self.backoff_initial = backoff_initial
139138
self.backoff_max = backoff_max
140-
self.adaptive_retry = adaptive_retry
139+
self.adaptive_retry = False
141140

142141
async def record_success(self, retry: bool) -> None:
143142
"""Record a successful operation."""

pymongo/asynchronous/mongo_client.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -615,17 +615,17 @@ def __init__(
615615
client to use Stable API. See `versioned API <https://www.mongodb.com/docs/manual/reference/stable-api/#what-is-the-stable-api--and-should-you-use-it->`_ for
616616
details.
617617
618-
| **Adaptive retry options:**
619-
| (If not enabled explicitly, adaptive retries will not be enabled.)
618+
| **Overload retry options:**|
620619
621-
- `adaptive_retries`: (boolean) Whether the adaptive retry mechanism is enabled for this client.
622-
If enabled, server overload errors will use a token-bucket based system to mitigate further overload.
620+
- `max_adaptive_retries`: (int) How many retries to allow for overload errors. Defaults to ``2``.
621+
- `enable_overload_retargeting`: (boolean) Whether overload retargeting is enabled for this client.
622+
If enabled, server overload errors will cause retry attempts to select a server that has not yet returned an overload error, if possible.
623623
Defaults to ``False``.
624624
625625
.. seealso:: The MongoDB documentation on `connections <https://dochub.mongodb.org/core/connections>`_.
626626
627627
.. versionchanged:: 4.17
628-
Added the ``adaptive_retries`` URI and keyword argument.
628+
Added the ``max_adaptive_retries`` and ``enable_overload_retargeting`` URI and keyword arguments.
629629
630630
.. versionchanged:: 4.5
631631
Added the ``serverMonitoringMode`` keyword argument.
@@ -894,9 +894,7 @@ def __init__(
894894
self._options.read_concern,
895895
)
896896

897-
self._retry_policy = _RetryPolicy(
898-
_TokenBucket(), adaptive_retry=self._options.adaptive_retries
899-
)
897+
self._retry_policy = _RetryPolicy(_TokenBucket())
900898

901899
self._init_based_on_options(self._seeds, srv_max_hosts, srv_service_name)
902900

pymongo/client_options.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,16 @@ def __init__(
240240
if "adaptive_retries" in options
241241
else options.get("adaptiveretries", common.ADAPTIVE_RETRIES)
242242
)
243+
self.__max_adaptive_retries = (
244+
options.get("max_adaptive_retries", common.MAX_ADAPTIVE_RETRIES)
245+
if "max_adaptive_retries" in options
246+
else options.get("maxadaptiveretries", common.MAX_ADAPTIVE_RETRIES)
247+
)
248+
self.__enable_overload_retargeting = (
249+
options.get("enable_overload_retargeting", common.ENABLE_OVERLOAD_RETARGETING)
250+
if "enable_overload_retargeting" in options
251+
else options.get("enableoverloadretargeting", common.ENABLE_OVERLOAD_RETARGETING)
252+
)
243253

244254
@property
245255
def _options(self) -> Mapping[str, Any]:
@@ -353,9 +363,17 @@ def server_monitoring_mode(self) -> str:
353363
return self.__server_monitoring_mode
354364

355365
@property
356-
def adaptive_retries(self) -> bool:
357-
"""The configured adaptiveRetries option.
366+
def max_adaptive_retries(self) -> int:
367+
"""The configured maxAdaptiveRetries option.
368+
369+
.. versionadded:: 4.17
370+
"""
371+
return self.__max_adaptive_retries
372+
373+
@property
374+
def enable_overload_retargeting(self) -> bool:
375+
"""The configured enableOverloadRetargeting option.
358376
359-
.. versionadded:: 4.XX
377+
.. versionadded:: 4.17
360378
"""
361-
return self.__adaptive_retries
379+
return self.__enable_overload_retargeting

pymongo/common.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@
143143
# Default value for adaptiveRetries
144144
ADAPTIVE_RETRIES = False
145145

146+
# Default value for max adaptive retries
147+
MAX_ADAPTIVE_RETRIES = 2
148+
149+
# Default value for enableOverloadRetargeting
150+
ENABLE_OVERLOAD_RETARGETING = False
151+
146152
# Auth mechanism properties that must raise an error instead of warning if they invalidate.
147153
_MECH_PROP_MUST_RAISE = ["CANONICALIZE_HOST_NAME"]
148154

@@ -742,6 +748,8 @@ def validate_server_monitoring_mode(option: str, value: str) -> str:
742748
"timeoutms": validate_timeoutms,
743749
"servermonitoringmode": validate_server_monitoring_mode,
744750
"adaptiveretries": validate_boolean_or_string,
751+
"maxadaptiveretries": validate_non_negative_integer,
752+
"enableoverloadretargeting": validate_boolean_or_string,
745753
}
746754

747755
# Dictionary where keys are the names of URI options specific to pymongo,
@@ -776,6 +784,8 @@ def validate_server_monitoring_mode(option: str, value: str) -> str:
776784
"auto_encryption_opts": validate_auto_encryption_opts_or_none,
777785
"authoidcallowedhosts": validate_list,
778786
"adaptive_retries": validate_boolean_or_string,
787+
"max_adaptive_retries": validate_non_negative_integer,
788+
"enable_overload_retargeting": validate_boolean_or_string,
779789
}
780790

781791
# Dictionary where keys are any URI option name, and values are the

pymongo/synchronous/helpers.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
)
3131

3232
from pymongo import _csot
33+
from pymongo.common import MAX_ADAPTIVE_RETRIES
3334
from pymongo.errors import (
3435
OperationFailure,
3536
)
@@ -76,7 +77,6 @@ def inner(*args: Any, **kwargs: Any) -> Any:
7677
return cast(F, inner)
7778

7879

79-
_MAX_RETRIES = 5
8080
_BACKOFF_INITIAL = 0.1
8181
_BACKOFF_MAX = 10
8282
DEFAULT_RETRY_TOKEN_CAPACITY = 1000.0
@@ -128,16 +128,15 @@ class _RetryPolicy:
128128
def __init__(
129129
self,
130130
token_bucket: _TokenBucket,
131-
attempts: int = _MAX_RETRIES,
131+
attempts: int = MAX_ADAPTIVE_RETRIES,
132132
backoff_initial: float = _BACKOFF_INITIAL,
133133
backoff_max: float = _BACKOFF_MAX,
134-
adaptive_retry: bool = False,
135134
):
136135
self.token_bucket = token_bucket
137136
self.attempts = attempts
138137
self.backoff_initial = backoff_initial
139138
self.backoff_max = backoff_max
140-
self.adaptive_retry = adaptive_retry
139+
self.adaptive_retry = False
141140

142141
def record_success(self, retry: bool) -> None:
143142
"""Record a successful operation."""

pymongo/synchronous/mongo_client.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -615,17 +615,17 @@ def __init__(
615615
client to use Stable API. See `versioned API <https://www.mongodb.com/docs/manual/reference/stable-api/#what-is-the-stable-api--and-should-you-use-it->`_ for
616616
details.
617617
618-
| **Adaptive retry options:**
619-
| (If not enabled explicitly, adaptive retries will not be enabled.)
618+
| **Overload retry options:**|
620619
621-
- `adaptive_retries`: (boolean) Whether the adaptive retry mechanism is enabled for this client.
622-
If enabled, server overload errors will use a token-bucket based system to mitigate further overload.
620+
- `max_adaptive_retries`: (int) How many retries to allow for overload errors. Defaults to ``2``.
621+
- `enable_overload_retargeting`: (boolean) Whether overload retargeting is enabled for this client.
622+
If enabled, server overload errors will cause retry attempts to select a server that has not yet returned an overload error, if possible.
623623
Defaults to ``False``.
624624
625625
.. seealso:: The MongoDB documentation on `connections <https://dochub.mongodb.org/core/connections>`_.
626626
627627
.. versionchanged:: 4.17
628-
Added the ``adaptive_retries`` URI and keyword argument.
628+
Added the ``max_adaptive_retries`` and ``enable_overload_retargeting`` URI and keyword arguments.
629629
630630
.. versionchanged:: 4.5
631631
Added the ``serverMonitoringMode`` keyword argument.
@@ -894,9 +894,7 @@ def __init__(
894894
self._options.read_concern,
895895
)
896896

897-
self._retry_policy = _RetryPolicy(
898-
_TokenBucket(), adaptive_retry=self._options.adaptive_retries
899-
)
897+
self._retry_policy = _RetryPolicy(_TokenBucket())
900898

901899
self._init_based_on_options(self._seeds, srv_max_hosts, srv_service_name)
902900

test/asynchronous/test_client.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -652,20 +652,37 @@ async def test_detected_environment_warning(self, mock_get_hosts):
652652
with self.assertWarns(UserWarning):
653653
self.simple_client(multi_host)
654654

655-
async def test_adaptive_retries(self):
656-
# Assert that adaptive retries are disabled by default.
655+
async def test_max_adaptive_retries(self):
656+
# Assert that max adaptive retries default to 2.
657657
c = self.simple_client(connect=False)
658-
self.assertFalse(c.options.adaptive_retries)
658+
self.assertEqual(c.options.max_adaptive_retries, 2)
659659

660660
# Assert that adaptive retries can be enabled through connection or client options.
661-
c = self.simple_client(connect=False, adaptive_retries=True)
662-
self.assertTrue(c.options.adaptive_retries)
661+
c = self.simple_client(connect=False, max_adaptive_retries=10)
662+
self.assertEqual(c.options.max_adaptive_retries, 10)
663663

664-
c = self.simple_client(connect=False, adaptiveRetries=True)
665-
self.assertTrue(c.options.adaptive_retries)
664+
c = self.simple_client(connect=False, maxAdaptiveRetries=10)
665+
self.assertEqual(c.options.max_adaptive_retries, 10)
666666

667-
c = self.simple_client(host="mongodb://localhost/?adaptiveretries=true", connect=False)
668-
self.assertTrue(c.options.adaptive_retries)
667+
c = self.simple_client(host="mongodb://localhost/?maxAdaptiveRetries=10", connect=False)
668+
self.assertEqual(c.options.max_adaptive_retries, 10)
669+
670+
async def test_enable_overload_retargeting(self):
671+
# Assert that overload retargeting defaults to false.
672+
c = self.simple_client(connect=False)
673+
self.assertFalse(c.options.enable_overload_retargeting)
674+
675+
# Assert that overload retargeting can be enabled through connection or client options.
676+
c = self.simple_client(connect=False, enable_overload_retargeting=True)
677+
self.assertTrue(c.options.enable_overload_retargeting)
678+
679+
c = self.simple_client(connect=False, enableOverloadRetargeting=True)
680+
self.assertTrue(c.options.enable_overload_retargeting)
681+
682+
c = self.simple_client(
683+
host="mongodb://localhost/?enableOverloadRetargeting=true", connect=False
684+
)
685+
self.assertTrue(c.options.enable_overload_retargeting)
669686

670687

671688
class TestClient(AsyncIntegrationTest):

0 commit comments

Comments
 (0)