Skip to content

Commit 95d8f5f

Browse files
committed
PYTHON-5751 - Only retry overload errors if retries are enabled
1 parent e7a5247 commit 95d8f5f

7 files changed

Lines changed: 339 additions & 16 deletions

File tree

pymongo/asynchronous/database.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,7 @@ async def inner(
952952
)
953953

954954
return await self._client._retryable_read(
955-
inner, read_preference, session, command_name, None, False
955+
inner, read_preference, session, command_name, None, False, is_run_command=True
956956
)
957957

958958
@_csot.apply

pymongo/asynchronous/mongo_client.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2009,6 +2009,7 @@ async def _retry_internal(
20092009
read_pref: Optional[_ServerMode] = None,
20102010
retryable: bool = False,
20112011
operation_id: Optional[int] = None,
2012+
is_run_command: bool = False,
20122013
) -> T:
20132014
"""Internal retryable helper for all client transactions.
20142015
@@ -2020,6 +2021,7 @@ async def _retry_internal(
20202021
:param address: Server Address, defaults to None
20212022
:param read_pref: Topology of read operation, defaults to None
20222023
:param retryable: If the operation should be retried once, defaults to None
2024+
:param is_run_command: If this is a runCommand operation, defaults to False
20232025
20242026
:return: Output of the calling func()
20252027
"""
@@ -2034,6 +2036,7 @@ async def _retry_internal(
20342036
address=address,
20352037
retryable=retryable,
20362038
operation_id=operation_id,
2039+
is_run_command=is_run_command,
20372040
).run()
20382041

