diff --git a/pymongo/mongo_client.py b/pymongo/mongo_client.py index 7920b6a53..5d4703c0e 100644 --- a/pymongo/mongo_client.py +++ b/pymongo/mongo_client.py @@ -1421,12 +1421,15 @@ class MongoClient(common.BaseObject): # be a persistent outage. Attempting to retry in this case will # most likely be a waste of time. raise - except Exception as exc: + except PyMongoError as exc: if not retryable: raise - # Add the RetryableWriteError label. - if (not _retryable_writes_error(exc, max_wire_version) - or is_retrying()): + # Add the RetryableWriteError label, if applicable. + _add_retryable_write_error(exc, max_wire_version) + retryable_error = exc.has_error_label("RetryableWriteError") + if retryable_error: + session._unpin_mongos() + if is_retrying() or not retryable_error: raise if bulk: bulk.retrying = True @@ -2170,7 +2173,7 @@ def _retryable_error_doc(exc): return None -def _retryable_writes_error(exc, max_wire_version): +def _add_retryable_write_error(exc, max_wire_version): doc = _retryable_error_doc(exc) if doc: code = doc.get('code', 0) @@ -2183,18 +2186,18 @@ def _retryable_writes_error(exc, max_wire_version): "to your connection string.") raise OperationFailure(errmsg, code, exc.details) if max_wire_version >= 9: - # MongoDB 4.4+ utilizes RetryableWriteError. - return 'RetryableWriteError' in doc.get('errorLabels', []) + # In MongoDB 4.4+, the server reports the error labels. + for label in doc.get('errorLabels', []): + exc._add_error_label(label) else: if code in helpers._RETRYABLE_ERROR_CODES: exc._add_error_label("RetryableWriteError") - return True - return False - if isinstance(exc, ConnectionFailure): + # Connection errors are always retryable except NotMasterError which is + # handled above. + if (isinstance(exc, ConnectionFailure) and + not isinstance(exc, NotMasterError)): exc._add_error_label("RetryableWriteError") - return True - return False class _MongoClientErrorHandler(object): @@ -2232,7 +2235,8 @@ class _MongoClientErrorHandler(object): self.session._server_session.mark_dirty() if issubclass(exc_type, PyMongoError): - if exc_val.has_error_label("TransientTransactionError"): + if (exc_val.has_error_label("TransientTransactionError") or + exc_val.has_error_label("RetryableWriteError")): self.session._unpin_mongos() err_ctx = _ErrorContext( diff --git a/test/test_examples.py b/test/test_examples.py index 16e1936d5..c0913b34c 100644 --- a/test/test_examples.py +++ b/test/test_examples.py @@ -842,7 +842,6 @@ class TestSampleShellCommands(IntegrationTest): class TestTransactionExamples(IntegrationTest): - @client_context.require_version_max(4, 4, 99) # PYTHON-2154 skip on 4.5+ @client_context.require_transactions def test_transactions(self): # Transaction examples diff --git a/test/test_transactions.py b/test/test_transactions.py index ff707c5f9..85345e274 100644 --- a/test/test_transactions.py +++ b/test/test_transactions.py @@ -415,13 +415,7 @@ def create_test(scenario_def, test, name): @client_context.require_test_commands @client_context.require_transactions def run_scenario(self): - try: - self.run_scenario(scenario_def, test) - except OperationFailure as exc: - if (client_context.version.at_least(4, 5) and - client_context.is_mongos and exc.code == 13388): - self.skipTest('PYTHON-2189 Ignoring StaleConfig error: %r' % ( - exc.details)) + self.run_scenario(scenario_def, test) return run_scenario