PYTHON-2082 Retryable writes use the RetryableWriteError label

Use retryable write logic for transaction commit/abort.
Do not assign the TransientTransactionError label to errors outside a
transaction.
This commit is contained in:
Shane Harvey 2020-05-08 18:12:20 -07:00
parent 48df9b088f
commit 45a7963aac
34 changed files with 2537 additions and 193 deletions

View File

@ -98,13 +98,11 @@ Classes
"""
import collections
import os
import sys
import uuid
from bson.binary import Binary
from bson.int64 import Int64
from bson.py3compat import abc, integer_types, reraise_instance
from bson.py3compat import abc, integer_types
from bson.son import SON
from bson.timestamp import Timestamp
@ -114,7 +112,6 @@ from pymongo.errors import (ConfigurationError,
InvalidOperation,
OperationFailure,
PyMongoError,
ServerSelectionTimeoutError,
WTimeoutError)
from pymongo.helpers import _RETRYABLE_ERROR_CODES
from pymongo.read_concern import ReadConcern
@ -295,6 +292,7 @@ class _Transaction(object):
self.sharded = False
self.pinned_address = None
self.recovery_token = None
self.attempt = 0
def active(self):
return self.state in (_TxnState.STARTING, _TxnState.IN_PROGRESS)
@ -304,12 +302,13 @@ class _Transaction(object):
self.sharded = False
self.pinned_address = None
self.recovery_token = None
self.attempt = 0
def _reraise_with_unknown_commit(exc):
"""Re-raise an exception with the UnknownTransactionCommitResult label."""
exc._add_error_label("UnknownTransactionCommitResult")
reraise_instance(exc, trace=sys.exc_info()[2])
raise
def _max_time_expired_error(exc):
@ -579,7 +578,6 @@ class ClientSession(object):
.. versionadded:: 3.7
"""
self._check_ended()
retry = False
state = self._transaction.state
if state is _TxnState.NONE:
raise InvalidOperation("No transaction started")
@ -594,10 +592,9 @@ class ClientSession(object):
# We're explicitly retrying the commit, move the state back to
# "in progress" so that in_transaction returns true.
self._transaction.state = _TxnState.IN_PROGRESS
retry = True
try:
self._finish_transaction_with_retry("commitTransaction", retry)
self._finish_transaction_with_retry("commitTransaction")
except ConnectionFailure as exc:
# We do not know if the commit was successfully applied on the
# server or if it satisfied the provided write concern, set the
@ -640,44 +637,25 @@ class ClientSession(object):
"Cannot call abortTransaction after calling commitTransaction")
try:
self._finish_transaction_with_retry("abortTransaction", False)
self._finish_transaction_with_retry("abortTransaction")
except (OperationFailure, ConnectionFailure):
# The transactions spec says to ignore abortTransaction errors.
pass
finally:
self._transaction.state = _TxnState.ABORTED
def _finish_transaction_with_retry(self, command_name, explict_retry):
def _finish_transaction_with_retry(self, command_name):
"""Run commit or abort with one retry after any retryable error.
:Parameters:
- `command_name`: Either "commitTransaction" or "abortTransaction".
- `explict_retry`: True when this is an explict commit retry attempt,
ie the application called session.commit_transaction() twice.
"""
# This can be refactored with MongoClient._retry_with_session.
try:
return self._finish_transaction(command_name, explict_retry)
except ServerSelectionTimeoutError:
raise
except ConnectionFailure as exc:
try:
return self._finish_transaction(command_name, True)
except ServerSelectionTimeoutError:
# Raise the original error so the application can infer that
# an attempt was made.
raise exc
except OperationFailure as exc:
if exc.code not in _RETRYABLE_ERROR_CODES:
raise
try:
return self._finish_transaction(command_name, True)
except ServerSelectionTimeoutError:
# Raise the original error so the application can infer that
# an attempt was made.
raise exc
def func(session, sock_info, retryable):
return self._finish_transaction(sock_info, command_name)
return self._client._retry_internal(True, func, self, None)
def _finish_transaction(self, command_name, retrying):
def _finish_transaction(self, sock_info, command_name):
self._transaction.attempt += 1
opts = self._transaction.opts
wc = opts.write_concern
cmd = SON([(command_name, 1)])
@ -688,7 +666,7 @@ class ClientSession(object):
# Transaction spec says that after the initial commit attempt,
# subsequent commitTransaction commands should be upgraded to use
# w:"majority" and set a default value of 10 seconds for wtimeout.
if retrying:
if self._transaction.attempt > 1:
wc_doc = wc.document
wc_doc["w"] = "majority"
wc_doc.setdefault("wtimeout", 10000)
@ -697,13 +675,12 @@ class ClientSession(object):
if self._transaction.recovery_token:
cmd['recoveryToken'] = self._transaction.recovery_token
with self._client._socket_for_writes(self) as sock_info:
return self._client.admin._command(
sock_info,
cmd,
session=self,
write_concern=wc,
parse_write_concern_error=True)
return self._client.admin._command(
sock_info,
cmd,
session=self,
write_concern=wc,
parse_write_concern_error=True)
def _advance_cluster_time(self, cluster_time):
"""Internal cluster time helper."""

View File