20392042
async def _retryable_read(
@@ -2045,6 +2048,7 @@ async def _retryable_read(
20452048
address: Optional[_Address] = None,
20462049
retryable: bool = True,
20472050
operation_id: Optional[int] = None,
2051+
is_run_command: bool = False,
20482052
) -> T:
20492053
"""Execute an operation with consecutive retries if possible
20502054
@@ -2060,6 +2064,7 @@ async def _retryable_read(
20602064
:param address: Optional address when sending a message, defaults to None
20612065
:param retryable: if we should attempt retries
20622066
(may not always be supported even if supplied), defaults to False
2067+
:param is_run_command: If this is a runCommand operation, defaults to False.
20632068
"""
20642069

20652070
# Ensure that the client supports retrying on reads and there is no session in
@@ -2078,6 +2083,7 @@ async def _retryable_read(
20782083
read_pref=read_pref,
20792084
retryable=retryable,
20802085
operation_id=operation_id,
2086+
is_run_command=is_run_command,
20812087
)
20822088

20832089
async def _retryable_write(
@@ -2747,6 +2753,7 @@ def __init__(
27472753
address: Optional[_Address] = None,
27482754
retryable: bool = False,
27492755
operation_id: Optional[int] = None,
2756+
is_run_command: bool = False,
27502757
):
27512758
self._last_error: Optional[Exception] = None
27522759
self._retrying = False
@@ -2769,6 +2776,7 @@ def __init__(
27692776
self._operation = operation
27702777
self._operation_id = operation_id
27712778
self._attempt_number = 0
2779+
self._is_run_command = is_run_command
27722780

27732781
async def run(self) -> T:
27742782
"""Runs the supplied func() and attempts a retry
@@ -2809,18 +2817,27 @@ async def run(self) -> T:
28092817
always_retryable = False
28102818
overloaded = False
28112819
exc_to_check = exc
2820+
2821+
if self._is_run_command and not (
2822+
self._client.options.retry_reads and self._client.options.retry_writes
2823+
):
2824+
raise
28122825
# Execute specialized catch on read
28132826
if self._is_read:
28142827
if isinstance(exc, (ConnectionFailure, OperationFailure)):
28152828
# ConnectionFailures do not supply a code property
28162829
exc_code = getattr(exc, "code", None)
28172830
overloaded = exc.has_error_label("SystemOverloadedError")
28182831
always_retryable = exc.has_error_label("RetryableError") and overloaded
2819-
if not always_retryable and (
2820-
self._is_not_eligible_for_retry()
2821-
or (
2822-
isinstance(exc, OperationFailure)
2823-
and exc_code not in helpers_shared._RETRYABLE_ERROR_CODES
2832+
if (
2833+
not self._client.options.retry_reads
2834+
or not always_retryable
2835+
and (
2836+
self._is_not_eligible_for_retry()
2837+
or (
2838+
isinstance(exc, OperationFailure)
2839+
and exc_code not in helpers_shared._RETRYABLE_ERROR_CODES
2840+
)
28242841
)
28252842
):
28262843
raise
@@ -2851,7 +2868,11 @@ async def run(self) -> T:
28512868
retryable_write_label = exc_to_check.has_error_label("RetryableWriteError")
28522869
overloaded = exc_to_check.has_error_label("SystemOverloadedError")
28532870
always_retryable = exc_to_check.has_error_label("RetryableError") and overloaded
2854-
if not self._retryable and not always_retryable:
2871+
if (
2872+
not self._client.options.retry_writes
2873+
or not self._retryable
2874+
and not always_retryable
2875+
):
28552876
raise
28562877
if retryable_write_label or always_retryable:
28572878
assert self._session

pymongo/synchronous/database.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,7 @@ def inner(
952952
)
953953

954954
return self._client._retryable_read(
955-
inner, read_preference, session, command_name, None, False
955+
inner, read_preference, session, command_name, None, False, is_run_command=True
956956
)
957957

958958
@_csot.apply

pymongo/synchronous/mongo_client.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2005,6 +2005,7 @@ def _retry_internal(
20052005
read_pref: Optional[_ServerMode] = None,
20062006
retryable: bool = False,
20072007
operation_id: Optional[int] = None,
2008+
is_run_command: bool = False,
20082009
) -> T:
20092010
"""Internal retryable helper for all client transactions.
20102011
@@ -2016,6 +2017,7 @@ def _retry_internal(
20162017
:param address: Server Address, defaults to None
20172018
:param read_pref: Topology of read operation, defaults to None
20182019
:param retryable: If the operation should be retried once, defaults to None
2020+
:param is_run_command: If this is a runCommand operation, defaults to False
20192021
20202022
:return: Output of the calling func()
20212023
"""
@@ -2030,6 +2032,7 @@ def _retry_internal(
20302032
address=address,
20312033
retryable=retryable,
20322034
operation_id=operation_id,
2035+
is_run_command=is_run_command,
20332036
).run()
20342037

20352038
def _retryable_read(
@@ -2041,6 +2044,7 @@ def _retryable_read(
20412044
address: Optional[_Address] = None,
20422045
retryable: bool = True,
20432046
operation_id: Optional[int] = None,
2047+
is_run_command: bool = False,
20442048
) -> T:
20452049
"""Execute an operation with consecutive retries if possible
20462050
@@ -2056,6 +2060,7 @@ def _retryable_read(
20562060
:param address: Optional address when sending a message, defaults to None
20572061
:param retryable: if we should attempt retries
20582062
(may not always be supported even if supplied), defaults to False
2063+
:param is_run_command: If this is a runCommand operation, defaults to False.
20592064
"""
20602065

20612066
# Ensure that the client supports retrying on reads and there is no session in
@@ -2074,6 +2079,7 @@ def _retryable_read(
20742079
read_pref=read_pref,
20752080
retryable=retryable,
20762081
operation_id=operation_id,
2082+
is_run_command=is_run_command,
20772083
)
20782084

20792085
def _retryable_write(
@@ -2737,6 +2743,7 @@ def __init__(
27372743
address: Optional[_Address] = None,
27382744
retryable: bool = False,
27392745
operation_id: Optional[int] = None,
2746+
is_run_command: bool = False,
27402747
):
27412748
self._last_error: Optional[Exception] = None
27422749
self._retrying = False
@@ -2759,6 +2766,7 @@ def __init__(
27592766
self._operation = operation
27602767
self._operation_id = operation_id
27612768
self._attempt_number = 0
2769+
self._is_run_command = is_run_command
27622770

27632771
def run(self) -> T:
27642772
"""Runs the supplied func() and attempts a retry
@@ -2799,18 +2807,27 @@ def run(self) -> T:
27992807
always_retryable = False
28002808
overloaded = False
28012809
exc_to_check = exc
2810+
2811+
if self._is_run_command and not (
2812+
self._client.options.retry_reads and self._client.options.retry_writes
2813+
):
2814+
raise
28022815
# Execute specialized catch on read
28032816
if self._is_read:
28042817
if isinstance(exc, (ConnectionFailure, OperationFailure)):
28052818
# ConnectionFailures do not supply a code property
28062819
exc_code = getattr(exc, "code", None)
28072820
overloaded = exc.has_error_label("SystemOverloadedError")
28082821
always_retryable = exc.has_error_label("RetryableError") and overloaded
2809-
if not always_retryable and (
2810-
self._is_not_eligible_for_retry()
2811-
or (
2812-
isinstance(exc, OperationFailure)
2813-
and exc_code not in helpers_shared._RETRYABLE_ERROR_CODES
2822+
if (
2823+
not self._client.options.retry_reads
2824+
or not always_retryable
2825+
and (
2826+
self._is_not_eligible_for_retry()
2827+
or (
2828+
isinstance(exc, OperationFailure)
2829+
and exc_code not in helpers_shared._RETRYABLE_ERROR_CODES
2830+
)
28142831
)
28152832
):
28162833
raise
@@ -2841,7 +2858,11 @@ def run(self) -> T:
28412858
retryable_write_label = exc_to_check.has_error_label("RetryableWriteError")
28422859
overloaded = exc_to_check.has_error_label("SystemOverloadedError")
28432860
always_retryable = exc_to_check.has_error_label("RetryableError") and overloaded
2844-
if not self._retryable and not always_retryable:
2861+
if (
2862+
not self._client.options.retry_writes
2863+
or not self._retryable
2864+
and not always_retryable
2865+
):
28452866
raise
28462867
if retryable_write_label or always_retryable:
28472868
assert self._session

test/asynchronous/test_client_backpressure.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ async def asyncSetUp(self) -> None:
227227
self.listener.reset()
228228
self.app_name = self.__class__.__name__.lower()
229229
self.client = await self.async_rs_or_single_client(
230-
event_listeners=[self.listener], retryWrites=False, appName=self.app_name
230+
event_listeners=[self.listener], appName=self.app_name
231231
)
232232

233233
@patch("random.random")

0 commit comments

Comments
 (0)