Skip to content

Commit f3be7f7

Browse files
committed
PYTHON-5505 Only apply backoff when SystemOverloaded label is present
1 parent b436647 commit f3be7f7

File tree

2 files changed

+42
-34
lines changed

2 files changed

+42
-34
lines changed

pymongo/asynchronous/mongo_client.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2737,7 +2737,7 @@ def __init__(
27372737
):
27382738
self._last_error: Optional[Exception] = None
27392739
self._retrying = False
2740-
self._overloaded = False
2740+
self._always_retryable = False
27412741
self._multiple_retries = _csot.get_timeout() is not None
27422742
self._client = mongo_client
27432743

@@ -2786,14 +2786,16 @@ async def run(self) -> T:
27862786
# most likely be a waste of time.
27872787
raise
27882788
except PyMongoError as exc:
2789+
always_retryable = False
27892790
overloaded = False
27902791
# Execute specialized catch on read
27912792
if self._is_read:
27922793
if isinstance(exc, (ConnectionFailure, OperationFailure)):
27932794
# ConnectionFailures do not supply a code property
27942795
exc_code = getattr(exc, "code", None)
2795-
overloaded = exc.has_error_label("Retryable")
2796-
if not overloaded and (
2796+
always_retryable = exc.has_error_label("Retryable")
2797+
overloaded = exc.has_error_label("SystemOverloaded")
2798+
if not always_retryable and (
27972799
self._is_not_eligible_for_retry()
27982800
or (
27992801
isinstance(exc, OperationFailure)
@@ -2810,21 +2812,22 @@ async def run(self) -> T:
28102812
# Specialized catch on write operation
28112813
if not self._is_read:
28122814
if isinstance(exc, ClientBulkWriteException) and exc.error:
2813-
retryable_write_error_exc = isinstance(
2814-
exc.error, PyMongoError
2815-
) and exc.error.has_error_label("RetryableWriteError")
2816-
overloaded = isinstance(
2817-
exc.error, PyMongoError
2818-
) and exc.error.has_error_label("Retryable")
2815+
if isinstance(exc.error, PyMongoError):
2816+
retryable_write_error_exc = exc.error.has_error_label(
2817+
"RetryableWriteError"
2818+
)
2819+
always_retryable = exc.error.has_error_label("Retryable")
2820+
overloaded = exc.error.has_error_label("SystemOverloaded")
28192821
else:
28202822
retryable_write_error_exc = exc.has_error_label("RetryableWriteError")
2821-
overloaded = exc.has_error_label("Retryable")
2822-
if not self._retryable and not overloaded:
2823+
always_retryable = exc.has_error_label("Retryable")
2824+
overloaded = exc.has_error_label("SystemOverloaded")
2825+
if not self._retryable and not always_retryable:
28232826
raise
2824-
if retryable_write_error_exc or overloaded:
2827+
if retryable_write_error_exc or always_retryable:
28252828
assert self._session
28262829
await self._session._unpin()
2827-
if not overloaded and (
2830+
if not always_retryable and (
28282831
not retryable_write_error_exc or not self._is_not_eligible_for_retry()
28292832
):
28302833
if exc.has_error_label("NoWritesPerformed") and self._last_error:
@@ -2844,14 +2847,15 @@ async def run(self) -> T:
28442847
if self._client.topology_description.topology_type == TOPOLOGY_TYPE.Sharded:
28452848
self._deprioritized_servers.append(self._server)
28462849

2847-
self._overloaded = overloaded
2848-
if overloaded:
2850+
self._always_retryable = always_retryable
2851+
if always_retryable:
28492852
if self._attempt_number > _MAX_RETRIES:
28502853
if exc.has_error_label("NoWritesPerformed") and self._last_error:
28512854
raise self._last_error from exc
28522855
else:
28532856
raise
2854-
await _backoff(self._attempt_number)
2857+
if overloaded:
2858+
await _backoff(self._attempt_number)
28552859

28562860
def _is_not_eligible_for_retry(self) -> bool:
28572861
"""Checks if the exchange is not eligible for retry"""
@@ -2946,7 +2950,7 @@ async def _read(self) -> T:
29462950
conn,
29472951
read_pref,
29482952
):
2949-
if self._retrying and not self._retryable and not self._overloaded:
2953+
if self._retrying and not self._retryable and not self._always_retryable:
29502954
self._check_last_error()
29512955
if self._retrying:
29522956
_debug_log(

pymongo/synchronous/mongo_client.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2727,7 +2727,7 @@ def __init__(
27272727
):
27282728
self._last_error: Optional[Exception] = None
27292729
self._retrying = False
2730-
self._overloaded = False
2730+
self._always_retryable = False
27312731
self._multiple_retries = _csot.get_timeout() is not None
27322732
self._client = mongo_client
27332733

@@ -2776,14 +2776,16 @@ def run(self) -> T:
27762776
# most likely be a waste of time.
27772777
raise
27782778
except PyMongoError as exc:
2779+
always_retryable = False
27792780
overloaded = False
27802781
# Execute specialized catch on read
27812782
if self._is_read:
27822783
if isinstance(exc, (ConnectionFailure, OperationFailure)):
27832784
# ConnectionFailures do not supply a code property
27842785
exc_code = getattr(exc, "code", None)
2785-
overloaded = exc.has_error_label("Retryable")
2786-
if not overloaded and (
2786+
always_retryable = exc.has_error_label("Retryable")
2787+
overloaded = exc.has_error_label("SystemOverloaded")
2788+
if not always_retryable and (
27872789
self._is_not_eligible_for_retry()
27882790
or (
27892791
isinstance(exc, OperationFailure)
@@ -2800,21 +2802,22 @@ def run(self) -> T:
28002802
# Specialized catch on write operation
28012803
if not self._is_read:
28022804
if isinstance(exc, ClientBulkWriteException) and exc.error:
2803-
retryable_write_error_exc = isinstance(
2804-
exc.error, PyMongoError
2805-
) and exc.error.has_error_label("RetryableWriteError")
2806-
overloaded = isinstance(
2807-
exc.error, PyMongoError
2808-
) and exc.error.has_error_label("Retryable")
2805+
if isinstance(exc.error, PyMongoError):
2806+
retryable_write_error_exc = exc.error.has_error_label(
2807+
"RetryableWriteError"
2808+
)
2809+
always_retryable = exc.error.has_error_label("Retryable")
2810+
overloaded = exc.error.has_error_label("SystemOverloaded")
28092811
else:
28102812
retryable_write_error_exc = exc.has_error_label("RetryableWriteError")
2811-
overloaded = exc.has_error_label("Retryable")
2812-
if not self._retryable and not overloaded:
2813+
always_retryable = exc.has_error_label("Retryable")
2814+
overloaded = exc.has_error_label("SystemOverloaded")
2815+
if not self._retryable and not always_retryable:
28132816
raise
2814-
if retryable_write_error_exc or overloaded:
2817+
if retryable_write_error_exc or always_retryable:
28152818
assert self._session
28162819
self._session._unpin()
2817-
if not overloaded and (
2820+
if not always_retryable and (
28182821
not retryable_write_error_exc or not self._is_not_eligible_for_retry()
28192822
):
28202823
if exc.has_error_label("NoWritesPerformed") and self._last_error:
@@ -2834,14 +2837,15 @@ def run(self) -> T:
28342837
if self._client.topology_description.topology_type == TOPOLOGY_TYPE.Sharded:
28352838
self._deprioritized_servers.append(self._server)
28362839

2837-
self._overloaded = overloaded
2838-
if overloaded:
2840+
self._always_retryable = always_retryable
2841+
if always_retryable:
28392842
if self._attempt_number > _MAX_RETRIES:
28402843
if exc.has_error_label("NoWritesPerformed") and self._last_error:
28412844
raise self._last_error from exc
28422845
else:
28432846
raise
2844-
_backoff(self._attempt_number)
2847+
if overloaded:
2848+
_backoff(self._attempt_number)
28452849

28462850
def _is_not_eligible_for_retry(self) -> bool:
28472851
"""Checks if the exchange is not eligible for retry"""
@@ -2936,7 +2940,7 @@ def _read(self) -> T:
29362940
conn,
29372941
read_pref,
29382942
):
2939-
if self._retrying and not self._retryable and not self._overloaded:
2943+
if self._retrying and not self._retryable and not self._always_retryable:
29402944
self._check_last_error()
29412945
if self._retrying:
29422946
_debug_log(

0 commit comments

Comments
 (0)