@ -48,7 +48,7 @@ class PyMongoError(Exception):
def _remove_error_label(self, label):
"""Remove the given label from this error."""
self._error_labels.remove(label)
self._error_labels.discard(label)
if sys.version_info[0] == 2:
def __str__(self):
@ -68,12 +68,6 @@ class ProtocolError(PyMongoError):
class ConnectionFailure(PyMongoError):
"""Raised when a connection to the database cannot be made or is lost."""
def __init__(self, message='', error_labels=None):
if error_labels is None:
# Connection errors are transient errors by default.
error_labels = ("TransientTransactionError",)
super(ConnectionFailure, self).__init__(
message, error_labels=error_labels)
class AutoReconnect(ConnectionFailure):
@ -89,7 +83,10 @@ class AutoReconnect(ConnectionFailure):
Subclass of :exc:`~pymongo.errors.ConnectionFailure`.
"""
def __init__(self, message='', errors=None):
super(AutoReconnect, self).__init__(message)
error_labels = None
if errors is not None and isinstance(errors, dict):
error_labels = errors.get('errorLabels')
super(AutoReconnect, self).__init__(message, error_labels)
self.errors = self.details = errors or []

View File

@ -59,6 +59,7 @@ from pymongo.errors import (AutoReconnect,
ConfigurationError,
ConnectionFailure,
InvalidOperation,
NotMasterError,
OperationFailure,
PyMongoError,
ServerSelectionTimeoutError)
@ -1265,7 +1266,9 @@ class MongoClient(common.BaseObject):
session._pin_mongos(server)
return server
except PyMongoError as exc:
if session and exc.has_error_label("TransientTransactionError"):
# Server selection errors in a transaction are transient.
if session and session.in_transaction:
exc._add_error_label("TransientTransactionError")
session._unpin_mongos()
raise
@ -1361,6 +1364,11 @@ class MongoClient(common.BaseObject):
"""
retryable = (retryable and self.retry_writes
and session and not session.in_transaction)
return self._retry_internal(retryable, func, session, bulk)
def _retry_internal(self, retryable, func, session, bulk):
"""Internal retryable write helper."""
max_wire_version = 0
last_error = None
retrying = False
@ -1369,7 +1377,7 @@ class MongoClient(common.BaseObject):
# Increment the transaction id up front to ensure any retry attempt
# will use the proper txnNumber, even if server or socket selection
# fails before the command can be sent.
if retryable:
if retryable and session and not session.in_transaction:
session._start_retryable_write()
if bulk:
bulk.started_retryable_write = True
@ -1381,6 +1389,7 @@ class MongoClient(common.BaseObject):
session is not None and
server.description.retryable_writes_supported)
with self._get_socket(server, session) as sock_info:
max_wire_version = sock_info.max_wire_version
if retryable and not supports_session:
if is_retrying():
# A retry is not possible because this server does
@ -1398,40 +1407,12 @@ class MongoClient(common.BaseObject):
# be a persistent outage. Attempting to retry in this case will
# most likely be a waste of time.
raise
except ConnectionFailure as exc:
if not retryable or is_retrying():
except Exception as exc:
if not retryable:
raise
if bulk:
bulk.retrying = True
else:
retrying = True
last_error = exc
except BulkWriteError as exc:
if not retryable or is_retrying():
raise
# Check the last writeConcernError to determine if this
# BulkWriteError is retryable.
wces = exc.details['writeConcernErrors']
wce = wces[-1] if wces else {}
if wce.get('code', 0) not in helpers._RETRYABLE_ERROR_CODES:
raise
if bulk:
bulk.retrying = True
else:
retrying = True
last_error = exc
except OperationFailure as exc:
# retryWrites on MMAPv1 should raise an actionable error.
if (exc.code == 20 and
str(exc).startswith("Transaction numbers")):
errmsg = (
"This MongoDB deployment does not support "
"retryable writes. Please add retryWrites=false "
"to your connection string.")
raise OperationFailure(errmsg, exc.code, exc.details)
if not retryable or is_retrying():
raise
if exc.code not in helpers._RETRYABLE_ERROR_CODES:
# Add the RetryableWriteError label.
if (not _retryable_writes_error(exc, max_wire_version)
or is_retrying()):
raise
if bulk:
bulk.retrying = True
@ -2162,26 +2143,66 @@ class MongoClient(common.BaseObject):
next = __next__
def _retryable_error_doc(exc):
"""Return the server response from PyMongo exception or None."""
if isinstance(exc, BulkWriteError):
# Check the last writeConcernError to determine if this
# BulkWriteError is retryable.
wces = exc.details['writeConcernErrors']
wce = wces[-1] if wces else None
return wce
if isinstance(exc, (NotMasterError, OperationFailure)):
return exc.details
return None
def _retryable_writes_error(exc, max_wire_version):
doc = _retryable_error_doc(exc)
if doc:
code = doc.get('code', 0)
# retryWrites on MMAPv1 should raise an actionable error.
if (code == 20 and
str(exc).startswith("Transaction numbers")):
errmsg = (
"This MongoDB deployment does not support "
"retryable writes. Please add retryWrites=false "
"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', [])
else:
if code in helpers._RETRYABLE_ERROR_CODES:
exc._add_error_label("RetryableWriteError")
return True
return False
if isinstance(exc, ConnectionFailure):
exc._add_error_label("RetryableWriteError")
return True
return False
class _MongoClientErrorHandler(object):
"""Error handler for MongoClient."""
__slots__ = ('_client', '_server_address', '_session',
'_max_wire_version', '_sock_generation')
"""Handle errors raised when executing an operation."""
__slots__ = ('client', 'server_address', 'session', 'max_wire_version',
'sock_generation')
def __init__(self, client, server, session):
self._client = client
self._server_address = server.description.address
self._session = session
self._max_wire_version = common.MIN_WIRE_VERSION
self.client = client
self.server_address = server.description.address
self.session = session
self.max_wire_version = common.MIN_WIRE_VERSION
# XXX: When get_socket fails, this generation could be out of date:
# "Note that when a network error occurs before the handshake
# completes then the error's generation number is the generation
# of the pool at the time the connection attempt was started."
self._sock_generation = server.pool.generation
self.sock_generation = server.pool.generation
def contribute_socket(self, sock_info):
"""Provide socket information to the error handler."""
self._max_wire_version = sock_info.max_wire_version
self._sock_generation = sock_info.generation
self.max_wire_version = sock_info.max_wire_version
self.sock_generation = sock_info.generation
def __enter__(self):
return self
@ -2190,15 +2211,16 @@ class _MongoClientErrorHandler(object):
if exc_type is None:
return
if self.session:
if issubclass(exc_type, ConnectionFailure):
if self.session.in_transaction:
exc_val._add_error_label("TransientTransactionError")
self.session._server_session.mark_dirty()
if issubclass(exc_type, PyMongoError):
if exc_val.has_error_label("TransientTransactionError"):
self.session._unpin_mongos()
err_ctx = _ErrorContext(
exc_val, self._max_wire_version, self._sock_generation)
self._client._topology.handle_error(self._server_address, err_ctx)
if issubclass(exc_type, PyMongoError):
if self._session and exc_val.has_error_label(
"TransientTransactionError"):
self._session._unpin_mongos()
if issubclass(exc_type, ConnectionFailure):
if self._session:
self._session._server_session.mark_dirty()
exc_val, self.max_wire_version, self.sock_generation)
self.client._topology.handle_error(self.server_address, err_ctx)

View File

@ -0,0 +1,182 @@
{
"runOn": [
{
"minServerVersion": "4.3.1",
"topology": [
"replicaset",
"sharded"
]
}
],
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
],
"tests": [
{
"description": "BulkWrite succeeds with RetryableWriteError from server",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"update"
],
"errorCode": 112,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "deleteOne",
"arguments": {
"filter": {
"_id": 1
}
}
},
{
"name": "insertOne",
"arguments": {
"document": {
"_id": 3,
"x": 33
}
}
},
{
"name": "updateOne",
"arguments": {
"filter": {
"_id": 2
},
"update": {
"$inc": {
"x": 1
}
}
}
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"result": {
"deletedCount": 1,
"insertedCount": 1,
"insertedIds": {
"1": 3
},
"matchedCount": 1,
"modifiedCount": 1,
"upsertedCount": 0,
"upsertedIds": {}
},
"collection": {
"data": [
{
"_id": 2,
"x": 23
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "BulkWrite fails if server does not return RetryableWriteError",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"update"
],
"errorCode": 11600,
"errorLabels": []
}
},
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "deleteOne",
"arguments": {
"filter": {
"_id": 1
}
}
},
{
"name": "insertOne",
"arguments": {
"document": {
"_id": 3,
"x": 33
}
}
},
{
"name": "updateOne",
"arguments": {
"filter": {
"_id": 2
},
"update": {
"$inc": {
"x": 1
}
}
}
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsOmit": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
}
]
}

