6767from pymongo .asynchronous .client_bulk import _AsyncClientBulk
6868from pymongo .asynchronous .client_session import _EmptyServerSession
6969from pymongo .asynchronous .command_cursor import AsyncCommandCursor
70+ from pymongo .asynchronous .helpers import _MAX_RETRIES , _backoff , _retry_overload
7071from pymongo .asynchronous .settings import TopologySettings
7172from pymongo .asynchronous .topology import Topology , _ErrorContext
7273from pymongo .client_options import ClientOptions
@@ -2398,6 +2399,7 @@ async def list_database_names(
23982399 return [doc ["name" ] async for doc in res ]
23992400
24002401 @_csot .apply
2402+ @_retry_overload
24012403 async def drop_database (
24022404 self ,
24032405 name_or_database : Union [str , database .AsyncDatabase [_DocumentTypeArg ]],
@@ -2735,6 +2737,7 @@ def __init__(
27352737 ):
27362738 self ._last_error : Optional [Exception ] = None
27372739 self ._retrying = False
2740+ self ._always_retryable = False
27382741 self ._multiple_retries = _csot .get_timeout () is not None
27392742 self ._client = mongo_client
27402743
@@ -2783,14 +2786,22 @@ async def run(self) -> T:
27832786 # most likely be a waste of time.
27842787 raise
27852788 except PyMongoError as exc :
2789+ always_retryable = False
2790+ overloaded = False
2791+ exc_to_check = exc
27862792 # Execute specialized catch on read
27872793 if self ._is_read :
27882794 if isinstance (exc , (ConnectionFailure , OperationFailure )):
27892795 # ConnectionFailures do not supply a code property
27902796 exc_code = getattr (exc , "code" , None )
2791- if self ._is_not_eligible_for_retry () or (
2792- isinstance (exc , OperationFailure )
2793- and exc_code not in helpers_shared ._RETRYABLE_ERROR_CODES
2797+ always_retryable = exc .has_error_label ("Retryable" )
2798+ overloaded = exc .has_error_label ("SystemOverloaded" )
2799+ if not always_retryable and (
2800+ self ._is_not_eligible_for_retry ()
2801+ or (
2802+ isinstance (exc , OperationFailure )
2803+ and exc_code not in helpers_shared ._RETRYABLE_ERROR_CODES
2804+ )
27942805 ):
27952806 raise
27962807 self ._retrying = True
@@ -2801,19 +2812,22 @@ async def run(self) -> T:
28012812
28022813 # Specialized catch on write operation
28032814 if not self ._is_read :
2804- if not self ._retryable :
2815+ if isinstance (exc , ClientBulkWriteException ) and isinstance (
2816+ exc .error , PyMongoError
2817+ ):
2818+ exc_to_check = exc .error
2819+ retryable_write_label = exc_to_check .has_error_label ("RetryableWriteError" )
2820+ always_retryable = exc_to_check .has_error_label ("Retryable" )
2821+ overloaded = exc_to_check .has_error_label ("SystemOverloaded" )
2822+ if not self ._retryable and not always_retryable :
28052823 raise
2806- if isinstance (exc , ClientBulkWriteException ) and exc .error :
2807- retryable_write_error_exc = isinstance (
2808- exc .error , PyMongoError
2809- ) and exc .error .has_error_label ("RetryableWriteError" )
2810- else :
2811- retryable_write_error_exc = exc .has_error_label ("RetryableWriteError" )
2812- if retryable_write_error_exc :
2824+ if retryable_write_label or always_retryable :
28132825 assert self ._session
28142826 await self ._session ._unpin ()
2815- if not retryable_write_error_exc or self ._is_not_eligible_for_retry ():
2816- if exc .has_error_label ("NoWritesPerformed" ) and self ._last_error :
2827+ if not always_retryable and (
2828+ not retryable_write_label or self ._is_not_eligible_for_retry ()
2829+ ):
2830+ if exc_to_check .has_error_label ("NoWritesPerformed" ) and self ._last_error :
28172831 raise self ._last_error from exc
28182832 else :
28192833 raise
@@ -2822,14 +2836,24 @@ async def run(self) -> T:
28222836 self ._bulk .retrying = True
28232837 else :
28242838 self ._retrying = True
2825- if not exc .has_error_label ("NoWritesPerformed" ):
2839+ if not exc_to_check .has_error_label ("NoWritesPerformed" ):
28262840 self ._last_error = exc
28272841 if self ._last_error is None :
28282842 self ._last_error = exc
28292843
28302844 if self ._client .topology_description .topology_type == TOPOLOGY_TYPE .Sharded :
28312845 self ._deprioritized_servers .append (self ._server )
28322846
2847+ self ._always_retryable = always_retryable
2848+ if always_retryable :
2849+ if self ._attempt_number > _MAX_RETRIES :
2850+ if exc_to_check .has_error_label ("NoWritesPerformed" ) and self ._last_error :
2851+ raise self ._last_error from exc
2852+ else :
2853+ raise
2854+ if overloaded :
2855+ await _backoff (self ._attempt_number )
2856+
28332857 def _is_not_eligible_for_retry (self ) -> bool :
28342858 """Checks if the exchange is not eligible for retry"""
28352859 return not self ._retryable or (self ._is_retrying () and not self ._multiple_retries )
@@ -2891,7 +2915,7 @@ async def _write(self) -> T:
28912915 and conn .supports_sessions
28922916 )
28932917 is_mongos = conn .is_mongos
2894- if not sessions_supported :
2918+ if not self . _always_retryable and not sessions_supported :
28952919 # A retry is not possible because this server does
28962920 # not support sessions raise the last error.
28972921 self ._check_last_error ()
@@ -2923,7 +2947,7 @@ async def _read(self) -> T:
29232947 conn ,
29242948 read_pref ,
29252949 ):
2926- if self ._retrying and not self ._retryable :
2950+ if self ._retrying and not self ._retryable and not self . _always_retryable :
29272951 self ._check_last_error ()
29282952 if self ._retrying :
29292953 _debug_log (
0 commit comments