View File

@ -35,7 +35,10 @@
"failCommands": [
"update"
],
"errorCode": 189
"errorCode": 189,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
@ -117,7 +120,10 @@
],
"writeConcernError": {
"code": 91,
"errmsg": "Replication is being shut down"
"errmsg": "Replication is being shut down",
"errorLabels": [
"RetryableWriteError"
]
}
}
},
@ -186,6 +192,81 @@
]
}
}
},
{
"description": "BulkWrite fails with a RetryableWriteError label after two connection failures",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 2
},
"data": {
"failCommands": [
"update"
],
"closeConnection": true
}
},
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "deleteOne",
"arguments": {
"filter": {
"_id": 1
}
}
},
{
"name": "insertOne",
"arguments": {
"document": {
"_id": 3,
"x": 33
}
}
},
{
"name": "updateOne",
"arguments": {
"filter": {
"_id": 2
},
"update": {
"$inc": {
"x": 1
}
}
}
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsContain": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
}
]
}

View File

@ -0,0 +1,106 @@
{
"runOn": [
{
"minServerVersion": "4.3.1",
"topology": [
"replicaset",
"sharded"
]
}
],
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
],
"tests": [
{
"description": "DeleteOne succeeds with RetryableWriteError from server",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"delete"
],
"errorCode": 112,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
"name": "deleteOne",
"arguments": {
"filter": {
"_id": 1
}
}
},
"outcome": {
"result": {
"deletedCount": 1
},
"collection": {
"data": [
{
"_id": 2,
"x": 22
}
]
}
}
},
{
"description": "DeleteOne fails if server does not return RetryableWriteError",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"delete"
],
"errorCode": 11600,
"errorLabels": []
}
},
"operation": {
"name": "deleteOne",
"arguments": {
"filter": {
"_id": 1
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsOmit": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
]
}
}
}
]
}

View File

@ -35,7 +35,10 @@
"failCommands": [
"delete"
],
"errorCode": 189
"errorCode": 189,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
@ -73,7 +76,10 @@
],
"writeConcernError": {
"code": 91,
"errmsg": "Replication is being shut down"
"errmsg": "Replication is being shut down",
"errorLabels": [
"RetryableWriteError"
]
}
}
},
@ -98,6 +104,49 @@
]
}
}
},
{
"description": "DeleteOne fails with RetryableWriteError label after two connection failures",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 2
},
"data": {
"failCommands": [
"delete"
],
"closeConnection": true
}
},
"operation": {
"name": "deleteOne",
"arguments": {
"filter": {
"_id": 1
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsContain": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
]
}
}
}
]
}

View File

@ -0,0 +1,117 @@
{
"runOn": [
{
"minServerVersion": "4.3.1",
"topology": [
"replicaset",
"sharded"
]
}
],
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
],
"tests": [
{
"description": "FindOneAndDelete succeeds with RetryableWriteError from server",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"findAndModify"
],
"errorCode": 112,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
"name": "findOneAndDelete",
"arguments": {
"filter": {
"x": {
"$gte": 11
}
},
"sort": {
"x": 1
}
}
},
"outcome": {
"result": {
"_id": 1,
"x": 11
},
"collection": {
"data": [
{
"_id": 2,
"x": 22
}
]
}
}
},
{
"description": "FindOneAndDelete fails if server does not return RetryableWriteError",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"findAndModify"
],
"errorCode": 11600,
"errorLabels": []
}
},
"operation": {
"name": "findOneAndDelete",
"arguments": {
"filter": {
"x": {
"$gte": 11
}
},
"sort": {
"x": 1
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsOmit": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
]
}
}
}
]
}

View File

@ -35,7 +35,10 @@
"failCommands": [
"findAndModify"
],
"errorCode": 189
"errorCode": 189,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
@ -79,7 +82,10 @@
],
"writeConcernError": {
"code": 91,
"errmsg": "Replication is being shut down"
"errmsg": "Replication is being shut down",
"errorLabels": [
"RetryableWriteError"
]
}
}
},
@ -110,6 +116,54 @@
]
}
}
},
{
"description": "FindOneAndDelete fails with a RetryableWriteError label after two connection failures",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 2
},
"data": {
"failCommands": [
"findAndModify"
],
"closeConnection": true
}
},
"operation": {
"name": "findOneAndDelete",
"arguments": {
"filter": {
"x": {
"$gte": 11
}
},
"sort": {
"x": 1
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsContain": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
]
}
}
}
]
}

View File

@ -0,0 +1,121 @@
{
"runOn": [
{
"minServerVersion": "4.3.1",
"topology": [
"replicaset",
"sharded"
]
}
],
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
],
"tests": [
{
"description": "FindOneAndReplace succeeds with RetryableWriteError from server",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"findAndModify"
],
"errorCode": 112,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
"name": "findOneAndReplace",
"arguments": {
"filter": {
"_id": 1
},
"replacement": {
"_id": 1,
"x": 111
},
"returnDocument": "Before"
}
},
"outcome": {
"result": {
"_id": 1,
"x": 11
},
"collection": {
"data": [
{
"_id": 1,
"x": 111
},
{
"_id": 2,
"x": 22
}
]
}
}
},
{
"description": "FindOneAndReplace fails if server does not return RetryableWriteError",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"findAndModify"
],
"errorCode": 11600,
"errorLabels": []
}
},
"operation": {
"name": "findOneAndReplace",
"arguments": {
"filter": {
"_id": 1
},
"replacement": {
"_id": 1,
"x": 111
},
"returnDocument": "Before"
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsOmit": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
]
}
}
}
]
}

View File

@ -35,7 +35,10 @@
"failCommands": [
"findAndModify"
],
"errorCode": 189
"errorCode": 189,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
@ -83,7 +86,10 @@
],
"writeConcernError": {
"code": 91,
"errmsg": "Replication is being shut down"
"errmsg": "Replication is being shut down",
"errorLabels": [
"RetryableWriteError"
]
}
}
},
@ -118,6 +124,54 @@
]
}
}
},
{
"description": "FindOneAndReplace fails with a RetryableWriteError label after two connection failures",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 2
},
"data": {
"failCommands": [
"findAndModify"
],
"closeConnection": true
}
},
"operation": {
"name": "findOneAndReplace",
"arguments": {
"filter": {
"_id": 1
},
"replacement": {
"_id": 1,
"x": 111
},
"returnDocument": "Before"
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsContain": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
]
}
}
}
]
}

View File

@ -0,0 +1,123 @@
{
"runOn": [
{
"minServerVersion": "4.3.1",
"topology": [
"replicaset",
"sharded"
]
}
],
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
],
"tests": [
{
"description": "FindOneAndUpdate succeeds with RetryableWriteError from server",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"findAndModify"
],
"errorCode": 112,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
"name": "findOneAndUpdate",
"arguments": {
"filter": {
"_id": 1
},
"update": {
"$inc": {
"x": 1
}
},
"returnDocument": "Before"
}
},
"outcome": {
"result": {
"_id": 1,
"x": 11
},
"collection": {
"data": [
{
"_id": 1,
"x": 12
},
{
"_id": 2,
"x": 22
}
]
}
}
},
{
"description": "FindOneAndUpdate fails if server does not return RetryableWriteError",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"findAndModify"
],
"errorCode": 11600,
"errorLabels": []
}
},
"operation": {
"name": "findOneAndUpdate",
"arguments": {
"filter": {
"_id": 1
},
"update": {
"$inc": {
"x": 1
}
},
"returnDocument": "Before"
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsOmit": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
]
}
}
}
]
}

View File

@ -35,7 +35,10 @@
"failCommands": [
"findAndModify"
],
"errorCode": 189
"errorCode": 189,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
@ -84,7 +87,10 @@
],
"writeConcernError": {
"code": 91,
"errmsg": "Replication is being shut down"
"errmsg": "Replication is being shut down",
"errorLabels": [
"RetryableWriteError"
]
}
}
},
@ -120,6 +126,55 @@
]
}
}
},
{
"description": "FindOneAndUpdate fails with a RetryableWriteError label after two connection failures",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 2
},
"data": {
"failCommands": [
"findAndModify"
],
"closeConnection": true
}
},
"operation": {
"name": "findOneAndUpdate",
"arguments": {
"filter": {
"_id": 1
},
"update": {
"$inc": {
"x": 1
}
},
"returnDocument": "Before"
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsContain": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
]
}
}
}
]
}

View File

@ -0,0 +1,129 @@
{
"runOn": [
{
"minServerVersion": "4.3.1",
"topology": [
"replicaset",
"sharded"
]
}
],
"data": [
{
"_id": 1,
"x": 11
}
],
"tests": [
{
"description": "InsertMany succeeds with RetryableWriteError from server",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"insert"
],
"errorCode": 112,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
"name": "insertMany",
"arguments": {
"documents": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"result": {
"insertedIds": {
"0": 2,
"1": 3
}
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "InsertMany fails if server does not return RetryableWriteError",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"insert"
],
"errorCode": 11600,
"errorLabels": []
}
},
"operation": {
"name": "insertMany",
"arguments": {
"documents": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsOmit": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
}
]
}
}
}
]
}

View File

@ -31,7 +31,10 @@
"failCommands": [
"insert"
],
"errorCode": 189
"errorCode": 189,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
@ -90,7 +93,10 @@
],
"writeConcernError": {
"code": 91,
"errmsg": "Replication is being shut down"
"errmsg": "Replication is being shut down",
"errorLabels": [
"RetryableWriteError"
]
}
}
},
@ -136,6 +142,55 @@
]
}
}
},
{
"description": "InsertMany fails with a RetryableWriteError label after two connection failures",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 2
},
"data": {
"failCommands": [
"insert"
],
"closeConnection": true
}
},
"operation": {
"name": "insertMany",
"arguments": {
"documents": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsContain": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
}
]
}
}
}
]
}

View File

@ -0,0 +1,90 @@
{
"runOn": [
{
"minServerVersion": "4.3.1",
"topology": [
"replicaset",
"sharded"
]
}
],
"data": [],
"tests": [
{
"description": "InsertOne succeeds with RetryableWriteError from server",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"insert"
],
"errorCode": 112,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
"name": "insertOne",
"arguments": {
"document": {
"_id": 1,
"x": 11
}
}
},
"outcome": {
"result": {
"insertedId": 1
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
}
]
}
}
},
{
"description": "InsertOne fails if server does not return RetryableWriteError",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"insert"
],
"errorCode": 11600,
"errorLabels": []
}
},
"operation": {
"name": "insertOne",
"arguments": {
"document": {
"_id": 1,
"x": 11
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsOmit": [
"RetryableWriteError"
]
},
"collection": {
"data": []
}
}
}
]
}

View File

@ -69,6 +69,53 @@
}
}
},
{
"description": "InsertOne fails after connection failure when retryWrites option is false",
"clientOptions": {
"retryWrites": false
},
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"insert"
],
"closeConnection": true
}
},
"operation": {
"name": "insertOne",
"arguments": {
"document": {
"_id": 3,
"x": 33
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsOmit": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
]
}
}
},
{
"description": "InsertOne succeeds after NotMaster",
"failPoint": {
@ -81,6 +128,9 @@
"insert"
],
"errorCode": 10107,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -127,6 +177,9 @@
"insert"
],
"errorCode": 13436,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -173,6 +226,9 @@
"insert"
],
"errorCode": 13435,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -219,6 +275,9 @@
"insert"
],
"errorCode": 11602,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -265,6 +324,9 @@
"insert"
],
"errorCode": 11600,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -311,6 +373,9 @@
"insert"
],
"errorCode": 189,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -357,6 +422,9 @@
"insert"
],
"errorCode": 91,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -403,6 +471,9 @@
"insert"
],
"errorCode": 7,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -449,6 +520,9 @@
"insert"
],
"errorCode": 6,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -495,6 +569,9 @@
"insert"
],
"errorCode": 9001,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -541,6 +618,9 @@
"insert"
],
"errorCode": 89,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -586,10 +666,11 @@
"failCommands": [
"insert"
],
"writeConcernError": {
"code": 262,
"closeConnection": false
}
"errorCode": 262,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
"operation": {
@ -649,6 +730,11 @@
},
"outcome": {
"error": true,
"result": {
"errorLabelsOmit": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
@ -676,7 +762,10 @@
],
"writeConcernError": {
"code": 11600,
"errmsg": "Replication is being shut down"
"errmsg": "Replication is being shut down",
"errorLabels": [
"RetryableWriteError"
]
}
}
},
@ -724,7 +813,10 @@
],
"writeConcernError": {
"code": 11602,
"errmsg": "Replication is being shut down"
"errmsg": "Replication is being shut down",
"errorLabels": [
"RetryableWriteError"
]
}
}
},
@ -772,7 +864,10 @@
],
"writeConcernError": {
"code": 189,
"errmsg": "Replication is being shut down"
"errmsg": "Replication is being shut down",
"errorLabels": [
"RetryableWriteError"
]
}
}
},
@ -820,7 +915,10 @@
],
"writeConcernError": {
"code": 91,
"errmsg": "Replication is being shut down"
"errmsg": "Replication is being shut down",
"errorLabels": [
"RetryableWriteError"
]
}
}
},
@ -883,6 +981,11 @@
},
"outcome": {
"error": true,
"result": {
"errorLabelsContain": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
@ -929,6 +1032,11 @@
},
"outcome": {
"error": true,
"result": {
"errorLabelsOmit": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
@ -979,6 +1087,11 @@
},
"outcome": {
"error": true,
"result": {
"errorLabelsOmit": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
@ -996,6 +1109,50 @@
]
}
}
},
{
"description": "InsertOne fails with a RetryableWriteError label after two connection failures",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 2
},
"data": {
"failCommands": [
"insert"
],
"closeConnection": true
}
},
"operation": {
"name": "insertOne",
"arguments": {
"document": {
"_id": 3,
"x": 33
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsContain": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
]
}
}
}
]
}

View File

@ -0,0 +1,120 @@
{
"runOn": [
{
"minServerVersion": "4.3.1",
"topology": [
"replicaset",
"sharded"
]
}
],
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
],
"tests": [
{
"description": "ReplaceOne succeeds with RetryableWriteError from server",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"update"
],
"errorCode": 112,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 1
},
"replacement": {
"_id": 1,
"x": 111
}
}
},
"outcome": {
"result": {
"matchedCount": 1,
"modifiedCount": 1,
"upsertedCount": 0
},
"collection": {
"data": [
{
"_id": 1,
"x": 111
},
{
"_id": 2,
"x": 22
}
]
}
}
},
{
"description": "ReplaceOne fails if server does not return RetryableWriteError",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"update"
],
"errorCode": 11600,
"errorLabels": []
}
},
"operation": {
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 1
},
"replacement": {
"_id": 1,
"x": 111
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsOmit": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
]
}
}
}
]
}

View File

@ -35,7 +35,10 @@
"failCommands": [
"update"
],
"errorCode": 189
"errorCode": 189,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
@ -83,7 +86,10 @@
],
"writeConcernError": {
"code": 91,
"errmsg": "Replication is being shut down"
"errmsg": "Replication is being shut down",
"errorLabels": [
"RetryableWriteError"
]
}
}
},
@ -118,6 +124,53 @@
]
}
}
},
{
"description": "ReplaceOne fails with a RetryableWriteError label after two connection failures",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 2
},
"data": {
"failCommands": [
"update"
],
"closeConnection": true
}
},
"operation": {
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 1
},
"replacement": {
"_id": 1,
"x": 111
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsContain": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
]
}
}
}
]
}

View File

@ -0,0 +1,122 @@
{
"runOn": [
{
"minServerVersion": "4.3.1",
"topology": [
"replicaset",
"sharded"
]
}
],
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
],
"tests": [
{
"description": "UpdateOne succeeds with RetryableWriteError from server",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"update"
],
"errorCode": 112,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
"name": "updateOne",
"arguments": {
"filter": {
"_id": 1
},
"update": {
"$inc": {
"x": 1
}
}
}
},
"outcome": {
"result": {
"matchedCount": 1,
"modifiedCount": 1,
"upsertedCount": 0
},
"collection": {
"data": [
{
"_id": 1,
"x": 12
},
{
"_id": 2,
"x": 22
}
]
}
}
},
{
"description": "UpdateOne fails if server does not return RetryableWriteError",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"update"
],
"errorCode": 11600,
"errorLabels": []
}
},
"operation": {
"name": "updateOne",
"arguments": {
"filter": {
"_id": 1
},
"update": {
"$inc": {
"x": 1
}
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsOmit": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
]
}
}
}
]
}

View File

@ -35,7 +35,10 @@
"failCommands": [
"update"
],
"errorCode": 189
"errorCode": 189,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operation": {
@ -84,7 +87,10 @@
],
"writeConcernError": {
"code": 91,
"errmsg": "Replication is being shut down"
"errmsg": "Replication is being shut down",
"errorLabels": [
"RetryableWriteError"
]
}
}
},
@ -120,6 +126,54 @@
]
}
}
},
{
"description": "UpdateOne fails with a RetryableWriteError label after two connection failures",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 2
},
"data": {
"failCommands": [
"update"
],
"closeConnection": true
}
},
"operation": {
"name": "updateOne",
"arguments": {
"filter": {
"_id": 1
},
"update": {
"$inc": {
"x": 1
}
}
}
},
"outcome": {
"error": true,
"result": {
"errorLabelsContain": [
"RetryableWriteError"
]
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
]
}
}
}
]
}

View File

@ -331,11 +331,13 @@ class TestSdamMonitoring(IntegrationTest):
# changes because topologyVersion is not incremented.
@client_context.require_version_max(4, 3)
def test_not_master_error_publishes_events(self):
self._test_app_error({'errorCode': 10107, 'closeConnection': False},
self._test_app_error({'errorCode': 10107, 'closeConnection': False,
'errorLabels': ['RetryableWriteError']},
NotMasterError)
def test_shutdown_error_publishes_events(self):
self._test_app_error({'errorCode': 91, 'closeConnection': False},
self._test_app_error({'errorCode': 91, 'closeConnection': False,
'errorLabels': ['RetryableWriteError']},
NotMasterError)

View File

@ -304,6 +304,9 @@
"commitTransaction"
],
"errorCode": 10107,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},

View File

@ -304,9 +304,7 @@
"$set": {
"x": 1
}
},
"multi": false,
"upsert": false
}
},
{
"q": {
@ -317,7 +315,6 @@
"x": 2
}
},
"multi": false,
"upsert": true
}
],
@ -379,9 +376,7 @@
},
"u": {
"y": 1
},
"multi": false,
"upsert": false
}
},
{
"q": {
@ -389,9 +384,7 @@
},
"u": {
"y": 2
},
"multi": false,
"upsert": false
}
}
],
"ordered": true,
@ -454,8 +447,7 @@
"z": 1
}
},
"multi": true,
"upsert": false
"multi": true
}
],
"ordered": true,

View File

@ -40,8 +40,7 @@
"$inc": {
"count": 1
}
},
"upsert": false
}
},
"result": {
"matchedCount": 1,
@ -65,8 +64,7 @@
"$inc": {
"count": 1
}
},
"upsert": false
}
},
"result": {
"matchedCount": 1,
@ -93,9 +91,7 @@
"$inc": {
"count": 1
}
},
"multi": false,
"upsert": false
}
}
],
"ordered": true,
@ -123,9 +119,7 @@
"$inc": {
"count": 1
}
},
"multi": false,
"upsert": false
}
}
],
"ordered": true,
@ -212,8 +206,7 @@
"$inc": {
"count": 1
}
},
"upsert": false
}
},
"result": {
"matchedCount": 1,
@ -260,9 +253,7 @@
"$inc": {
"count": 1
}
},
"multi": false,
"upsert": false
}
}
],
"ordered": true,

View File

@ -134,6 +134,7 @@
"TransientTransactionError"
],
"errorLabelsOmit": [
"RetryableWriteError",
"UnknownTransactionCommitResult"
]
}
@ -223,6 +224,7 @@
"TransientTransactionError"
],
"errorLabelsOmit": [
"RetryableWriteError",
"UnknownTransactionCommitResult"
]
}
@ -312,6 +314,7 @@
"TransientTransactionError"
],
"errorLabelsOmit": [
"RetryableWriteError",
"UnknownTransactionCommitResult"
]
}
@ -408,6 +411,7 @@
"TransientTransactionError"
],
"errorLabelsOmit": [
"RetryableWriteError",
"UnknownTransactionCommitResult"
]
}
@ -461,7 +465,7 @@
}
},
{
"description": "add transient label to connection errors",
"description": "add TransientTransactionError label to connection errors, but do not add RetryableWriteError label",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
@ -496,6 +500,7 @@
"TransientTransactionError"
],
"errorLabelsOmit": [
"RetryableWriteError",
"UnknownTransactionCommitResult"
]
}
@ -511,6 +516,7 @@
"TransientTransactionError"
],
"errorLabelsOmit": [
"RetryableWriteError",
"UnknownTransactionCommitResult"
]
}
@ -533,6 +539,7 @@
"TransientTransactionError"
],
"errorLabelsOmit": [
"RetryableWriteError",
"UnknownTransactionCommitResult"
]
}
@ -549,6 +556,7 @@
"TransientTransactionError"
],
"errorLabelsOmit": [
"RetryableWriteError",
"UnknownTransactionCommitResult"
]
}
@ -663,7 +671,7 @@
}
},
{
"description": "add unknown commit label to connection errors",
"description": "add RetryableWriteError and UnknownTransactionCommitResult labels to connection errors",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
@ -699,6 +707,7 @@
"object": "session0",
"result": {
"errorLabelsContain": [
"RetryableWriteError",
"UnknownTransactionCommitResult"
],
"errorLabelsOmit": [
@ -801,7 +810,7 @@
}
},
{
"description": "add unknown commit label to retryable commit errors",
"description": "add RetryableWriteError and UnknownTransactionCommitResult labels to retryable commit errors",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
@ -811,7 +820,10 @@
"failCommands": [
"commitTransaction"
],
"errorCode": 11602
"errorCode": 11602,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operations": [
@ -837,6 +849,7 @@
"object": "session0",
"result": {
"errorLabelsContain": [
"RetryableWriteError",
"UnknownTransactionCommitResult"
],
"errorLabelsOmit": [
@ -939,7 +952,7 @@
}
},
{
"description": "add unknown commit label to writeConcernError ShutdownInProgress",
"description": "add RetryableWriteError and UnknownTransactionCommitResult labels to writeConcernError ShutdownInProgress",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
@ -951,7 +964,10 @@
],
"writeConcernError": {
"code": 91,
"errmsg": "Replication is being shut down"
"errmsg": "Replication is being shut down",
"errorLabels": [
"RetryableWriteError"
]
}
}
},
@ -985,6 +1001,7 @@
"object": "session0",
"result": {
"errorLabelsContain": [
"RetryableWriteError",
"UnknownTransactionCommitResult"
],
"errorLabelsOmit": [
@ -1089,7 +1106,104 @@
}
},
{
"description": "add unknown commit label to writeConcernError WriteConcernFailed",
"description": "do not add RetryableWriteError label to writeConcernError ShutdownInProgress that occurs within transaction",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"insert"
],
"writeConcernError": {
"code": 91,
"errmsg": "Replication is being shut down"
}
}
},
"operations": [
{
"name": "startTransaction",
"object": "session0",
"arguments": {
"options": {
"writeConcern": {
"w": "majority"
}
}
}
},
{
"name": "insertOne",
"object": "collection",
"arguments": {
"session": "session0",
"document": {
"_id": 1
}
},
"result": {
"errorLabelsContain": [],
"errorLabelsOmit": [
"RetryableWriteError",
"TransientTransactionError",
"UnknownTransactionCommitResult"
]
}
},
{
"name": "abortTransaction",
"object": "session0"
}
],
"expectations": [
{
"command_started_event": {
"command": {
"insert": "test",
"documents": [
{
"_id": 1
}
],
"ordered": true,
"readConcern": null,
"lsid": "session0",
"txnNumber": {
"$numberLong": "1"
},
"startTransaction": true,
"autocommit": false
},
"command_name": "insert",
"database_name": "transaction-tests"
}
},
{
"command_started_event": {
"command": {
"abortTransaction": 1,
"lsid": "session0",
"txnNumber": {
"$numberLong": "1"
},
"startTransaction": null,
"autocommit": false
},
"command_name": "abortTransaction",
"database_name": "admin"
}
}
],
"outcome": {
"collection": {
"data": []
}
}
},
{
"description": "add UnknownTransactionCommitResult label to writeConcernError WriteConcernFailed",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
@ -1138,6 +1252,7 @@
"UnknownTransactionCommitResult"
],
"errorLabelsOmit": [
"RetryableWriteError",
"TransientTransactionError"
]
}
@ -1220,7 +1335,7 @@
}
},
{
"description": "add unknown commit label to writeConcernError WriteConcernFailed with wtimeout",
"description": "add UnknownTransactionCommitResult label to writeConcernError WriteConcernFailed with wtimeout",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
@ -1273,6 +1388,7 @@
"UnknownTransactionCommitResult"
],
"errorLabelsOmit": [
"RetryableWriteError",
"TransientTransactionError"
]
}
@ -1355,7 +1471,7 @@
}
},
{
"description": "omit unknown commit label to writeConcernError UnsatisfiableWriteConcern",
"description": "omit UnknownTransactionCommitResult label from writeConcernError UnsatisfiableWriteConcern",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
@ -1401,6 +1517,7 @@
"object": "session0",
"result": {
"errorLabelsOmit": [
"RetryableWriteError",
"TransientTransactionError",
"UnknownTransactionCommitResult"
]
@ -1461,7 +1578,7 @@
}
},
{
"description": "omit unknown commit label to writeConcernError UnknownReplWriteConcern",
"description": "omit UnknownTransactionCommitResult label from writeConcernError UnknownReplWriteConcern",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
@ -1507,6 +1624,7 @@
"object": "session0",
"result": {
"errorLabelsOmit": [
"RetryableWriteConcern",
"TransientTransactionError",
"UnknownTransactionCommitResult"
]
@ -1567,7 +1685,7 @@
}
},
{
"description": "do not add unknown commit label to MaxTimeMSExpired inside transactions",
"description": "do not add UnknownTransactionCommitResult label to MaxTimeMSExpired inside transactions",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
@ -1614,6 +1732,7 @@
},
"result": {
"errorLabelsOmit": [
"RetryableWriteError",
"UnknownTransactionCommitResult",
"TransientTransactionError"
]
@ -1696,7 +1815,7 @@
}
},
{
"description": "add unknown commit label to MaxTimeMSExpired",
"description": "add UnknownTransactionCommitResult label to MaxTimeMSExpired",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
@ -1743,6 +1862,7 @@
"UnknownTransactionCommitResult"
],
"errorLabelsOmit": [
"RetryableWriteError",
"TransientTransactionError"
]
}
@ -1827,7 +1947,7 @@
}
},
{
"description": "add unknown commit label to writeConcernError MaxTimeMSExpired",
"description": "add UnknownTransactionCommitResult label to writeConcernError MaxTimeMSExpired",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
@ -1877,6 +1997,7 @@
"UnknownTransactionCommitResult"
],
"errorLabelsOmit": [
"RetryableWriteError",
"TransientTransactionError"
]
}

View File

@ -181,7 +181,10 @@
],
"writeConcernError": {
"code": 91,
"errmsg": "Replication is being shut down"
"errmsg": "Replication is being shut down",
"errorLabels": [
"RetryableWriteError"
]
}
}
}

View File

@ -875,7 +875,7 @@
"failCommands": [
"commitTransaction"
],
"errorCode": 50
"errorCode": 51
}
}
}
@ -887,7 +887,7 @@
"errorLabelsOmit": [
"TransientTransactionError"
],
"errorCode": 50
"errorCode": 51
}
},
{

View File

@ -0,0 +1,204 @@
{
"runOn": [
{
"minServerVersion": "4.3.1",
"topology": [
"replicaset",
"sharded"
]
}
],
"database_name": "transaction-tests",
"collection_name": "test",
"data": [],
"tests": [
{
"description": "abortTransaction only retries once with RetryableWriteError from server",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 2
},
"data": {
"failCommands": [
"abortTransaction"
],
"errorCode": 112,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operations": [
{
"name": "startTransaction",
"object": "session0"
},
{
"name": "insertOne",
"object": "collection",
"arguments": {
"session": "session0",
"document": {
"_id": 1
}
},
"result": {
"insertedId": 1
}
},
{
"name": "abortTransaction",
"object": "session0"
}
],
"expectations": [
{
"command_started_event": {
"command": {
"insert": "test",
"documents": [
{
"_id": 1
}
],
"ordered": true,
"readConcern": null,
"lsid": "session0",
"txnNumber": {
"$numberLong": "1"
},
"startTransaction": true,
"autocommit": false,
"writeConcern": null
},
"command_name": "insert",
"database_name": "transaction-tests"
}
},
{
"command_started_event": {
"command": {
"abortTransaction": 1,
"lsid": "session0",
"txnNumber": {
"$numberLong": "1"
},
"startTransaction": null,
"autocommit": false,
"writeConcern": null
},
"command_name": "abortTransaction",
"database_name": "admin"
}
},
{
"command_started_event": {
"command": {
"abortTransaction": 1,
"lsid": "session0",
"txnNumber": {
"$numberLong": "1"
},
"startTransaction": null,
"autocommit": false,
"writeConcern": null
},
"command_name": "abortTransaction",
"database_name": "admin"
}
}
],
"outcome": {
"collection": {
"data": []
}
}
},
{
"description": "abortTransaction does not retry without RetryableWriteError label",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"abortTransaction"
],
"errorCode": 11600,
"errorLabels": []
}
},
"operations": [
{
"name": "startTransaction",
"object": "session0"
},
{
"name": "insertOne",
"object": "collection",
"arguments": {
"session": "session0",
"document": {
"_id": 1
}
},
"result": {
"insertedId": 1
}
},
{
"name": "abortTransaction",
"object": "session0"
}
],
"expectations": [
{
"command_started_event": {
"command": {
"insert": "test",
"documents": [
{
"_id": 1
}
],
"ordered": true,
"readConcern": null,
"lsid": "session0",
"txnNumber": {
"$numberLong": "1"
},
"startTransaction": true,
"autocommit": false,
"writeConcern": null
},
"command_name": "insert",
"database_name": "transaction-tests"
}
},
{
"command_started_event": {
"command": {
"abortTransaction": 1,
"lsid": "session0",
"txnNumber": {
"$numberLong": "1"
},
"startTransaction": null,
"autocommit": false,
"writeConcern": null
},
"command_name": "abortTransaction",
"database_name": "admin"
}
}
],
"outcome": {
"collection": {
"data": []
}
}
}
]
}

View File

@ -413,6 +413,9 @@
"abortTransaction"
],
"errorCode": 10107,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -514,6 +517,9 @@
"abortTransaction"
],
"errorCode": 13436,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -615,6 +621,9 @@
"abortTransaction"
],
"errorCode": 13435,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -716,6 +725,9 @@
"abortTransaction"
],
"errorCode": 11602,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -817,6 +829,9 @@
"abortTransaction"
],
"errorCode": 11600,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -918,6 +933,9 @@
"abortTransaction"
],
"errorCode": 189,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -1019,6 +1037,9 @@
"abortTransaction"
],
"errorCode": 91,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -1120,6 +1141,9 @@
"abortTransaction"
],
"errorCode": 7,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -1221,6 +1245,9 @@
"abortTransaction"
],
"errorCode": 6,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -1322,6 +1349,9 @@
"abortTransaction"
],
"errorCode": 9001,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -1423,6 +1453,9 @@
"abortTransaction"
],
"errorCode": 89,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -1525,6 +1558,9 @@
],
"writeConcernError": {
"code": 11600,
"errorLabels": [
"RetryableWriteError"
],
"errmsg": "Replication is being shut down"
}
}
@ -1639,6 +1675,9 @@
],
"writeConcernError": {
"code": 11602,
"errorLabels": [
"RetryableWriteError"
],
"errmsg": "Replication is being shut down"
}
}
@ -1753,6 +1792,9 @@
],
"writeConcernError": {
"code": 189,
"errorLabels": [
"RetryableWriteError"
],
"errmsg": "Replication is being shut down"
}
}
@ -1867,6 +1909,9 @@
],
"writeConcernError": {
"code": 91,
"errorLabels": [
"RetryableWriteError"
],
"errmsg": "Replication is being shut down"
}
}

View File

@ -0,0 +1,223 @@
{
"runOn": [
{
"minServerVersion": "4.3.1",
"topology": [
"replicaset",
"sharded"
]
}
],
"database_name": "transaction-tests",
"collection_name": "test",
"data": [],
"tests": [
{
"description": "commitTransaction does not retry error without RetryableWriteError label",
"clientOptions": {
"retryWrites": false
},
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"commitTransaction"
],
"errorCode": 11600,
"errorLabels": []
}
},
"operations": [
{
"name": "startTransaction",
"object": "session0"
},
{
"name": "insertOne",
"object": "collection",
"arguments": {
"session": "session0",
"document": {
"_id": 1
}
},
"result": {
"insertedId": 1
}
},
{
"name": "commitTransaction",
"object": "session0",
"result": {
"errorLabelsOmit": [
"RetryableWriteError",
"TransientTransactionError"
]
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"insert": "test",
"documents": [
{
"_id": 1
}
],
"ordered": true,
"readConcern": null,
"lsid": "session0",
"txnNumber": {
"$numberLong": "1"
},
"startTransaction": true,
"autocommit": false,
"writeConcern": null
},
"command_name": "insert",
"database_name": "transaction-tests"
}
},
{
"command_started_event": {
"command": {
"commitTransaction": 1,
"lsid": "session0",
"txnNumber": {
"$numberLong": "1"
},
"startTransaction": null,
"autocommit": false,
"writeConcern": null
},
"command_name": "commitTransaction",
"database_name": "admin"
}
}
],
"outcome": {
"collection": {
"data": []
}
}
},
{
"description": "commitTransaction retries once with RetryableWriteError from server",
"clientOptions": {
"retryWrites": false
},
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"commitTransaction"
],
"errorCode": 112,
"errorLabels": [
"RetryableWriteError"
]
}
},
"operations": [
{
"name": "startTransaction",
"object": "session0"
},
{
"name": "insertOne",
"object": "collection",
"arguments": {
"session": "session0",
"document": {
"_id": 1
}
},
"result": {
"insertedId": 1
}
},
{
"name": "commitTransaction",
"object": "session0"
}
],
"expectations": [
{
"command_started_event": {
"command": {
"insert": "test",
"documents": [
{
"_id": 1
}
],
"ordered": true,
"readConcern": null,
"lsid": "session0",
"txnNumber": {
"$numberLong": "1"
},
"startTransaction": true,
"autocommit": false,
"writeConcern": null
},
"command_name": "insert",
"database_name": "transaction-tests"
}
},
{
"command_started_event": {
"command": {
"commitTransaction": 1,
"lsid": "session0",
"txnNumber": {
"$numberLong": "1"
},
"startTransaction": null,
"autocommit": false,
"writeConcern": null
},
"command_name": "commitTransaction",
"database_name": "admin"
}
},
{
"command_started_event": {
"command": {
"commitTransaction": 1,
"lsid": "session0",
"txnNumber": {
"$numberLong": "1"
},
"startTransaction": null,
"autocommit": false,
"writeConcern": {
"w": "majority",
"wtimeout": 10000
}
},
"command_name": "commitTransaction",
"database_name": "admin"
}
}
],
"outcome": {
"collection": {
"data": [
{
"_id": 1
}
]
}
}
}
]
}

View File

@ -57,6 +57,7 @@
"object": "session0",
"result": {
"errorLabelsContain": [
"RetryableWriteError",
"UnknownTransactionCommitResult"
],
"errorLabelsOmit": [
@ -207,6 +208,7 @@
"object": "session0",
"result": {
"errorLabelsContain": [
"RetryableWriteError",
"UnknownTransactionCommitResult"
],
"errorLabelsOmit": [
@ -353,7 +355,9 @@
"result": {
"errorCodeName": "Interrupted",
"errorLabelsOmit": [
"TransientTransactionError"
"RetryableWriteError",
"TransientTransactionError",
"UnknownTransactionCommitResult"
]
}
}
@ -406,7 +410,7 @@
}
},
{
"description": "commitTransaction fails after WriteConcernError Interrupted",
"description": "commitTransaction is not retried after UnsatisfiableWriteConcern error",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
@ -417,8 +421,8 @@
"commitTransaction"
],
"writeConcernError": {
"code": 11601,
"errmsg": "operation was interrupted"
"code": 100,
"errmsg": "Not enough data-bearing nodes"
}
}
},
@ -452,7 +456,9 @@
"object": "session0",
"result": {
"errorLabelsOmit": [
"TransientTransactionError"
"RetryableWriteError",
"TransientTransactionError",
"UnknownTransactionCommitResult"
]
}
}
@ -629,6 +635,9 @@
"commitTransaction"
],
"errorCode": 10107,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -737,6 +746,9 @@
"commitTransaction"
],
"errorCode": 13436,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -845,6 +857,9 @@
"commitTransaction"
],
"errorCode": 13435,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -953,6 +968,9 @@
"commitTransaction"
],
"errorCode": 11602,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -1061,6 +1079,9 @@
"commitTransaction"
],
"errorCode": 11600,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -1169,6 +1190,9 @@
"commitTransaction"
],
"errorCode": 189,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -1277,6 +1301,9 @@
"commitTransaction"
],
"errorCode": 91,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -1385,6 +1412,9 @@
"commitTransaction"
],
"errorCode": 7,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -1493,6 +1523,9 @@
"commitTransaction"
],
"errorCode": 6,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -1601,6 +1634,9 @@
"commitTransaction"
],
"errorCode": 9001,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -1709,6 +1745,9 @@
"commitTransaction"
],
"errorCode": 89,
"errorLabels": [
"RetryableWriteError"
],
"closeConnection": false
}
},
@ -1818,6 +1857,9 @@
],
"writeConcernError": {
"code": 11600,
"errorLabels": [
"RetryableWriteError"
],
"errmsg": "Replication is being shut down"
}
}
@ -1937,6 +1979,9 @@
],
"writeConcernError": {
"code": 11602,
"errorLabels": [
"RetryableWriteError"
],
"errmsg": "Replication is being shut down"
}
}
@ -2056,6 +2101,9 @@
],
"writeConcernError": {
"code": 189,
"errorLabels": [
"RetryableWriteError"
],
"errmsg": "Replication is being shut down"
}
}
@ -2175,6 +2223,9 @@
],
"writeConcernError": {
"code": 91,
"errorLabels": [
"RetryableWriteError"
],
"errmsg": "Replication is being shut down"
}
}

View File

@ -116,7 +116,6 @@
"x": 1
}
},
"multi": false,
"upsert": true
}
],
@ -145,9 +144,7 @@
},
"u": {
"y": 1
},
"multi": false,
"upsert": false
}
}
],
"ordered": true,
@ -179,8 +176,7 @@
"z": 1
}
},
"multi": true,
"upsert": false
"multi": true
}
],
"ordered": true,
@ -346,7 +342,6 @@
"x": 1
}
},
"multi": false,
"upsert": true
}
],
@ -375,9 +370,7 @@
},
"u": {
"y": 1
},
"multi": false,
"upsert": false
}
}
],
"ordered": true,
@ -409,8 +402,7 @@
"z": 1
}
},
"multi": true,
"upsert": false
"multi": true
}
],
"ordered": true,

View File

@ -877,7 +877,6 @@
"x": 1
}
},
"multi": false,
"upsert": true
}
],