diff --git a/test/spec/retryable-writes/README.rst b/test/spec/retryable-writes/README.rst deleted file mode 100644 index 5ddfb6a2d06..00000000000 --- a/test/spec/retryable-writes/README.rst +++ /dev/null @@ -1,487 +0,0 @@ -===================== -Retryable Write Tests -===================== - -.. contents:: - ----- - -Introduction -============ - -Tests in this directory are platform-independent tests that drivers can use to -prove their conformance to the Retryable Writes specification. - -Tests in the ``unified`` directory are implemented in the -`Unified Test Format <../../unified-test-format/unified-test-format.rst>`__. - -Tests in the ``legacy`` directory should be executed as described below. - -Several prose tests, which are not easily expressed in YAML, are also presented -in this file. Those tests will need to be manually implemented by each driver. - -Tests will require a MongoClient created with options defined in the tests. -Integration tests will require a running MongoDB cluster with server versions -3.6.0 or later. The ``{setFeatureCompatibilityVersion: 3.6}`` admin command -will also need to have been executed to enable support for retryable writes on -the cluster. Some tests may have more stringent version requirements depending -on the fail points used. - -Server Fail Point -================= - -onPrimaryTransactionalWrite ---------------------------- - -Some tests depend on a server fail point, ``onPrimaryTransactionalWrite``, which -allows us to force a network error before the server would return a write result -to the client. The fail point also allows control whether the server will -successfully commit the write via its ``failBeforeCommitExceptionCode`` option. -Keep in mind that the fail point only triggers for transaction writes (i.e. write -commands including ``txnNumber`` and ``lsid`` fields). See `SERVER-29606`_ for -more information. - -.. _SERVER-29606: https://jira.mongodb.org/browse/SERVER-29606 - -The fail point may be configured like so:: - - db.runCommand({ - configureFailPoint: "onPrimaryTransactionalWrite", - mode: , - data: - }); - -``mode`` is a generic fail point option and may be assigned a string or document -value. The string values ``"alwaysOn"`` and ``"off"`` may be used to enable or -disable the fail point, respectively. A document may be used to specify either -``times`` or ``skip``, which are mutually exclusive: - -- ``{ times: }`` may be used to limit the number of times the fail - point may trigger before transitioning to ``"off"``. -- ``{ skip: }`` may be used to defer the first trigger of a fail - point, after which it will transition to ``"alwaysOn"``. - -The ``data`` option is a document that may be used to specify options that -control the fail point's behavior. As noted in `SERVER-29606`_, -``onPrimaryTransactionalWrite`` supports the following ``data`` options, which -may be combined if desired: - -- ``closeConnection``: Boolean option, which defaults to ``true``. If ``true``, - the connection on which the write is executed will be closed before a result - can be returned. -- ``failBeforeCommitExceptionCode``: Integer option, which is unset by default. - If set, the specified exception code will be thrown and the write will not be - committed. If unset, the write will be allowed to commit. - -failCommand ------------ - -Some tests depend on a server fail point, ``failCommand``, which allows the -client to force the server to return an error. Unlike -``onPrimaryTransactionalWrite``, ``failCommand`` does not allow the client to -directly control whether the server will commit the operation (execution of the -write depends on whether the ``closeConnection`` and/or ``errorCode`` options -are specified). See: `failCommand <../../transactions/tests#failcommand>`_ in -the Transactions spec test suite for more information. - -Disabling Fail Points after Test Execution ------------------------------------------- - -After each test that configures a fail point, drivers should disable the fail -point to avoid spurious failures in subsequent tests. The fail point may be -disabled like so:: - - db.runCommand({ - configureFailPoint: , - mode: "off" - }); - -Speeding Up Tests -================= - -See `Speeding Up Tests <../../retryable-reads/tests/README.rst#speeding-up-tests>`_ in the retryable reads spec tests. - -Use as Integration Tests -======================== - -Integration tests are expressed in YAML and can be run against a replica set or -sharded cluster as denoted by the top-level ``runOn`` field. Tests that rely on -the ``onPrimaryTransactionalWrite`` fail point cannot be run against a sharded -cluster because the fail point is not supported by mongos. - -The tests exercise the following scenarios: - -- Single-statement write operations - - - Each test expecting a write result will encounter at-most one network error - for the write command. Retry attempts should return without error and allow - operation to succeed. Observation of the collection state will assert that - the write occurred at-most once. - - - Each test expecting an error will encounter successive network errors for - the write command. Observation of the collection state will assert that the - write was never committed on the server. - -- Multi-statement write operations - - - Each test expecting a write result will encounter at-most one network error - for some write command(s) in the batch. Retry attempts should return without - error and allow the batch to ultimately succeed. Observation of the - collection state will assert that each write occurred at-most once. - - - Each test expecting an error will encounter successive network errors for - some write command in the batch. The batch will ultimately fail with an - error, but observation of the collection state will assert that the failing - write was never committed on the server. We may observe that earlier writes - in the batch occurred at-most once. - -We cannot test a scenario where the first and second attempts both encounter -network errors but the write does actually commit during one of those attempts. -This is because (1) the fail point only triggers when a write would be committed -and (2) the skip and times options are mutually exclusive. That said, such a -test would mainly assert the server's correctness for at-most once semantics and -is not essential to assert driver correctness. - -Test Format ------------ - -Each YAML file has the following keys: - -- ``runOn`` (optional): An array of server version and/or topology requirements - for which the tests can be run. If the test environment satisfies one or more - of these requirements, the tests may be executed; otherwise, this file should - be skipped. If this field is omitted, the tests can be assumed to have no - particular requirements and should be executed. Each element will have some or - all of the following fields: - - - ``minServerVersion`` (optional): The minimum server version (inclusive) - required to successfully run the tests. If this field is omitted, it should - be assumed that there is no lower bound on the required server version. - - - ``maxServerVersion`` (optional): The maximum server version (inclusive) - against which the tests can be run successfully. If this field is omitted, - it should be assumed that there is no upper bound on the required server - version. - - - ``topology`` (optional): An array of server topologies against which the - tests can be run successfully. Valid topologies are "single", - "replicaset", "sharded", and "load-balanced". If this field is omitted, - the default is all topologies (i.e. ``["single", "replicaset", "sharded", - "load-balanced"]``). - - - ``serverless``: (optional): Whether or not the test should be run on Atlas - Serverless instances. Valid values are "require", "forbid", and "allow". If - "require", the test MUST only be run on Atlas Serverless instances. If - "forbid", the test MUST NOT be run on Atlas Serverless instances. If omitted - or "allow", this option has no effect. - - The test runner MUST be informed whether or not Atlas Serverless is being - used in order to determine if this requirement is met (e.g. through an - environment variable or configuration option). - - Note: the Atlas Serverless proxy imitates mongos, so the test runner is not - capable of determining if Atlas Serverless is in use by issuing commands - such as ``buildInfo`` or ``hello``. Furthermore, connections to Atlas - Serverless use a load balancer, so the topology will appear as - "load-balanced". - -- ``data``: The data that should exist in the collection under test before each - test run. - -- ``tests``: An array of tests that are to be run independently of each other. - Each test will have some or all of the following fields: - - - ``description``: The name of the test. - - - ``clientOptions``: Parameters to pass to MongoClient(). - - - ``useMultipleMongoses`` (optional): If ``true``, and the topology type is - ``Sharded``, the MongoClient for this test should be initialized with multiple - mongos seed addresses. If ``false`` or omitted, only a single mongos address - should be specified. - - If ``true``, the topology type is ``LoadBalanced``, and Atlas Serverless is - not being used, the MongoClient for this test should be initialized with the - URI of the load balancer fronting multiple servers. If ``false`` or omitted, - the MongoClient for this test should be initialized with the URI of the load - balancer fronting a single server. - - ``useMultipleMongoses`` only affects ``Sharded`` and ``LoadBalanced`` - topologies (excluding Atlas Serverless). - - - ``failPoint`` (optional): The ``configureFailPoint`` command document to run - to configure a fail point on the primary server. Drivers must ensure that - ``configureFailPoint`` is the first field in the command. This option and - ``useMultipleMongoses: true`` are mutually exclusive. - - - ``operation``: Document describing the operation to be executed. The - operation should be executed through a collection object derived from a - client that has been created with ``clientOptions``. The operation will have - some or all of the following fields: - - - ``name``: The name of the operation as defined in the CRUD specification. - - - ``arguments``: The names and values of arguments from the CRUD - specification. - - - ``outcome``: Document describing the return value and/or expected state of - the collection after the operation is executed. This will have some or all - of the following fields: - - - ``error``: If ``true``, the test should expect an error or exception. Note - that some drivers may report server-side errors as a write error within a - write result object. - - - ``result``: The return value from the operation. This will correspond to - an operation's result object as defined in the CRUD specification. This - field may be omitted if ``error`` is ``true``. If this field is present - and ``error`` is ``true`` (generally for multi-statement tests), the - result reports information about operations that succeeded before an - unrecoverable failure. In that case, drivers may choose to check the - result object if their BulkWriteException (or equivalent) provides access - to a write result object. - - - ``errorLabelsContain``: A list of error label strings that the - error is expected to have. - - - ``errorLabelsOmit``: A list of error label strings that the - error is expected not to have. - - - ``collection``: - - - ``name`` (optional): The name of the collection to verify. If this isn't - present then use the collection under test. - - - ``data``: The data that should exist in the collection after the - operation has been run. - -Split Batch Tests -================= - -The YAML tests specify bulk write operations that are split by command type -(e.g. sequence of insert, update, and delete commands). Multi-statement write -operations may also be split due to ``maxWriteBatchSize``, -``maxBsonObjectSize``, or ``maxMessageSizeBytes``. - -For instance, an insertMany operation with five 10 MiB documents executed using -OP_MSG payload type 0 (i.e. entire command in one document) would be split into -five insert commands in order to respect the 16 MiB ``maxBsonObjectSize`` limit. -The same insertMany operation executed using OP_MSG payload type 1 (i.e. command -arguments pulled out into a separate payload vector) would be split into two -insert commands in order to respect the 48 MB ``maxMessageSizeBytes`` limit. - -Noting when a driver might split operations, the ``onPrimaryTransactionalWrite`` -fail point's ``skip`` option may be used to control when the fail point first -triggers. Once triggered, the fail point will transition to the ``alwaysOn`` -state until disabled. Driver authors should also note that the server attempts -to process all documents in a single insert command within a single commit (i.e. -one insert command with five documents may only trigger the fail point once). -This behavior is unique to insert commands (each statement in an update and -delete command is processed independently). - -If testing an insert that is split into two commands, a ``skip`` of one will -allow the fail point to trigger on the second insert command (because all -documents in the first command will be processed in the same commit). When -testing an update or delete that is split into two commands, the ``skip`` should -be set to the number of statements in the first command to allow the fail point -to trigger on the second command. - -Command Construction Tests -========================== - -Drivers should also assert that command documents are properly constructed with -or without a transaction ID, depending on whether the write operation is -supported. `Command Logging and Monitoring`_ may be used to check for the presence of a -``txnNumber`` field in the command document. Note that command documents may -always include an ``lsid`` field per the `Driver Session`_ specification. - -.. _Command Logging and Monitoring: ../../command-logging-and-monitoring/command-logging-and-monitoring.rst -.. _Driver Session: ../../sessions/driver-sessions.rst - -These tests may be run against both a replica set and shard cluster. - -Drivers should test that transaction IDs are never included in commands for -unsupported write operations: - -* Write commands with unacknowledged write concerns (e.g. ``{w: 0}``) - -* Unsupported single-statement write operations - - - ``updateMany()`` - - ``deleteMany()`` - -* Unsupported multi-statement write operations - - - ``bulkWrite()`` that includes ``UpdateMany`` or ``DeleteMany`` - -* Unsupported write commands - - - ``aggregate`` with write stage (e.g. ``$out``, ``$merge``) - -Drivers should test that transactions IDs are always included in commands for -supported write operations: - -* Supported single-statement write operations - - - ``insertOne()`` - - ``updateOne()`` - - ``replaceOne()`` - - ``deleteOne()`` - - ``findOneAndDelete()`` - - ``findOneAndReplace()`` - - ``findOneAndUpdate()`` - -* Supported multi-statement write operations - - - ``insertMany()`` with ``ordered=true`` - - ``insertMany()`` with ``ordered=false`` - - ``bulkWrite()`` with ``ordered=true`` (no ``UpdateMany`` or ``DeleteMany``) - - ``bulkWrite()`` with ``ordered=false`` (no ``UpdateMany`` or ``DeleteMany``) - -Prose Tests -=========== - -The following tests ensure that retryable writes work properly with replica sets -and sharded clusters. - -#. Test that retryable writes raise an exception when using the MMAPv1 storage - engine. For this test, execute a write operation, such as ``insertOne``, - which should generate an exception. Assert that the error message is the - replacement error message:: - - This MongoDB deployment does not support retryable writes. Please add - retryWrites=false to your connection string. - - and the error code is 20. - - **Note**: Drivers that rely on ``serverStatus`` to determine the storage engine - in use MAY skip this test for sharded clusters, since ``mongos`` does not report - this information in its ``serverStatus`` response. - -#. Test that drivers properly retry after encountering PoolClearedErrors. This - test MUST be implemented by any driver that implements the CMAP - specification. This test requires MongoDB 4.2.9+ for ``blockConnection`` support in the failpoint. - - 1. Create a client with maxPoolSize=1 and retryWrites=true. If testing - against a sharded deployment, be sure to connect to only a single mongos. - - 2. Enable the following failpoint:: - - { - configureFailPoint: "failCommand", - mode: { times: 1 }, - data: { - failCommands: ["insert"], - errorCode: 91, - blockConnection: true, - blockTimeMS: 1000, - errorLabels: ["RetryableWriteError"] - } - } - - 3. Start two threads and attempt to perform an ``insertOne`` simultaneously on both. - - 4. Verify that both ``insertOne`` attempts succeed. - - 5. Via CMAP monitoring, assert that the first check out succeeds. - - 6. Via CMAP monitoring, assert that a PoolClearedEvent is then emitted. - - 7. Via CMAP monitoring, assert that the second check out then fails due to a - connection error. - - 8. Via Command Monitoring, assert that exactly three ``insert`` - CommandStartedEvents were observed in total. - - 9. Disable the failpoint. - -#. Test that drivers return the original error after encountering a - WriteConcernError with a RetryableWriteError label. This test MUST - - 1. be implemented by any driver that implements the Command Monitoring - specification, - - 2. only run against replica sets as mongos does not propagate the - NoWritesPerformed label to the drivers. - - 3. be run against server versions 6.0 and above. - - Additionally, this test requires drivers to set a fail point after an - ``insertOne`` operation but before the subsequent retry. Drivers that are - unable to set a failCommand after the CommandSucceededEvent SHOULD use - mocking or write a unit test to cover the same sequence of events. - - - 1. Create a client with ``retryWrites=true``. - - 2. Configure a fail point with error code ``91`` (ShutdownInProgress):: - - db.adminCommand({ - configureFailPoint: "failCommand", - mode: {times: 1}, - data: { - writeConcernError: { - code: 91, - errorLabels: ["RetryableWriteError"], - }, - failCommands: ["insert"], - }, - }); - - 3. Via the command monitoring CommandSucceededEvent, configure a fail point - with error code ``10107`` (NotWritablePrimary) and a NoWritesPerformed - label:: - - db.adminCommand({ - configureFailPoint: "failCommand", - mode: {times: 1}, - data: { - errorCode: 10107, - errorLabels: ["RetryableWriteError", "NoWritesPerformed"], - failCommands: ["insert"], - }, - }); - - Drivers SHOULD only configure the ``10107`` fail point command if the the - succeeded event is for the ``91`` error configured in step 2. - - 4. Attempt an ``insertOne`` operation on any record for any database and - collection. For the resulting error, assert that the associated error code - is ``91``. - - 5. Disable the fail point:: - - db.adminCommand({ - configureFailPoint: "failCommand", - mode: "off", - }) - -Changelog -========= - -:2022-08-30: Add prose test verifying correct error handling for errors with - the NoWritesPerformed label, which is to return the original - error. - -:2022-04-22: Clarifications to ``serverless`` and ``useMultipleMongoses``. - -:2021-08-27: Add ``serverless`` to ``runOn``. Clarify behavior of - ``useMultipleMongoses`` for ``LoadBalanced`` topologies. - -:2021-04-23: Add ``load-balanced`` to test topology requirements. - -:2021-03-24: Add prose test verifying ``PoolClearedErrors`` are retried. - -:2019-10-21: Add ``errorLabelsContain`` and ``errorLabelsContain`` fields to - ``result`` - -:2019-08-07: Add Prose Tests section - -:2019-06-07: Mention $merge stage for aggregate alongside $out - -:2019-03-01: Add top-level ``runOn`` field to denote server version and/or - topology requirements requirements for the test file. Removes the - ``minServerVersion`` and ``maxServerVersion`` top-level fields, - which are now expressed within ``runOn`` elements. - - Add test-level ``useMultipleMongoses`` field. diff --git a/test/spec/retryable-writes/legacy/bulkWrite-errorLabels.json b/test/spec/retryable-writes/legacy/bulkWrite-errorLabels.json deleted file mode 100644 index 66c3ecb336a..00000000000 --- a/test/spec/retryable-writes/legacy/bulkWrite-errorLabels.json +++ /dev/null @@ -1,183 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/bulkWrite-errorLabels.yml b/test/spec/retryable-writes/legacy/bulkWrite-errorLabels.yml deleted file mode 100644 index fb9d7e47e06..00000000000 --- a/test/spec/retryable-writes/legacy/bulkWrite-errorLabels.yml +++ /dev/null @@ -1,77 +0,0 @@ -runOn: - - minServerVersion: "4.3.1" - topology: ["replicaset", "sharded", "load-balanced"] - -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 # WriteConflict, not a retryable error code - errorLabels: ["RetryableWriteError"] # Override server behavior: send RetryableWriteError label with non-retryable error code - 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: # Driver retries operation and it succeeds - 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 # InterruptedAtShutdown, normally a retryable error code - errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code - 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 # Driver does not retry operation because there was no RetryableWriteError label on response - result: - errorLabelsOmit: ["RetryableWriteError"] - collection: - data: - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } diff --git a/test/spec/retryable-writes/legacy/bulkWrite-serverErrors.json b/test/spec/retryable-writes/legacy/bulkWrite-serverErrors.json deleted file mode 100644 index 1e6cc74c052..00000000000 --- a/test/spec/retryable-writes/legacy/bulkWrite-serverErrors.json +++ /dev/null @@ -1,273 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "BulkWrite succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 189, - "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 succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "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 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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/bulkWrite-serverErrors.yml b/test/spec/retryable-writes/legacy/bulkWrite-serverErrors.yml deleted file mode 100644 index 6ca6540f61e..00000000000 --- a/test/spec/retryable-writes/legacy/bulkWrite-serverErrors.yml +++ /dev/null @@ -1,130 +0,0 @@ -runOn: - - - minServerVersion: "4.0" - topology: ["replicaset"] - - - minServerVersion: "4.1.7" - topology: ["sharded", "load-balanced"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "BulkWrite succeeds after PrimarySteppedDown" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["update"] - errorCode: 189 - 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 succeeds after WriteConcernError ShutdownInProgress" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorLabels: ["RetryableWriteError"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 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 } diff --git a/test/spec/retryable-writes/legacy/bulkWrite.json b/test/spec/retryable-writes/legacy/bulkWrite.json deleted file mode 100644 index 72a8d018939..00000000000 --- a/test/spec/retryable-writes/legacy/bulkWrite.json +++ /dev/null @@ -1,806 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "First command is retried", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 1, - "insertedIds": { - "0": 2 - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 23 - } - ] - } - } - }, - { - "description": "All commands are retried", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 7 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 4, - "x": 44 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 5, - "x": 55 - } - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 3 - }, - "replacement": { - "_id": 3, - "x": 333 - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 3, - "insertedIds": { - "0": 2, - "2": 3, - "4": 5 - }, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 1, - "upsertedIds": { - "3": 4 - } - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 333 - }, - { - "_id": 4, - "x": 45 - }, - { - "_id": 5, - "x": 55 - } - ] - } - } - }, - { - "description": "Both commands are retried after their first statement fails", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "0": 2 - }, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 23 - } - ] - } - } - }, - { - "description": "Second command is retried after its second statement fails", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "skip": 2 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "0": 2 - }, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 23 - } - ] - } - } - }, - { - "description": "BulkWrite with unordered execution", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - } - ], - "options": { - "ordered": false - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 2, - "insertedIds": { - "0": 2, - "1": 3 - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "First insertOne is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "result": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - }, - { - "description": "Second updateOne is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "skip": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "0": 2 - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Third updateOne is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "skip": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "1": 2 - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Single-document write following deleteMany is retried", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteMany", - "arguments": { - "filter": { - "x": 11 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 1, - "insertedIds": { - "1": 2 - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Single-document write following updateMany is retried", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateMany", - "arguments": { - "filter": { - "x": 11 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "1": 2 - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/bulkWrite.yml b/test/spec/retryable-writes/legacy/bulkWrite.yml deleted file mode 100644 index 939dacf7728..00000000000 --- a/test/spec/retryable-writes/legacy/bulkWrite.yml +++ /dev/null @@ -1,396 +0,0 @@ -runOn: - - - minServerVersion: "3.6" - topology: ["replicaset"] - -data: - - { _id: 1, x: 11 } - -tests: - - - description: "First command is retried" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - - - name: "deleteOne" - arguments: - filter: { _id: 1 } - options: { ordered: true } - outcome: - result: - deletedCount: 1 - insertedCount: 1 - insertedIds: { 0: 2 } - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 2, x: 23 } - - - # Write operations in this ordered batch are intentionally sequenced so - # that each write command consists of a single statement, which will - # fail on the first attempt and succeed on the second, retry attempt. - description: "All commands are retried" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 7 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - - - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - - - name: "updateOne" - arguments: - filter: { _id: 4, x: 44 } - update: { $inc: { x : 1 }} - upsert: true - - - name: "insertOne" - arguments: - document: { _id: 5, x: 55 } - - - name: "replaceOne" - arguments: - filter: { _id: 3 } - replacement: { _id: 3, x: 333 } - - - name: "deleteOne" - arguments: - filter: { _id: 1 } - options: { ordered: true } - outcome: - result: - deletedCount: 1 - insertedCount: 3 - insertedIds: { 0: 2, 2: 3, 4: 5 } - matchedCount: 2 - modifiedCount: 2 - upsertedCount: 1 - upsertedIds: { 3: 4 } - collection: - data: - - { _id: 2, x: 23 } - - { _id: 3, x: 333 } - - { _id: 4, x: 45 } - - { _id: 5, x: 55 } - - - description: "Both commands are retried after their first statement fails" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "updateOne" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - options: { ordered: true } - outcome: - result: - deletedCount: 0 - insertedCount: 1 - insertedIds: { 0: 2 } - matchedCount: 2 - modifiedCount: 2 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 23 } - - - description: "Second command is retried after its second statement fails" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { skip: 2 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "updateOne" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - options: { ordered: true } - outcome: - result: - deletedCount: 0 - insertedCount: 1 - insertedIds: { 0: 2 } - matchedCount: 2 - modifiedCount: 2 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 23 } - - - description: "BulkWrite with unordered execution" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - options: { ordered: false } - outcome: - result: - deletedCount: 0 - insertedCount: 2 - insertedIds: { 0: 2, 1: 3 } - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "First insertOne is never committed" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - - - name: "deleteOne" - arguments: - filter: { _id: 1 } - options: { ordered: true } - outcome: - error: true - result: - deletedCount: 0 - insertedCount: 0 - insertedIds: { } - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 1, x: 11 } - - - description: "Second updateOne is never committed" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { skip: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - - - name: "deleteOne" - arguments: - filter: { _id: 1 } - options: { ordered: true } - outcome: - error: true - result: - deletedCount: 0 - insertedCount: 1 - insertedIds: { 0: 2 } - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - - description: "Third updateOne is never committed" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { skip: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "updateOne" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - options: { ordered: true } - outcome: - error: true - result: - deletedCount: 0 - insertedCount: 1 - insertedIds: { 1: 2 } - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 22 } - - - # The onPrimaryTransactionalWrite fail point only triggers for write - # operations that include a transaction ID. Therefore, it will not - # affect the initial deleteMany and will trigger once (and only once) - # for the first insertOne attempt. - description: "Single-document write following deleteMany is retried" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "deleteMany" - arguments: - filter: { x: 11 } - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - options: { ordered: true } - outcome: - result: - deletedCount: 1 - insertedCount: 1 - insertedIds: { 1: 2 } - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 2, x: 22 } - - - # The onPrimaryTransactionalWrite fail point only triggers for write - # operations that include a transaction ID. Therefore, it will not - # affect the initial updateMany and will trigger once (and only once) - # for the first insertOne attempt. - description: "Single-document write following updateMany is retried" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "updateMany" - arguments: - filter: { x: 11 } - update: { $inc: { x : 1 }} - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - options: { ordered: true } - outcome: - result: - deletedCount: 0 - insertedCount: 1 - insertedIds: { 1: 2 } - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/legacy/deleteMany.json b/test/spec/retryable-writes/legacy/deleteMany.json deleted file mode 100644 index faa21c44f1e..00000000000 --- a/test/spec/retryable-writes/legacy/deleteMany.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "DeleteMany ignores retryWrites", - "useMultipleMongoses": true, - "operation": { - "name": "deleteMany", - "arguments": { - "filter": {} - } - }, - "outcome": { - "result": { - "deletedCount": 2 - }, - "collection": { - "data": [] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/deleteMany.yml b/test/spec/retryable-writes/legacy/deleteMany.yml deleted file mode 100644 index 4743953fffe..00000000000 --- a/test/spec/retryable-writes/legacy/deleteMany.yml +++ /dev/null @@ -1,22 +0,0 @@ -runOn: - - - minServerVersion: "3.6" - topology: ["replicaset", "sharded", "load-balanced"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "DeleteMany ignores retryWrites" - useMultipleMongoses: true - operation: - name: "deleteMany" - arguments: - filter: { } - outcome: - result: - deletedCount: 2 - collection: - data: [] diff --git a/test/spec/retryable-writes/legacy/deleteOne-errorLabels.json b/test/spec/retryable-writes/legacy/deleteOne-errorLabels.json deleted file mode 100644 index c14692fd1a8..00000000000 --- a/test/spec/retryable-writes/legacy/deleteOne-errorLabels.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/deleteOne-errorLabels.yml b/test/spec/retryable-writes/legacy/deleteOne-errorLabels.yml deleted file mode 100644 index 9ee5c7426e4..00000000000 --- a/test/spec/retryable-writes/legacy/deleteOne-errorLabels.yml +++ /dev/null @@ -1,48 +0,0 @@ -runOn: - - minServerVersion: "4.3.1" - topology: ["replicaset", "sharded", "load-balanced"] - -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 # WriteConflict, not a retryable error code - errorLabels: ["RetryableWriteError"] # Override server behavior: send RetryableWriteError label with non-retryable error code - operation: - name: "deleteOne" - arguments: - filter: { _id: 1 } - outcome: # Driver retries operation and it succeeds - 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 # InterruptedAtShutdown, normally a retryable error code - errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code - operation: - name: "deleteOne" - arguments: - filter: { _id: 1 } - outcome: - error: true # Driver does not retry operation because there was no RetryableWriteError label on response - result: - errorLabelsOmit: ["RetryableWriteError"] - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/legacy/deleteOne-serverErrors.json b/test/spec/retryable-writes/legacy/deleteOne-serverErrors.json deleted file mode 100644 index a1a27838de3..00000000000 --- a/test/spec/retryable-writes/legacy/deleteOne-serverErrors.json +++ /dev/null @@ -1,153 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "DeleteOne succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "errorCode": 189, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "DeleteOne succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/deleteOne-serverErrors.yml b/test/spec/retryable-writes/legacy/deleteOne-serverErrors.yml deleted file mode 100644 index f4c98c919b5..00000000000 --- a/test/spec/retryable-writes/legacy/deleteOne-serverErrors.yml +++ /dev/null @@ -1,73 +0,0 @@ -runOn: - - - minServerVersion: "4.0" - topology: ["replicaset"] - - - minServerVersion: "4.1.7" - topology: ["sharded", "load-balanced"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "DeleteOne succeeds after PrimarySteppedDown" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["delete"] - errorCode: 189 - errorLabels: ["RetryableWriteError"] - operation: - name: "deleteOne" - arguments: - filter: { _id: 1 } - outcome: - result: - deletedCount: 1 - collection: - data: - - { _id: 2, x: 22 } - - - description: "DeleteOne succeeds after WriteConcernError ShutdownInProgress" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["delete"] - errorLabels: ["RetryableWriteError"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - operation: - name: "deleteOne" - arguments: - filter: { _id: 1 } - outcome: - result: - deletedCount: 1 - collection: - data: - - { _id: 2, x: 22 } - - - 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 } diff --git a/test/spec/retryable-writes/legacy/deleteOne.json b/test/spec/retryable-writes/legacy/deleteOne.json deleted file mode 100644 index 592937acedd..00000000000 --- a/test/spec/retryable-writes/legacy/deleteOne.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "DeleteOne is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "DeleteOne is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "DeleteOne is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/deleteOne.yml b/test/spec/retryable-writes/legacy/deleteOne.yml deleted file mode 100644 index b15c991cda7..00000000000 --- a/test/spec/retryable-writes/legacy/deleteOne.yml +++ /dev/null @@ -1,57 +0,0 @@ -runOn: - - - minServerVersion: "3.6" - topology: ["replicaset"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "DeleteOne is committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "deleteOne" - arguments: - filter: { _id: 1 } - outcome: - result: - deletedCount: 1 - collection: - data: - - { _id: 2, x: 22 } - - - description: "DeleteOne is not committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "deleteOne" - arguments: - filter: { _id: 1 } - outcome: - result: - deletedCount: 1 - collection: - data: - - { _id: 2, x: 22 } - - - description: "DeleteOne is never committed" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "deleteOne" - arguments: - filter: { _id: 1 } - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/legacy/findOneAndDelete-errorLabels.json b/test/spec/retryable-writes/legacy/findOneAndDelete-errorLabels.json deleted file mode 100644 index 60e6e0a7bc1..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndDelete-errorLabels.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/findOneAndDelete-errorLabels.yml b/test/spec/retryable-writes/legacy/findOneAndDelete-errorLabels.yml deleted file mode 100644 index 5192c5adfef..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndDelete-errorLabels.yml +++ /dev/null @@ -1,49 +0,0 @@ -runOn: - - minServerVersion: "4.3.1" - topology: ["replicaset", "sharded", "load-balanced"] - -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 # WriteConflict, not a retryable error code - errorLabels: ["RetryableWriteError"] # Override server behavior: send RetryableWriteError label with non-retryable error code - operation: - name: "findOneAndDelete" - arguments: - filter: { x: { $gte: 11 } } - sort: { x: 1 } - outcome: # Driver retries operation and it succeeds - 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 # InterruptedAtShutdown, normally a retryable error code - errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code - operation: - name: "findOneAndDelete" - arguments: - filter: { x: { $gte: 11 } } - sort: { x: 1 } - outcome: - error: true # Driver does not retry operation because there was no RetryableWriteError label on response - result: - errorLabelsOmit: ["RetryableWriteError"] - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/legacy/findOneAndDelete-serverErrors.json b/test/spec/retryable-writes/legacy/findOneAndDelete-serverErrors.json deleted file mode 100644 index c18b63f4567..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndDelete-serverErrors.json +++ /dev/null @@ -1,170 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndDelete succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 189, - "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 succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "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 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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/findOneAndDelete-serverErrors.yml b/test/spec/retryable-writes/legacy/findOneAndDelete-serverErrors.yml deleted file mode 100644 index 688ee3342e1..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndDelete-serverErrors.yml +++ /dev/null @@ -1,74 +0,0 @@ -runOn: - - - minServerVersion: "4.0" - topology: ["replicaset"] - - - minServerVersion: "4.1.7" - topology: ["sharded", "load-balanced"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "FindOneAndDelete succeeds after PrimarySteppedDown" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["findAndModify"] - errorCode: 189 - 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 succeeds after WriteConcernError ShutdownInProgress" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["findAndModify"] - errorLabels: ["RetryableWriteError"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 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 } diff --git a/test/spec/retryable-writes/legacy/findOneAndDelete.json b/test/spec/retryable-writes/legacy/findOneAndDelete.json deleted file mode 100644 index 0cbe18108bd..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndDelete.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndDelete is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "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 is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "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 is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "x": { - "$gte": 11 - } - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/findOneAndDelete.yml b/test/spec/retryable-writes/legacy/findOneAndDelete.yml deleted file mode 100644 index 1456ad71626..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndDelete.yml +++ /dev/null @@ -1,58 +0,0 @@ -runOn: - - - minServerVersion: "3.6" - topology: ["replicaset"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "FindOneAndDelete is committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - 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 is not committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - 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 is never committed" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "findOneAndDelete" - arguments: - filter: { x: { $gte: 11 }} - sort: { x: 1 } - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/legacy/findOneAndReplace-errorLabels.json b/test/spec/retryable-writes/legacy/findOneAndReplace-errorLabels.json deleted file mode 100644 index afa2f47af44..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndReplace-errorLabels.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/findOneAndReplace-errorLabels.yml b/test/spec/retryable-writes/legacy/findOneAndReplace-errorLabels.yml deleted file mode 100644 index 184366163f9..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndReplace-errorLabels.yml +++ /dev/null @@ -1,52 +0,0 @@ -runOn: - - minServerVersion: "4.3.1" - topology: ["replicaset", "sharded", "load-balanced"] - -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 # WriteConflict, not a retryable error code - errorLabels: ["RetryableWriteError"] # Override server behavior: send RetryableWriteError label with non-retryable error code - operation: - name: "findOneAndReplace" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - returnDocument: "Before" - outcome: # Driver retries operation and it succeeds - 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 # InterruptedAtShutdown, normally a retryable error code - errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code - operation: - name: "findOneAndReplace" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - returnDocument: "Before" - outcome: - error: true # Driver does not retry operation because there was no RetryableWriteError label on response - result: - errorLabelsOmit: ["RetryableWriteError"] - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/legacy/findOneAndReplace-serverErrors.json b/test/spec/retryable-writes/legacy/findOneAndReplace-serverErrors.json deleted file mode 100644 index 944a3af8483..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndReplace-serverErrors.json +++ /dev/null @@ -1,178 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndReplace succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 189, - "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 succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "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 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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/findOneAndReplace-serverErrors.yml b/test/spec/retryable-writes/legacy/findOneAndReplace-serverErrors.yml deleted file mode 100644 index 6f0f31874a4..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndReplace-serverErrors.yml +++ /dev/null @@ -1,80 +0,0 @@ -runOn: - - - minServerVersion: "4.0" - topology: ["replicaset"] - - - minServerVersion: "4.1.7" - topology: ["sharded", "load-balanced"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "FindOneAndReplace succeeds after PrimarySteppedDown" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["findAndModify"] - errorCode: 189 - 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 succeeds after WriteConcernError ShutdownInProgress" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["findAndModify"] - errorLabels: ["RetryableWriteError"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 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 } diff --git a/test/spec/retryable-writes/legacy/findOneAndReplace.json b/test/spec/retryable-writes/legacy/findOneAndReplace.json deleted file mode 100644 index e1f9ab7f8c3..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndReplace.json +++ /dev/null @@ -1,145 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndReplace is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "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 is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "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 is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "Before" - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/findOneAndReplace.yml b/test/spec/retryable-writes/legacy/findOneAndReplace.yml deleted file mode 100644 index 36d81d461ed..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndReplace.yml +++ /dev/null @@ -1,63 +0,0 @@ -runOn: - - - minServerVersion: "3.6" - topology: ["replicaset"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "FindOneAndReplace is committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - 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 is not committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - 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 is never committed" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "findOneAndReplace" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - returnDocument: "Before" - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/legacy/findOneAndUpdate-errorLabels.json b/test/spec/retryable-writes/legacy/findOneAndUpdate-errorLabels.json deleted file mode 100644 index 19b3a9e771c..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndUpdate-errorLabels.json +++ /dev/null @@ -1,124 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/findOneAndUpdate-errorLabels.yml b/test/spec/retryable-writes/legacy/findOneAndUpdate-errorLabels.yml deleted file mode 100644 index 03751d568ba..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndUpdate-errorLabels.yml +++ /dev/null @@ -1,52 +0,0 @@ -runOn: - - minServerVersion: "4.3.1" - topology: ["replicaset", "sharded", "load-balanced"] - -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 # WriteConflict, not a retryable error code - errorLabels: ["RetryableWriteError"] # Override server behavior: send RetryableWriteError label with non-retryable error code - operation: - name: "findOneAndUpdate" - arguments: - filter: { _id: 1 } - update: { $inc: { x: 1 } } - returnDocument: "Before" - outcome: # Driver retries operation and it succeeds - 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 # InterruptedAtShutdown, normally a retryable error code - errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code - operation: - name: "findOneAndUpdate" - arguments: - filter: { _id: 1 } - update: { $inc: { x: 1 } } - returnDocument: "Before" - outcome: - error: true # Driver does not retry operation because there was no RetryableWriteError label on response - result: - errorLabelsOmit: ["RetryableWriteError"] - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/legacy/findOneAndUpdate-serverErrors.json b/test/spec/retryable-writes/legacy/findOneAndUpdate-serverErrors.json deleted file mode 100644 index e83a610615c..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndUpdate-serverErrors.json +++ /dev/null @@ -1,181 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndUpdate succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 189, - "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 succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "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 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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/findOneAndUpdate-serverErrors.yml b/test/spec/retryable-writes/legacy/findOneAndUpdate-serverErrors.yml deleted file mode 100644 index c1e0b6a7ca1..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndUpdate-serverErrors.yml +++ /dev/null @@ -1,79 +0,0 @@ -runOn: - - - minServerVersion: "4.0" - topology: ["replicaset"] - - - minServerVersion: "4.1.7" - topology: ["sharded", "load-balanced"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "FindOneAndUpdate succeeds after PrimarySteppedDown" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["findAndModify"] - errorCode: 189 - 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 succeeds after WriteConcernError ShutdownInProgress" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["findAndModify"] - errorLabels: ["RetryableWriteError"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 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 } diff --git a/test/spec/retryable-writes/legacy/findOneAndUpdate.json b/test/spec/retryable-writes/legacy/findOneAndUpdate.json deleted file mode 100644 index 9ae2d87d821..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndUpdate.json +++ /dev/null @@ -1,147 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndUpdate is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "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 is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "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 is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/findOneAndUpdate.yml b/test/spec/retryable-writes/legacy/findOneAndUpdate.yml deleted file mode 100644 index 9235526be75..00000000000 --- a/test/spec/retryable-writes/legacy/findOneAndUpdate.yml +++ /dev/null @@ -1,62 +0,0 @@ -runOn: - - - minServerVersion: "3.6" - topology: ["replicaset"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "FindOneAndUpdate is committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - 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 is not committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - 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 is never committed" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "findOneAndUpdate" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/legacy/insertMany-errorLabels.json b/test/spec/retryable-writes/legacy/insertMany-errorLabels.json deleted file mode 100644 index 65fd377fa60..00000000000 --- a/test/spec/retryable-writes/legacy/insertMany-errorLabels.json +++ /dev/null @@ -1,130 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/insertMany-errorLabels.yml b/test/spec/retryable-writes/legacy/insertMany-errorLabels.yml deleted file mode 100644 index 9f5e1636235..00000000000 --- a/test/spec/retryable-writes/legacy/insertMany-errorLabels.yml +++ /dev/null @@ -1,54 +0,0 @@ -runOn: - - minServerVersion: "4.3.1" - topology: ["replicaset", "sharded", "load-balanced"] - -data: - - { _id: 1, x: 11 } - -tests: - - description: "InsertMany succeeds with RetryableWriteError from server" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 112 # WriteConflict, not a retryable error code - errorLabels: ["RetryableWriteError"] # Override server behavior: send RetryableWriteError label with non-retryable error code - operation: - name: "insertMany" - arguments: - documents: - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - options: { ordered: true } - outcome: # Driver retries operation and it succeeds - 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 # InterruptedAtShutdown, normally a retryable error code - errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code - operation: - name: "insertMany" - arguments: - documents: - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - options: { ordered: true } - outcome: - error: true # Driver does not retry operation because there was no RetryableWriteError label on response - result: - errorLabelsOmit: ["RetryableWriteError"] - collection: - data: - - { _id: 1, x: 11 } diff --git a/test/spec/retryable-writes/legacy/insertMany-serverErrors.json b/test/spec/retryable-writes/legacy/insertMany-serverErrors.json deleted file mode 100644 index fe8dbf4a622..00000000000 --- a/test/spec/retryable-writes/legacy/insertMany-serverErrors.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "InsertMany succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 189, - "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 succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "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 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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/insertMany-serverErrors.yml b/test/spec/retryable-writes/legacy/insertMany-serverErrors.yml deleted file mode 100644 index 0dc7518c6ba..00000000000 --- a/test/spec/retryable-writes/legacy/insertMany-serverErrors.yml +++ /dev/null @@ -1,84 +0,0 @@ -runOn: - - - minServerVersion: "4.0" - topology: ["replicaset"] - - - minServerVersion: "4.1.7" - topology: ["sharded", "load-balanced"] - -data: - - { _id: 1, x: 11 } - -tests: - - - description: "InsertMany succeeds after PrimarySteppedDown" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 189 - 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 succeeds after WriteConcernError ShutdownInProgress" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorLabels: ["RetryableWriteError"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 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 } diff --git a/test/spec/retryable-writes/legacy/insertMany.json b/test/spec/retryable-writes/legacy/insertMany.json deleted file mode 100644 index 0ad326e2dc9..00000000000 --- a/test/spec/retryable-writes/legacy/insertMany.json +++ /dev/null @@ -1,163 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "InsertMany succeeds after one network error", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "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 with unordered execution", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": false - } - } - }, - "outcome": { - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertMany fails after multiple network errors", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": "alwaysOn", - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/insertMany.yml b/test/spec/retryable-writes/legacy/insertMany.yml deleted file mode 100644 index eed450e0a3e..00000000000 --- a/test/spec/retryable-writes/legacy/insertMany.yml +++ /dev/null @@ -1,74 +0,0 @@ -runOn: - - - minServerVersion: "3.6" - topology: ["replicaset"] - -data: - - { _id: 1, x: 11 } - -tests: - - - description: "InsertMany succeeds after one network error" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - 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 with unordered execution" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "insertMany" - arguments: - documents: - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - options: { ordered: false } - outcome: - result: - insertedIds: { 0: 2, 1: 3 } - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertMany fails after multiple network errors" - failPoint: - # Normally, a mongod will insert the documents as a batch with a - # single commit. If this fails, mongod may try to insert each - # document one at a time depending on the failure. Therefore our - # single insert command may trigger the failpoint twice on each - # driver attempt. This test permanently enables the fail point to - # ensure the retry attempt always fails. - configureFailPoint: onPrimaryTransactionalWrite - mode: "alwaysOn" - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "insertMany" - arguments: - documents: - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - { _id: 4, x: 44 } - options: { ordered: true } - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } diff --git a/test/spec/retryable-writes/legacy/insertOne-errorLabels.json b/test/spec/retryable-writes/legacy/insertOne-errorLabels.json deleted file mode 100644 index d90ac5dfbd8..00000000000 --- a/test/spec/retryable-writes/legacy/insertOne-errorLabels.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "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": [] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/insertOne-errorLabels.yml b/test/spec/retryable-writes/legacy/insertOne-errorLabels.yml deleted file mode 100644 index 87100aa5cf3..00000000000 --- a/test/spec/retryable-writes/legacy/insertOne-errorLabels.yml +++ /dev/null @@ -1,44 +0,0 @@ -runOn: - - minServerVersion: "4.3.1" - topology: ["replicaset", "sharded", "load-balanced"] - -data: [] - -tests: - - description: "InsertOne succeeds with RetryableWriteError from server" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 112 # WriteConflict, not a retryable error code - errorLabels: ["RetryableWriteError"] # Override server behavior: send RetryableWriteError label with non-retryable error code - operation: - name: "insertOne" - arguments: - document: { _id: 1, x: 11 } - outcome: # Driver retries operation and it succeeds - 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 # InterruptedAtShutdown, normally a retryable error code - errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code - operation: - name: "insertOne" - arguments: - document: { _id: 1, x: 11 } - outcome: - error: true # Driver does not retry operation because there was no RetryableWriteError label on response - result: - errorLabelsOmit: ["RetryableWriteError"] - collection: - data: [] diff --git a/test/spec/retryable-writes/legacy/insertOne-serverErrors.json b/test/spec/retryable-writes/legacy/insertOne-serverErrors.json deleted file mode 100644 index 5179a6ab753..00000000000 --- a/test/spec/retryable-writes/legacy/insertOne-serverErrors.json +++ /dev/null @@ -1,1162 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "InsertOne succeeds after connection failure", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "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 NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 10107, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 13436, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 13435, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11602, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11600, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 189, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 91, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 7, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 6, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 9001, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 89, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after ExceededTimeLimit", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 262, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne fails after Interrupted", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11601, - "closeConnection": false - } - }, - "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 WriteConcernError InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 11600, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after WriteConcernError InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 11602, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after WriteConcernError PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 189, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne fails after multiple retryable writeConcernErrors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "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 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne fails after WriteConcernError Interrupted", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 11601, - "errmsg": "operation was interrupted" - } - } - }, - "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 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne fails after WriteConcernError WriteConcernFailed", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 64, - "codeName": "WriteConcernFailed", - "errmsg": "waiting for replication timed out", - "errInfo": { - "wtimeout": 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 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/insertOne-serverErrors.yml b/test/spec/retryable-writes/legacy/insertOne-serverErrors.yml deleted file mode 100644 index bfbd1877427..00000000000 --- a/test/spec/retryable-writes/legacy/insertOne-serverErrors.yml +++ /dev/null @@ -1,527 +0,0 @@ -runOn: - - - minServerVersion: "4.0" - topology: ["replicaset"] - - - minServerVersion: "4.1.7" - topology: ["sharded", "load-balanced"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "InsertOne succeeds after connection failure" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - closeConnection: true - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - 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: - # If retryWrites is false, the driver should not add the - # RetryableWriteError label to the error. - errorLabelsOmit: ["RetryableWriteError"] - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - - description: "InsertOne succeeds after NotWritablePrimary" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 10107 - errorLabels: ["RetryableWriteError"] - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after NotPrimaryOrSecondary" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 13436 - errorLabels: ["RetryableWriteError"] - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after NotPrimaryNoSecondaryOk" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 13435 - errorLabels: ["RetryableWriteError"] - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after InterruptedDueToReplStateChange" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 11602 - errorLabels: ["RetryableWriteError"] - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after InterruptedAtShutdown" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 11600 - errorLabels: ["RetryableWriteError"] - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after PrimarySteppedDown" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 189 - errorLabels: ["RetryableWriteError"] - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after ShutdownInProgress" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 91 - errorLabels: ["RetryableWriteError"] - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after HostNotFound" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 7 - errorLabels: ["RetryableWriteError"] - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after HostUnreachable" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 6 - errorLabels: ["RetryableWriteError"] - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after SocketException" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 9001 - errorLabels: ["RetryableWriteError"] - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after NetworkTimeout" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 89 - errorLabels: ["RetryableWriteError"] - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after ExceededTimeLimit" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 262 - errorLabels: ["RetryableWriteError"] - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne fails after Interrupted" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 11601 - closeConnection: false - 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 WriteConcernError InterruptedAtShutdown" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorLabels: ["RetryableWriteError"] - writeConcernError: - code: 11600 - errmsg: Replication is being shut down - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after WriteConcernError InterruptedDueToReplStateChange" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorLabels: ["RetryableWriteError"] - writeConcernError: - code: 11602 - errmsg: Replication is being shut down - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after WriteConcernError PrimarySteppedDown" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorLabels: ["RetryableWriteError"] - writeConcernError: - code: 189 - errmsg: Replication is being shut down - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after WriteConcernError ShutdownInProgress" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorLabels: ["RetryableWriteError"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne fails after multiple retryable writeConcernErrors" - failPoint: - configureFailPoint: failCommand - mode: { times: 2 } - data: - failCommands: ["insert"] - errorLabels: ["RetryableWriteError"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 } - - { _id: 3, x: 33 } # The write was still applied. - - - description: "InsertOne fails after WriteConcernError Interrupted" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - writeConcernError: - code: 11601 - errmsg: operation was interrupted - 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 } - - { _id: 3, x: 33 } # The write was still applied. - - - description: "InsertOne fails after WriteConcernError WriteConcernFailed" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - writeConcernError: - code: 64 - codeName: WriteConcernFailed - errmsg: waiting for replication timed out - errInfo: {wtimeout: 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 } - - { _id: 3, x: 33 } # The write was still applied. - - - - 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 } diff --git a/test/spec/retryable-writes/legacy/insertOne.json b/test/spec/retryable-writes/legacy/insertOne.json deleted file mode 100644 index 04dee6dd68a..00000000000 --- a/test/spec/retryable-writes/legacy/insertOne.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "InsertOne is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/insertOne.yml b/test/spec/retryable-writes/legacy/insertOne.yml deleted file mode 100644 index ebfdf23e662..00000000000 --- a/test/spec/retryable-writes/legacy/insertOne.yml +++ /dev/null @@ -1,61 +0,0 @@ -runOn: - - - minServerVersion: "3.6" - topology: ["replicaset"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "InsertOne is committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne is not committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne is never committed" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/legacy/replaceOne-errorLabels.json b/test/spec/retryable-writes/legacy/replaceOne-errorLabels.json deleted file mode 100644 index 6029b875dcf..00000000000 --- a/test/spec/retryable-writes/legacy/replaceOne-errorLabels.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/replaceOne-errorLabels.yml b/test/spec/retryable-writes/legacy/replaceOne-errorLabels.yml deleted file mode 100644 index 41939092934..00000000000 --- a/test/spec/retryable-writes/legacy/replaceOne-errorLabels.yml +++ /dev/null @@ -1,53 +0,0 @@ -runOn: - - minServerVersion: "4.3.1" - topology: ["replicaset", "sharded", "load-balanced"] - -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 # WriteConflict, not a retryable error code - errorLabels: ["RetryableWriteError"] # Override server behavior: send RetryableWriteError label with non-retryable error code - operation: - name: "replaceOne" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - outcome: # Driver retries operation and it succeeds - 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 # InterruptedAtShutdown, normally a retryable error code - errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code - operation: - name: "replaceOne" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - outcome: - error: true # Driver does not retry operation because there was no RetryableWriteError label on response - result: - errorLabelsOmit: ["RetryableWriteError"] - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/legacy/replaceOne-serverErrors.json b/test/spec/retryable-writes/legacy/replaceOne-serverErrors.json deleted file mode 100644 index 6b35722e12c..00000000000 --- a/test/spec/retryable-writes/legacy/replaceOne-serverErrors.json +++ /dev/null @@ -1,177 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "ReplaceOne succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 189, - "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 succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "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 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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/replaceOne-serverErrors.yml b/test/spec/retryable-writes/legacy/replaceOne-serverErrors.yml deleted file mode 100644 index 3dd79db750e..00000000000 --- a/test/spec/retryable-writes/legacy/replaceOne-serverErrors.yml +++ /dev/null @@ -1,82 +0,0 @@ -runOn: - - - minServerVersion: "4.0" - topology: ["replicaset"] - - - minServerVersion: "4.1.7" - topology: ["sharded", "load-balanced"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "ReplaceOne succeeds after PrimarySteppedDown" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["update"] - errorCode: 189 - 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 succeeds after WriteConcernError ShutdownInProgress" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["update"] - errorLabels: ["RetryableWriteError"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 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 } diff --git a/test/spec/retryable-writes/legacy/replaceOne.json b/test/spec/retryable-writes/legacy/replaceOne.json deleted file mode 100644 index e5b8cf8eabb..00000000000 --- a/test/spec/retryable-writes/legacy/replaceOne.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "ReplaceOne is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "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 is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "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 is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/replaceOne.yml b/test/spec/retryable-writes/legacy/replaceOne.yml deleted file mode 100644 index 0000904a401..00000000000 --- a/test/spec/retryable-writes/legacy/replaceOne.yml +++ /dev/null @@ -1,66 +0,0 @@ -runOn: - - - minServerVersion: "3.6" - topology: ["replicaset"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "ReplaceOne is committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - 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 is not committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - 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 is never committed" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "replaceOne" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/legacy/updateMany.json b/test/spec/retryable-writes/legacy/updateMany.json deleted file mode 100644 index 46fef73e742..00000000000 --- a/test/spec/retryable-writes/legacy/updateMany.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "UpdateMany ignores retryWrites", - "useMultipleMongoses": true, - "operation": { - "name": "updateMany", - "arguments": { - "filter": {}, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 23 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/updateMany.yml b/test/spec/retryable-writes/legacy/updateMany.yml deleted file mode 100644 index f3ab39faa32..00000000000 --- a/test/spec/retryable-writes/legacy/updateMany.yml +++ /dev/null @@ -1,27 +0,0 @@ -runOn: - - - minServerVersion: "3.6" - topology: ["replicaset", "sharded", "load-balanced"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "UpdateMany ignores retryWrites" - useMultipleMongoses: true - operation: - name: "updateMany" - arguments: - filter: { } - update: { $inc: { x : 1 }} - outcome: - result: - matchedCount: 2 - modifiedCount: 2 - upsertedCount: 0 - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 23 } diff --git a/test/spec/retryable-writes/legacy/updateOne-errorLabels.json b/test/spec/retryable-writes/legacy/updateOne-errorLabels.json deleted file mode 100644 index 5bd00cde904..00000000000 --- a/test/spec/retryable-writes/legacy/updateOne-errorLabels.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/updateOne-errorLabels.yml b/test/spec/retryable-writes/legacy/updateOne-errorLabels.yml deleted file mode 100644 index 6bfef3b1299..00000000000 --- a/test/spec/retryable-writes/legacy/updateOne-errorLabels.yml +++ /dev/null @@ -1,53 +0,0 @@ -runOn: - - minServerVersion: "4.3.1" - topology: ["replicaset", "sharded", "load-balanced"] - -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 # WriteConflict, not a retryable error code - errorLabels: ["RetryableWriteError"] # Override server behavior: send RetryableWriteError label with non-retryable error code - operation: - name: "updateOne" - arguments: - filter: { _id: 1 } - update: { $inc: { x: 1 } } - outcome: # Driver retries operation and it succeeds - 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 # InterruptedAtShutdown, normally a retryable error code - errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code - operation: - name: "updateOne" - arguments: - filter: { _id: 1 } - update: { $inc: { x: 1 } } - outcome: - error: true # Driver does not retry operation because there was no RetryableWriteError label on response - result: - errorLabelsOmit: ["RetryableWriteError"] - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/legacy/updateOne-serverErrors.json b/test/spec/retryable-writes/legacy/updateOne-serverErrors.json deleted file mode 100644 index cf274f57e09..00000000000 --- a/test/spec/retryable-writes/legacy/updateOne-serverErrors.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "UpdateOne succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 189, - "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 succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "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 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 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/updateOne-serverErrors.yml b/test/spec/retryable-writes/legacy/updateOne-serverErrors.yml deleted file mode 100644 index fffe851cae1..00000000000 --- a/test/spec/retryable-writes/legacy/updateOne-serverErrors.yml +++ /dev/null @@ -1,82 +0,0 @@ -runOn: - - - minServerVersion: "4.0" - topology: ["replicaset"] - - - minServerVersion: "4.1.7" - topology: ["sharded", "load-balanced"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "UpdateOne succeeds after PrimarySteppedDown" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["update"] - errorCode: 189 - 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 succeeds after WriteConcernError ShutdownInProgress" - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["update"] - errorLabels: ["RetryableWriteError"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 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 } diff --git a/test/spec/retryable-writes/legacy/updateOne.json b/test/spec/retryable-writes/legacy/updateOne.json deleted file mode 100644 index 0f806dc3d84..00000000000 --- a/test/spec/retryable-writes/legacy/updateOne.json +++ /dev/null @@ -1,288 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "UpdateOne is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "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 is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "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 is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "UpdateOne with upsert is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3, - "x": 33 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 34 - } - ] - } - } - }, - { - "description": "UpdateOne with upsert is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3, - "x": 33 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 34 - } - ] - } - } - }, - { - "description": "UpdateOne with upsert is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3, - "x": 33 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/test/spec/retryable-writes/legacy/updateOne.yml b/test/spec/retryable-writes/legacy/updateOne.yml deleted file mode 100644 index 56b7d822b4f..00000000000 --- a/test/spec/retryable-writes/legacy/updateOne.yml +++ /dev/null @@ -1,129 +0,0 @@ -runOn: - - - minServerVersion: "3.6" - topology: ["replicaset"] - -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -tests: - - - description: "UpdateOne is committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - 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 is not committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - 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 is never committed" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "updateOne" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - - description: "UpdateOne with upsert is committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "updateOne" - arguments: - filter: { _id: 3, x: 33 } - update: { $inc: { x : 1 }} - upsert: true - outcome: - result: - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 1 - upsertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 34 } - - - description: "UpdateOne with upsert is not committed on first attempt" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "updateOne" - arguments: - filter: { _id: 3, x: 33 } - update: { $inc: { x : 1 }} - upsert: true - outcome: - result: - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 1 - upsertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 34 } - - - description: "UpdateOne with upsert is never committed" - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "updateOne" - arguments: - filter: { _id: 3, x: 33 } - update: { $inc: { x : 1 }} - upsert: true - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/retryable-writes.md b/test/spec/retryable-writes/retryable-writes.md new file mode 100644 index 00000000000..1adfe21e189 --- /dev/null +++ b/test/spec/retryable-writes/retryable-writes.md @@ -0,0 +1,781 @@ +# Retryable Writes + +- Status: Accepted +- Minimum Server Version: 3.6 + +______________________________________________________________________ + +## Abstract + +MongoDB 3.6 will implement support for server sessions, which are shared resources within a cluster identified by a +session ID. Drivers compatible with MongoDB 3.6 will also implement support for client sessions, which are always +associated with a server session and will allow for certain commands to be executed within the context of a server +session. + +Additionally, MongoDB 3.6 will utilize server sessions to allow some write commands to specify a transaction ID to +enforce at-most-once semantics for the write operation(s) and allow for retrying the operation if the driver fails to +obtain a write result (e.g. network error or "not writable primary" error after a replica set failover). This +specification will outline how an API for retryable write operations will be implemented in drivers. The specification +will define an option to enable retryable writes for an application and describe how a transaction ID will be provided +to write commands executed therein. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +**Transaction ID**\ +The transaction ID identifies the transaction as part of which the command is running. In a write +command where the client has requested retryable behavior, it is expressed by the top-level `lsid` and `txnNumber` +fields. The `lsid` component is the corresponding server session ID. which is a BSON value defined in the +[Driver Session](../sessions/driver-sessions.md) specification. The `txnNumber` component is a monotonically increasing +(per server session), positive 64-bit integer. + +**ClientSession**\ +Driver object representing a client session, which is defined in the +[Driver Session](../sessions/driver-sessions.md) specification. This object is always associated with a server session; +however, drivers will pool server sessions so that creating a ClientSession will not always entail creation of a new +server session. The name of this object MAY vary across drivers. + +**Retryable Error**\ +An error is considered retryable if it has a RetryableWriteError label in its top-level +"errorLabels" field. See [Determining Retryable Errors](#determining-retryable-errors) for more information. + +Additional terms may be defined in the [Driver Session](../sessions/driver-sessions.md) specification. + +### Naming Deviations + +This specification defines the name for a new MongoClient option, `retryWrites`. Drivers MUST use the defined name for +the connection string parameter to ensure portability of connection strings across applications and drivers. + +If drivers solicit MongoClient options through another mechanism (e.g. options dictionary provided to the MongoClient +constructor), drivers SHOULD use the defined name but MAY deviate to comply with their existing conventions. For +example, a driver may use `retry_writes` instead of `retryWrites`. + +For any other names in the spec, drivers SHOULD use the defined name but MAY deviate to comply with their existing +conventions. + +### MongoClient Configuration + +This specification introduces the following client-level configuration option. + +#### retryWrites + +This boolean option determines whether retryable behavior will be applied to all supported write operations executed +within the MongoClient. This option MUST default to true. + +This option MUST NOT be configurable at the level of a database object, collection object, or at the level of an +individual write operation. + +### Requirements for Retryable Writes + +#### Supported Server Versions + +Like sessions, retryable writes require a MongoDB 3.6 replica set or shard cluster operating with feature compatibility +version 3.6 (i.e. the `{setFeatureCompatibilityVersion: 3.6}` administrative command has been run on the cluster). +Drivers MUST verify server eligibility by ensuring that `maxWireVersion` is at least six, the +`logicalSessionTimeoutMinutes` field is present in the server's `hello` or legacy hello response, and the server type is +not standalone. + +Retryable writes are only supported by storage engines that support document-level locking. Notably, that excludes the +MMAPv1 storage engine which is available in both MongoDB 3.6 and 4.0. Since `retryWrites` defaults to `true`, Drivers +MUST raise an actionable error message when the server returns code 20 with errmsg starting with "Transaction numbers". +The replacement error message MUST be: + +``` +This MongoDB deployment does not support retryable writes. Please add +retryWrites=false to your connection string. +``` + +If the server selected for the first attempt of a retryable write operation does not support retryable writes, drivers +MUST execute the write as if retryable writes were not enabled. Drivers MUST NOT include a transaction ID in the write +command and MUST not retry the command under any circumstances. + +In a sharded cluster, it is possible that mongos may appear to support retryable writes but one or more shards in the +cluster do not (e.g. replica set shard is configured with feature compatibility version 3.4, a standalone is added as a +new shard). In these rare cases, a write command that fans out to a shard that does not support retryable writes may +partially fail and an error may be reported in the write result from mongos (e.g. `writeErrors` array in the bulk write +result). This does not constitute a retryable error. Drivers MUST relay such errors to the user. + +#### Supported Write Operations + +MongoDB 3.6 will support retryability for some, but not all, write operations. + +Supported single-statement write operations include `insertOne()`, `updateOne()`, `replaceOne()`, `deleteOne()`, +`findOneAndDelete()`, `findOneAndReplace()`, and `findOneAndUpdate()`. + +Supported multi-statement write operations include `insertMany()` and `bulkWrite()`. The ordered option may be `true` or +`false`. For both the collection-level and client-level `bulkWrite()` methods, a bulk write batch is only retryable if +it does not contain any `multi: true` writes (i.e. `UpdateMany` and `DeleteMany`). Drivers MUST evaluate eligibility for +each write command sent as part of the `bulkWrite()` (after order and batch splitting) individually. Drivers MUST NOT +alter existing logic for order and batch splitting in an attempt to maximize retryability for operations within a bulk +write. + +These methods above are defined in the [CRUD](../crud/crud.md) specification. + +Later versions of MongoDB may add support for additional write operations. + +Drivers MUST document operations that support retryable behavior and the conditions for which retryability is determined +(see: [How will users know which operations are supported?](#how-will-users-know-which-operations-are-supported)). +Drivers are not required to exhaustively document all operations that do not support retryable behavior. + +#### Unsupported Write Operations + +Write commands specifying an unacknowledged write concern (e.g. `{w: 0})`) do not support retryable behavior. Drivers +MUST NOT add a transaction ID to any write command with an unacknowledged write concern executed within a MongoClient +where retryable writes have been enabled. Drivers MUST NOT retry these commands. + +Write commands where a single statement might affect multiple documents will not be initially supported by MongoDB 3.6, +although this may change in the future. This includes an +[update](https://www.mongodb.com/docs/manual/reference/command/update/) command where any statement in the updates +sequence specifies a `multi` option of `true` or a +[delete](https://www.mongodb.com/docs/manual/reference/command/delete/) command where any statement in the `deletes` +sequence specifies a `limit` option of `0`. In the context of the [CRUD](../crud/crud.md) specification, this includes +the `updateMany()` and `deleteMany()` methods and, in some cases, `bulkWrite()`. Drivers MUST NOT add a transaction ID +to any single- or multi-statement write commands that include one or more multi-document write operations. Drivers MUST +NOT retry these commands if they fail to return a response. With regard to `bulkWrite()`, drivers MUST evaluate +eligibility for each write command sent as part of the `bulkWrite()` (after order and batch splitting) individually. + +Write commands other than [insert](https://www.mongodb.com/docs/manual/reference/command/insert/), +[update](https://www.mongodb.com/docs/manual/reference/command/update/), +[delete](https://www.mongodb.com/docs/manual/reference/command/delete/), or +[findAndModify](https://www.mongodb.com/docs/manual/reference/command/findAndModify/) will not be initially supported by +MongoDB 3.6, although this may change in the future. This includes, but is not limited to, an +[aggregate](https://www.mongodb.com/docs/manual/reference/command/aggregate/) command using a write stage (e.g. `$out`, +`$merge`). Drivers MUST NOT add a transaction ID to these commands and MUST NOT retry these commands if they fail to +return a response. + +#### Retryable Writes Within Transactions + +In MongoDB 4.0 the only supported retryable write commands within a transaction are `commitTransaction` and +`abortTransaction`. Therefore drivers MUST NOT retry write commands within transactions even when `retryWrites` has been +set to true on the `MongoClient`. In addition, drivers MUST NOT add the `RetryableWriteError` label to any error that +occurs during a write command within a transaction (excepting `commitTransation` and `abortTransaction`), even when +`retryWrites` has been set to true on the `MongoClient`. + +### Implementing Retryable Writes + +#### Determining Retryable Errors + +When connected to a MongoDB instance that supports retryable writes (versions 3.6+), the driver MUST treat all errors +with the RetryableWriteError label as retryable. This error label can be found in the top-level "errorLabels" field of +the error. + +##### RetryableWriteError Labels + +The RetryableWriteError label might be added to an error in a variety of ways: + +- When the driver encounters a network error establishing an initial connection to a server, it MUST add a + RetryableWriteError label to that error if the MongoClient performing the operation has the retryWrites configuration + option set to true. + +- When the driver encounters a network error communicating with any server version that supports retryable writes, it + MUST add a RetryableWriteError label to that error if the MongoClient performing the operation has the retryWrites + configuration option set to true. + +- When a CMAP-compliant driver encounters a + [PoolClearedError](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#connection-pool-errors) + during connection check out, it MUST add a RetryableWriteError label to that error if the MongoClient performing the + operation has the retryWrites configuration option set to true. + +- For server versions 4.4 and newer, the server will add a RetryableWriteError label to errors or server responses that + it considers retryable before returning them to the driver. As new server versions are released, the errors that are + labeled with the RetryableWriteError label may change. Drivers MUST NOT add a RetryableWriteError label to any error + derived from a 4.4+ server response (i.e. any error that is not a network error). + +- When receiving a command result with an error from a pre-4.4 server that supports retryable writes, the driver MUST + add a RetryableWriteError label to errors that meet the following criteria if the retryWrites option is set to true on + the client performing the relevant operation: + + - a mongod or mongos response with any the following error codes in the top-level `code` field: + + | Error Name | Error Code | + | ------------------------------- | ---------- | + | InterruptedAtShutdown | 11600 | + | InterruptedDueToReplStateChange | 11602 | + | NotWritablePrimary | 10107 | + | NotPrimaryNoSecondaryOk | 13435 | + | NotPrimaryOrSecondary | 13436 | + | PrimarySteppedDown | 189 | + | ShutdownInProgress | 91 | + | HostNotFound | 7 | + | HostUnreachable | 6 | + | NetworkTimeout | 89 | + | SocketException | 9001 | + | ExceededTimeLimit | 262 | + + - a mongod response with any of the previously listed codes in the `writeConcernError.code` field. + + Drivers MUST NOT add a RetryableWriteError label based on the following: + + - any `writeErrors[].code` fields in a mongod or mongos response + - the `writeConcernError.code` field in a mongos response + + The criteria for retryable errors is similar to the discussion in the SDAM spec's section on + [Error Handling](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#error-handling), but includes + additional error codes. See [What do the additional error codes mean?](#what-do-the-additional-error-codes-mean) for + the reasoning behind these additional errors. + +To understand why the driver should only add the RetryableWriteError label to an error when the retryWrites option is +true on the MongoClient performing the operation, see +[Why does the driver only add the RetryableWriteError label to errors that occur on a MongoClient with retryWrites set to true?](#why-does-the-driver-only-add-the-retryablewriteerror-label-to-errors-that-occur-on-a-mongoclient-with-retrywrites-set-to-true) + +Note: During a retryable write operation on a sharded cluster, mongos may retry the operation internally, in which case +it will not add a RetryableWriteError label to any error that occurs after those internal retries to prevent excessive +retrying. + +For more information about error labels, see the +[Transactions specification](../transactions/transactions.md#error-labels). + +#### Generating Transaction IDs + +The server requires each retryable write operation to provide a unique transaction ID in its command document. The +transaction ID consists of a server session ID and a monotonically increasing transaction number. The session ID is +obtained from the ClientSession object, which will have either been passed to the write operation from the application +or constructed internally for the operation. Drivers will be responsible for maintaining a monotonically increasing +transaction number for each server session used by a ClientSession object. Drivers that pool server sessions MUST +preserve the transaction number when reusing a server session from the pool with a new ClientSession (this can be +tracked as another property on the driver's object for the server session). + +Drivers MUST ensure that each retryable write command specifies a transaction number larger than any previously used +transaction number for its session ID. + +Since ClientSession objects are not thread safe and may only be used by one thread at a time, drivers should not need to +worry about race conditions when incrementing the transaction number. + +#### Behavioral Changes for Write Commands + +Drivers MUST automatically add a transaction ID to all supported write commands executed via a specific +[CRUD](../crud/crud.md) method (e.g. `updateOne()`) or write command method (e.g. `executeWriteCommand()`) within a +MongoClient where retryable writes have been enabled and when the selected server supports retryable writes. + +If your driver offers a generic command method on your database object (e.g. `runCommand()`), it MUST NOT check the +user's command document to determine if it is a supported write operation and MUST NOT automatically add a transaction +ID. The method should send the user's command document to the server as-is. + +This specification does not affect write commands executed within a MongoClient where retryable writes have not been +enabled. + +#### Constructing Write Commands + +When constructing a supported write command that will be executed within a MongoClient where retryable writes have been +enabled, drivers MUST increment the transaction number for the corresponding server session and include the server +session ID and transaction number in top-level `lsid` and `txnNumber` fields, respectively. `lsid` is a BSON value +(discussed in the [Driver Session](../sessions/driver-sessions.md) specification). `txnNumber` MUST be a positive 64-bit +integer (BSON type 0x12). + +The following example illustrates a possible write command for an `updateOne()` operation: + +```typescript +{ + update: "coll", + lsid: { ... }, + txnNumber: 100, + updates: [ + { q: { x: 1 }, u: { $inc: { y: 1 } } }, + ], + ordered: true +} +``` + +When constructing multiple write commands for a multi-statement write operation (i.e. `insertMany()` and `bulkWrite()`), +drivers MUST increment the transaction number for each supported write command in the batch. + +#### Executing Retryable Write Commands + +When selecting a writable server for the first attempt of a retryable write command, drivers MUST allow a server +selection error to propagate. In this case, the caller is able to infer that no attempt was made. + +If retryable writes is not enabled or the selected server does not support retryable writes, drivers MUST NOT include a +transaction ID in the command and MUST attempt to execute the write command exactly once and allow any errors to +propagate. In this case, the caller is able to infer that an attempt was made. + +If retryable writes are enabled and the selected server supports retryable writes, drivers MUST add a transaction ID to +the command. Drivers MUST only attempt to retry a write command if the first attempt yields a retryable error. Drivers +MUST NOT attempt to retry a write command on any other error. + +If the first attempt of a write command including a transaction ID encounters a retryable error, the driver MUST update +its topology according to the SDAM spec (see: +[Error Handling](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#error-handling)) and capture this +original retryable error. + +Drivers MUST then retry the operation as many times as necessary until any one of the following conditions is reached: + +- the operation succeeds. +- the operation fails with a non-retryable error. +- CSOT is enabled and the operation times out per + [Client Side Operations Timeout: Retryability](../client-side-operations-timeout/client-side-operations-timeout.md#retryability). +- CSOT is not enabled and one retry was attempted. + +For each retry attempt, drivers MUST select a writable server. In a sharded cluster, the server on which the operation +failed MUST be provided to the server selection mechanism as a deprioritized server. + +If the driver cannot select a server for a retry attempt or the selected server does not support retryable writes, +retrying is not possible and drivers MUST raise the retryable error from the previous attempt. In both cases, the caller +is able to infer that an attempt was made. + +If a retry attempt also fails, drivers MUST update their topology according to the SDAM spec (see: +[Error Handling](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#error-handling)). If an error +would not allow the caller to infer that an attempt was made (e.g. connection pool exception originating from the +driver) or the error is labeled "NoWritesPerformed", the error from the previous attempt should be raised. If all server +errors are labeled "NoWritesPerformed", then the first error should be raised. + +If a driver associates server information (e.g. the server address or description) with an error, the driver MUST ensure +that the reported server information corresponds to the server that originated the error. + +The above rules are implemented in the following pseudo-code: + +```typescript +/** + * Checks if a server supports retryable writes. + */ +function isRetryableWritesSupported(server) { + if (server.getMaxWireVersion() < RETRYABLE_WIRE_VERSION) { + return false; + } + + if ( ! server.hasLogicalSessionTimeoutMinutes()) { + return false; + } + + if (server.isStandalone()) { + return false; + } + + return true; +} + +/** + * Executes a write command in the context of a MongoClient where retryable + * writes have been enabled. The session parameter may be an implicit or + * explicit client session (depending on how the CRUD method was invoked). + */ +function executeRetryableWrite(command, session) { + /* Allow ServerSelectionException to propagate to our caller, which can then + * assume that no attempts were made. */ + server = selectServer("writable"); + + /* If the server does not support retryable writes, execute the write as if + * retryable writes are not enabled. */ + if ( ! isRetryableWritesSupported(server)) { + return executeCommand(server, command); + } + + /* Incorporate lsid and txnNumber fields into the command document. These + * values will be derived from the implicit or explicit session object. */ + retryableCommand = addTransactionIdToCommand(command, session); + + Exception previousError = null; + retrying = false; + while true { + try { + return executeCommand(server, retryableCommand); + } catch (Exception currentError) { + handleError(currentError); + + /* If the error has a RetryableWriteError label, remember the exception + * and proceed with retrying the operation. + * + * IllegalOperation (code 20) with errmsg starting with "Transaction + * numbers" MUST be re-raised with an actionable error message. + */ + if (!currentError.hasErrorLabel("RetryableWriteError")) { + if ( currentError.code == 20 && previousError.errmsg.startsWith("Transaction numbers") ) { + currentError.errmsg = "This MongoDB deployment does not support retryable..."; + } + throw currentError; + } + + /* + * If the "previousError" is "null", then the "currentError" is the + * first error encountered during the retry attempt cycle. We must + * persist the first error in the case where all succeeding errors are + * labeled "NoWritesPerformed", which would otherwise raise "null" as + * the error. + */ + if (previousError == null) { + previousError = currentError; + } + + /* + * For exceptions that originate from the driver (e.g. no socket available + * from the connection pool), we should raise the previous error if there + * was one. + */ + if (currentError is not DriverException && ! previousError.hasErrorLabel("NoWritesPerformed")) { + previousError = currentError; + } + } + + /* + * We try to select server that is not the one that failed by passing the + * failed server as a deprioritized server. + * If we cannot select a writable server, do not proceed with retrying and + * throw the previous error. The caller can then infer that an attempt was + * made and failed. */ + try { + deprioritizedServers = [ server ]; + server = selectServer("writable", deprioritizedServers); + } catch (Exception ignoredError) { + throw previousError; + } + + /* If the server selected for retrying is too old, throw the previous error. + * The caller can then infer that an attempt was made and failed. This case + * is very rare, and likely means that the cluster is in the midst of a + * downgrade. */ + if ( ! isRetryableWritesSupported(server)) { + throw previousError; + } + + if (timeoutMS == null) { + /* If CSOT is not enabled, allow any retryable error from the second + * attempt to propagate to our caller, as it will be just as relevant + * (if not more relevant) than the original error. */ + if (retrying) { + throw previousError; + } + } else if (isExpired(timeoutMS)) { + /* CSOT is enabled and the operation has timed out. */ + throw previousError; + } + retrying = true; + } +} +``` + +`handleError` in the above pseudocode refers to the function defined in the +[Error handling pseudocode](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#error-handling-pseudocode) +section of the SDAM specification. + +When retrying a write command, drivers MUST resend the command with the same transaction ID. Drivers MUST NOT resend the +original wire protocol message if doing so would violate rules for +[gossipping the cluster time](../sessions/driver-sessions.md#gossipping-the-cluster-time) (see: +[Can drivers resend the same wire protocol message on retry attempts?](#can-drivers-resend-the-same-wire-protocol-message-on-retry-attempts)). + +In the case of a multi-statement write operation split across multiple write commands, a failed retry attempt will also +interrupt execution of any additional write operations in the batch (regardless of the ordered option). This is no +different than if a retryable error had been encountered without retryable behavior enabled or supported by the driver. +Drivers are encouraged to provide access to an intermediary write result (e.g. BulkWriteResult, InsertManyResult) +through the BulkWriteException, in accordance with the [CRUD](../crud/crud.md) specification. + +## Logging Retry Attempts + +Drivers MAY choose to log retry attempts for write operations. This specification does not define a format for such log +messages. + +## Command Monitoring + +In accordance with the +[Command Logging and Monitoring](../command-logging-and-monitoring/command-logging-and-monitoring.rst) specification, +drivers MUST guarantee that each `CommandStartedEvent` has either a correlating `CommandSucceededEvent` or +`CommandFailedEvent` and that every "command started" log message has either a correlating "command succeeded" log +message or "command failed" log message. If the first attempt of a retryable write operation encounters a retryable +error, drivers MUST fire a `CommandFailedEvent` and emit a "command failed" log message for the retryable error and fire +a separate `CommandStartedEvent` and "command succeeded" log message when executing the subsequent retry attempt. Note +that the second `CommandStartedEvent` and "command succeeded" log message may have a different `connectionId`, since a +writable server is reselected for the retry attempt. + +Each attempt of a retryable write operation SHOULD report a different `requestId` so that events for each attempt can be +properly correlated with one another. + +The [Command Logging and Monitoring](../command-logging-and-monitoring/command-logging-and-monitoring.rst) specification +states that the `operationId` field is a driver-generated, 64-bit integer and may be "used to link events together such +as bulk write operations." Each attempt of a retryable write operation SHOULD report the same `operationId`; however, +drivers SHOULD NOT use the `operationId` field to relay information about a transaction ID. A bulk write operation may +consist of multiple write commands, each of which may specify a unique transaction ID. + +## Test Plan + +See the [README](tests/README.md) for tests. + +At a high level, the test plan will cover the following scenarios for executing supported write operations within a +MongoClient where retryable writes have been enabled: + +- Executing the same write operation (and transaction ID) multiple times should yield an identical write result. +- Test at-most-once behavior by observing that subsequent executions of the same write operation do not incur further + modifications to the collection data. +- Exercise supported single-statement write operations (i.e. deleteOne, insertOne, replaceOne, updateOne, and + findAndModify). +- Exercise supported multi-statement insertMany and bulkWrite operations, which contain only supported single-statement + write operations. Both ordered and unordered execution should be tested. + +Additional prose tests for other scenarios are also included. + +## Motivation for Change + +Drivers currently have no API for specifying at-most-once semantics and retryable behavior for write operations. The +driver API needs to be extended to support this behavior. + +## Design Rationale + +The design of this specification piggy-backs that of the [Driver Session](../sessions/driver-sessions.md) specification +in that it modifies the driver API as little as possible to introduce the concept of at-most-once semantics and +retryable behavior for write operations. A transaction ID will be included in all supported write commands executed +within the scope of a MongoClient where retryable writes have been enabled. + +Drivers expect the server to yield an error if a transaction ID is included in an unsupported write command. This +requires drivers to maintain an allow list and track which write operations support retryable behavior for a given +server version (see: +[Why must drivers maintain an allow list of supported operations?](#why-must-drivers-maintain-an-allow-list-of-supported-operations)). + +While this approach will allow applications to take advantage of retryable write behavior with minimal code changes, it +also presents a documentation challenge. Users must understand exactly what can and will be retried (see: +[How will users know which operations are supported?](#how-will-users-know-which-operations-are-supported)). + +## Backwards Compatibility + +The API changes to support retryable writes extend the existing API but do not introduce any backward breaking changes. +Existing programs that do not make use of retryable writes will continue to compile and run correctly. + +## Reference Implementation + +The C# and C drivers will provide reference implementations. JIRA links will be added here at a later point. + +## Future Work + +Supporting at-most-once semantics and retryable behavior for updateMany and deleteMany operations may become possible +once the server implements support for multi-document transactions. + +A separate specification for retryable read operations could complement this specification. Retrying read operations +would not require client or server sessions and could be implemented independently of retryable writes. + +## Q & A + +### What do the additional error codes mean? + +The errors `HostNotFound`, `HostUnreachable`, `NetworkTimeout`, `SocketException` may be returned from mongos during +problems routing to a shard. These may be transient, or localized to that mongos. + +### Why are write operations only retried once by default? + +The spec concerns itself with retrying write operations that encounter a retryable error (i.e. no response due to +network error or a response indicating that the node is no longer a primary). A retryable error may be classified as +either a transient error (e.g. dropped connection, replica set failover) or persistent outage. In the case of a +transient error, the driver will mark the server as "unknown" per the +[SDAM](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) spec. A subsequent retry attempt will +allow the driver to rediscover the primary within the designated server selection timeout period (30 seconds by +default). If server selection times out during this retry attempt, we can reasonably assume that there is a persistent +outage. In the case of a persistent outage, multiple retry attempts are fruitless and would waste time. See +[How To Write Resilient MongoDB Applications](https://emptysqua.re/blog/how-to-write-resilient-mongodb-applications/) +for additional discussion on this strategy. + +However when [Client Side Operations Timeout](../client-side-operations-timeout/client-side-operations-timeout.rst) is +enabled, the driver will retry multiple times until the operation succeeds, a non-retryable error is encountered, or the +timeout expires. Retrying multiple times provides greater resilience to cascading failures such as rolling server +restarts during planned maintenance events. + +### What if the transaction number overflows? + +Since server sessions are pooled and session lifetimes are configurable on the server, it is theoretically possible for +the transaction number to overflow if it reaches the limits of a signed 64-bit integer. The spec does not address this +scenario. Drivers may decide to handle this as they wish. For example, they may raise a client-side error if a +transaction number would overflow, eagerly remove sessions with sufficiently high transactions numbers from the pool in +an attempt to limit such occurrences, or simply rely on the server to raise an error when a transaction number is +reused. + +### Why are unacknowledged write concerns unsupported? + +The server does not consider the write concern when deciding if a write operation supports retryable behavior. +Technically, operations with an unacknowledged write concern can specify a transaction ID and be retried. However, the +spec elects not to support unacknowledged write concerns due to various ways that drivers may issue write operations +with unacknowledged write concerns. + +When using `OP_QUERY` to issue a write command to the server, a command response is always returned. A write command +with an unacknowledged write concern (i.e. `{w: 0}`) will return a response of `{ok: 1}`. If a retryable error is +encountered (either a network error or "not writeable primary" response), the driver could attempt to retry the +operation by executing it again with the same transaction ID. + +Some drivers fall back to legacy opcodes (e.g. `OP_INSERT`) to execute write operations with an unacknowledged write +concern. In the future, `OP_MSG` may allow the server to avoid returning any response for write operations sent with an +unacknowledged write concern. In both of these cases, there is no response for which the driver might encounter a +retryable error and decide to retry the operation. + +Rather than depend on an implementation detail to determine if retryable behavior might apply, the spec has chosen to +not support retryable behavior for unacknowledged write concerns and guarantee a consistent user experience across all +drivers. + +### Why must drivers maintain an allow list of supported operations? + +Requiring that drivers maintain an allow list of supported write operations is unfortunate. It both adds complexity to +the driver's implementation and limits the driver's ability to immediately take advantage of new server functionality +(i.e. the driver must be upgraded to support additional write operations). + +Several other alternatives were discussed: + +- The server could inform drivers which write operations support retryable behavior in its `hello` or legacy hello + response. This would be a form of feature discovery, for which there is no established protocol. It would also add + complexity to the connection handshake. +- The server could ignore a transaction ID on the first observed attempt of an unsupported write command and only yield + an error on subsequent attempts. This would require the server to create a transaction record for unsupported writes + to avoid the risk of applying a write twice and ensuring that retry attempts could be differentiated. It also poses a + significant problem for sharding if a multi-document write does not reach all shards, since those shards would not + know to create a transaction record. +- The driver could allow more fine-grained control retryable write behavior by supporting a `retryWrites` option on the + database and collection objects. This would allow users to enable `retryWrites` on a MongoClient and disable it as + needed to execute unsupported write operations, or vice versa. Since we expect the `retryWrites` option to become less + relevant once transactions are implemented, we would prefer not to add the option throughout the driver API. + +### How will users know which operations are supported? + +The initial list of supported operations is already quite permissive. Most [CRUD](../crud/crud.md) operations are +supported apart from `updateMany()`, `deleteMany()`, and `aggregate()` with a write stage (e.g. `$out`, `$merge`). Other +write operations (e.g. `renameCollection`) are rare. + +That said, drivers will need to clearly document exactly which operations support retryable behavior. In the case +`bulkWrite()`, which may or may not support retryability, drivers should discuss how eligibility is determined. + +### Can drivers resend the same wire protocol message on retry attempts? + +Since retry attempts entail sending the same command and transaction ID to the server, drivers might consider resending +the same wire protocol message in order to avoid constructing a new message and computing its checksum. The server will +not complain if it receives two messages with the same `requestId`, as the field is only used for logging and populating +the `responseTo` field in its replies to the client. That said, re-using a wire protocol message might violate rules for +[gossipping the cluster time](../sessions/driver-sessions.md#gossipping-the-cluster-time) and might also have +implications for [Command Monitoring](#command-monitoring), since the original write command and its retry attempt may +report the same `requestId`. + +### Why can't drivers split bulk write commands to maximize retryability? + +In [Supported Write Operations](#supported-write-operations), the spec prohibits drivers from altering existing logic +for splits `bulkWrite()`'s `requests` parameter into write commands in an attempt to segregate unsupported, +multi-document write operations and maximize retryability for other, supported write operations. The reasoning behind +this prohibition is that such behavior would conflict with a primary goal of the bulk API in reducing the number of +command round-trips to the server. + +### retryWrites originally defaulted to false, why does it now default to true? + +Since the initial release of retryable writes in MongoDB 3.6 testing showed that the overhead for supported operations +was sufficiently small that there was no risk in changing the default. Additionally, the fact that some operations +continue to be unsupported for retryable writes (updateMany and deleteMany) does not seem to pose a problem in practice. + +### Why do drivers have to parse errmsg to determine storage engine support? + +There is no reliable way to determine the storage engine in use for shards in a sharded cluster, and replica sets (and +shards) can have mixed deployments using different storage engines on different members. This is especially true when a +replica set or sharded cluster is being upgraded from one storage engine to another. This could be common when upgrading +to MongoDB 4.2, where MMAPv1 is no longer supported. + +The server returns error code 20 (IllegalOperation) when the storage engine doesn't support document-level locking and +txnNumbers. Error code 20 is used for a large number of different error cases in the server so we need some other way to +differentiate this error case from any other. The error code and errmsg are the same in MongoDB 3.6 and 4.0, and the +same from a replica set or sharded cluster (mongos just forwards the error from the shard's replica set). + +### Why does the driver only add the RetryableWriteError label to errors that occur on a MongoClient with retryWrites set to true? + +The driver does this to maintain consistency with the MongoDB server. Servers that support the RetryableWriteError label +(MongoDB version 4.4 and newer) only add the label to an error when the client has added a txnNumber to the command, +which only happens when the retryWrites option is true on the client. For the driver to add the label even if +retryWrites is not true would be inconsistent with the server and potentially confusing to developers. + +## Changelog + +- 2024-05-08: Add guidance for client-level `bulkWrite()` retryability. + +- 2024-05-02: Migrated from reStructuredText to Markdown. + +- 2024-04-29: Fix the link to the Driver Sessions spec. + +- 2024-01-16: Do not use `writeConcernError.code` in pre-4.4 mongos response to\ + determine retryability. Do not use + `writeErrors[].code` in pre-4.4 server responses to determine retryability. + +- 2023-12-06: Clarify that writes are not retried within transactions. + +- 2023-12-05: Add that any server information associated with retryable\ + exceptions MUST reflect the originating server, + even in the presence of retries. + +- 2023-10-02: When CSOT is not enabled, one retry attempt occurs. + +- 2023-08-26: Require that in a sharded cluster the server on which the\ + operation failed MUST be provided to the server + selection mechanism as a deprioritized server. + +- 2022-11-17: Add logic for persisting "currentError" as "previousError" on first\ + retry attempt, avoiding raising + "null" errors. + +- 2022-11-09: CLAM must apply both events and log messages. + +- 2022-10-18: When CSOT is enabled multiple retry attempts may occur. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2022-01-25: Note that drivers should retry handshake network failures. + +- 2021-11-02: Clarify that error labels are only specified in a top-level field\ + of an error. + +- 2021-04-26: Replaced deprecated terminology + +- 2021-03-24: Require that PoolClearedErrors be retried + +- 2020-09-01: State the the driver should only add the RetryableWriteError label\ + to network errors when connected to a + 4.4+ server. + +- 2020-02-25: State that the driver should only add the RetryableWriteError label\ + when retryWrites is on, and make it + clear that mongos will sometimes perform internal retries and not return the RetryableWriteError label. + +- 2020-02-10: Remove redundant content in Tests section. + +- 2020-01-14: Add ExceededTimeLimit to the list of error codes that should\ + receive a RetryableWriteError label. + +- 2019-10-21: Change the definition of "retryable write" to be based on the\ + RetryableWriteError label. Stop requiring + drivers to parse errmsg to categorize retryable errors for pre-4.4 servers. + +- 2019-07-30: Drivers must rewrite error messages for error code 20 when\ + txnNumber is not supported by the storage + engine. + +- 2019-06-07: Mention `$merge` stage for aggregate alongside `$out` + +- 2019-05-29: Renamed InterruptedDueToStepDown to InterruptedDueToReplStateChange + +- 2019-03-06: retryWrites now defaults to true. + +- 2019-03-05: Prohibit resending wire protocol messages if doing so would violate\ + rules for gossipping the cluster + time. + +- 2018-06-07: WriteConcernFailed is not a retryable error code. + +- 2018-04-25: Evaluate retryable eligibility of bulkWrite() commands individually. + +- 2018-03-14: Clarify that retryable writes may fail with a FCV 3.4 shard. + +- 2017-11-02: Drivers should not raise errors if selected server does not support\ + retryable writes and instead fall + back to non-retryable behavior. In addition to wire protocol version, drivers may check for + `logicalSessionTimeoutMinutes` to determine if a server supports sessions and retryable writes. + +- 2017-10-26: Errors when retrying may be raised instead of the original error\ + provided they allow the user to infer + that an attempt was made. + +- 2017-10-23: Drivers must document operations that support retryability. + +- 2017-10-23: Raise the original retryable error if server selection or wire\ + protocol checks fail during the retry + attempt. Encourage drivers to provide intermediary write results after an unrecoverable failure during a bulk write. + +- 2017-10-18: Standalone servers do not support retryable writes. + +- 2017-10-18: Also retry writes after a "not writable primary" error. + +- 2017-10-08: Renamed `txnNum` to `txnNumber` and noted that it must be a\ + 64-bit integer (BSON type 0x12). + +- 2017-08-25: Drivers will maintain an allow list so that only supported write\ + operations may be retried. Transaction + IDs will not be included in unsupported write commands, irrespective of the `retryWrites` option. + +- 2017-08-18: `retryWrites` is now a MongoClient option. diff --git a/test/spec/retryable-writes/retryable-writes.rst b/test/spec/retryable-writes/retryable-writes.rst new file mode 100644 index 00000000000..9d1c8d2ef51 --- /dev/null +++ b/test/spec/retryable-writes/retryable-writes.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `retryable-writes.md `_. diff --git a/test/spec/retryable-writes/tests/README.md b/test/spec/retryable-writes/tests/README.md new file mode 100644 index 00000000000..e883ca368d4 --- /dev/null +++ b/test/spec/retryable-writes/tests/README.md @@ -0,0 +1,341 @@ +# Retryable Write Tests + +## Introduction + +The YAML and JSON files in this directory are platform-independent tests meant to exercise a driver's implementation of +retryable writes. These tests utilize the [Unified Test Format](../../unified-test-format/unified-test-format.md). + +Several prose tests, which are not easily expressed in YAML, are also presented in this file. Those tests will need to +be manually implemented by each driver. + +Tests will require a MongoClient created with options defined in the tests. Integration tests will require a running +MongoDB cluster with server versions 3.6.0 or later. The `{setFeatureCompatibilityVersion: 3.6}` admin command will also +need to have been executed to enable support for retryable writes on the cluster. Some tests may have more stringent +version requirements depending on the fail points used. + +## Use as Integration Tests + +Integration tests are expressed in YAML and can be run against a replica set or sharded cluster as denoted by the +top-level `runOn` field. Tests that rely on the `onPrimaryTransactionalWrite` fail point cannot be run against a sharded +cluster because the fail point is not supported by mongos. + +The tests exercise the following scenarios: + +- Single-statement write operations + - Each test expecting a write result will encounter at-most one network error for the write command. Retry attempts + should return without error and allow operation to succeed. Observation of the collection state will assert that the + write occurred at-most once. + - Each test expecting an error will encounter successive network errors for the write command. Observation of the + collection state will assert that the write was never committed on the server. +- Multi-statement write operations + - Each test expecting a write result will encounter at-most one network error for some write command(s) in the batch. + Retry attempts should return without error and allow the batch to ultimately succeed. Observation of the collection + state will assert that each write occurred at-most once. + - Each test expecting an error will encounter successive network errors for some write command in the batch. The batch + will ultimately fail with an error, but observation of the collection state will assert that the failing write was + never committed on the server. We may observe that earlier writes in the batch occurred at-most once. + +We cannot test a scenario where the first and second attempts both encounter network errors but the write does actually +commit during one of those attempts. This is because (1) the fail point only triggers when a write would be committed +and (2) the skip and times options are mutually exclusive. That said, such a test would mainly assert the server's +correctness for at-most once semantics and is not essential to assert driver correctness. + +## Split Batch Tests + +The YAML tests specify bulk write operations that are split by command type (e.g. sequence of insert, update, and delete +commands). Multi-statement write operations may also be split due to `maxWriteBatchSize`, `maxBsonObjectSize`, or +`maxMessageSizeBytes`. + +For instance, an insertMany operation with five 10 MiB documents executed using OP_MSG payload type 0 (i.e. entire +command in one document) would be split into five insert commands in order to respect the 16 MiB `maxBsonObjectSize` +limit. The same insertMany operation executed using OP_MSG payload type 1 (i.e. command arguments pulled out into a +separate payload vector) would be split into two insert commands in order to respect the 48 MB `maxMessageSizeBytes` +limit. + +Noting when a driver might split operations, the `onPrimaryTransactionalWrite` fail point's `skip` option may be used to +control when the fail point first triggers. Once triggered, the fail point will transition to the `alwaysOn` state until +disabled. Driver authors should also note that the server attempts to process all documents in a single insert command +within a single commit (i.e. one insert command with five documents may only trigger the fail point once). This behavior +is unique to insert commands (each statement in an update and delete command is processed independently). + +If testing an insert that is split into two commands, a `skip` of one will allow the fail point to trigger on the second +insert command (because all documents in the first command will be processed in the same commit). When testing an update +or delete that is split into two commands, the `skip` should be set to the number of statements in the first command to +allow the fail point to trigger on the second command. + +## Command Construction Tests + +Drivers should also assert that command documents are properly constructed with or without a transaction ID, depending +on whether the write operation is supported. +[Command Logging and Monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.rst) may be used to +check for the presence of a `txnNumber` field in the command document. Note that command documents may always include an +`lsid` field per the [Driver Session](../../sessions/driver-sessions.md) specification. + +These tests may be run against both a replica set and shard cluster. + +Drivers should test that transaction IDs are never included in commands for unsupported write operations: + +- Write commands with unacknowledged write concerns (e.g. `{w: 0}`) +- Unsupported single-statement write operations + - `updateMany()` + - `deleteMany()` +- Unsupported multi-statement write operations + - `bulkWrite()` that includes `UpdateMany` or `DeleteMany` +- Unsupported write commands + - `aggregate` with write stage (e.g. `$out`, `$merge`) + +Drivers should test that transactions IDs are always included in commands for supported write operations: + +- Supported single-statement write operations + - `insertOne()` + - `updateOne()` + - `replaceOne()` + - `deleteOne()` + - `findOneAndDelete()` + - `findOneAndReplace()` + - `findOneAndUpdate()` +- Supported multi-statement write operations + - `insertMany()` with `ordered=true` + - `insertMany()` with `ordered=false` + - `bulkWrite()` with `ordered=true` (no `UpdateMany` or `DeleteMany`) + - `bulkWrite()` with `ordered=false` (no `UpdateMany` or `DeleteMany`) + +## Prose Tests + +The following tests ensure that retryable writes work properly with replica sets and sharded clusters. + +### 1. Test that retryable writes raise an exception when using the MMAPv1 storage engine. + +For this test, execute a write operation, such as `insertOne`, which should generate an exception. Assert that the error +message is the replacement error message: + +``` +This MongoDB deployment does not support retryable writes. Please add +retryWrites=false to your connection string. +``` + +and the error code is 20. + +> [!NOTE] +> Drivers that rely on `serverStatus` to determine the storage engine in use MAY skip this test for sharded clusters, +> since `mongos` does not report this information in its `serverStatus` response. + +### 2. Test that drivers properly retry after encountering PoolClearedErrors. + +This test MUST be implemented by any driver that implements the CMAP specification. + +This test requires MongoDB 4.3.4+ for both the `errorLabels` and `blockConnection` fail point options. + +1. Create a client with maxPoolSize=1 and retryWrites=true. If testing against a sharded deployment, be sure to connect + to only a single mongos. + +2. Enable the following failpoint: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["insert"], + errorCode: 91, + blockConnection: true, + blockTimeMS: 1000, + errorLabels: ["RetryableWriteError"] + } + } + ``` + +3. Start two threads and attempt to perform an `insertOne` simultaneously on both. + +4. Verify that both `insertOne` attempts succeed. + +5. Via CMAP monitoring, assert that the first check out succeeds. + +6. Via CMAP monitoring, assert that a PoolClearedEvent is then emitted. + +7. Via CMAP monitoring, assert that the second check out then fails due to a connection error. + +8. Via Command Monitoring, assert that exactly three `insert` CommandStartedEvents were observed in total. + +9. Disable the failpoint. + +### 3. Test that drivers return the original error after encountering a WriteConcernError with a RetryableWriteError label. + +This test MUST: + +- be implemented by any driver that implements the Command Monitoring specification, +- only run against replica sets as mongos does not propagate the NoWritesPerformed label to the drivers. +- be run against server versions 6.0 and above. + +Additionally, this test requires drivers to set a fail point after an `insertOne` operation but before the subsequent +retry. Drivers that are unable to set a failCommand after the CommandSucceededEvent SHOULD use mocking or write a unit +test to cover the same sequence of events. + +1. Create a client with `retryWrites=true`. + +2. Configure a fail point with error code `91` (ShutdownInProgress): + + ```javascript + { + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + failCommands: ["insert"], + errorLabels: ["RetryableWriteError"], + writeConcernError: { code: 91 } + } + } + ``` + +3. Via the command monitoring CommandSucceededEvent, configure a fail point with error code `10107` (NotWritablePrimary) + and a NoWritesPerformed label: + + ```javascript + { + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + failCommands: ["insert"], + errorCode: 10107, + errorLabels: ["RetryableWriteError", "NoWritesPerformed"] + } + } + ``` + + Drivers SHOULD only configure the `10107` fail point command if the the succeeded event is for the `91` error + configured in step 2. + +4. Attempt an `insertOne` operation on any record for any database and collection. For the resulting error, assert that + the associated error code is `91`. + +5. Disable the fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "off" + } + ``` + +### 4. Test that in a sharded cluster writes are retried on a different mongos when one is available. + +This test MUST be executed against a sharded cluster that has at least two mongos instances, supports +`retryWrites=true`, has enabled the `configureFailPoint` command, and supports the `errorLabels` field (MongoDB 4.3.1+). + +> [!NOTE] +> This test cannot reliably distinguish "retry on a different mongos due to server deprioritization" (the behavior +> intended to be tested) from "retry on a different mongos due to normal SDAM randomized suitable server selection". +> Verify relevant code paths are correctly executed by the tests using external means such as a logging, debugger, code +> coverage tool, etc. + +1. Create two clients `s0` and `s1` that each connect to a single mongos from the sharded cluster. They must not connect + to the same mongos. + +2. Configure the following fail point for both `s0` and `s1`: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["insert"], + errorCode: 6, + errorLabels: ["RetryableWriteError"] + } + } + ``` + +3. Create a client `client` with `retryWrites=true` that connects to the cluster using the same two mongoses as `s0` and + `s1`. + +4. Enable failed command event monitoring for `client`. + +5. Execute an `insert` command with `client`. Assert that the command failed. + +6. Assert that two failed command events occurred. Assert that the failed command events occurred on different mongoses. + +7. Disable the fail points on both `s0` and `s1`. + +### 5. Test that in a sharded cluster writes are retried on the same mongos when no others are available. + +This test MUST be executed against a sharded cluster that supports `retryWrites=true`, has enabled the +`configureFailPoint` command, and supports the `errorLabels` field (MongoDB 4.3.1+). + +Note: this test cannot reliably distinguish "retry on a different mongos due to server deprioritization" (the behavior +intended to be tested) from "retry on a different mongos due to normal SDAM behavior of randomized suitable server +selection". Verify relevant code paths are correctly executed by the tests using external means such as a logging, +debugger, code coverage tool, etc. + +1. Create a client `s0` that connects to a single mongos from the cluster. + +2. Configure the following fail point for `s0`: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["insert"], + errorCode: 6, + errorLabels: ["RetryableWriteError"], + closeConnection: true + } + } + ``` + +3. Create a client `client` with `directConnection=false` (when not set by default) and `retryWrites=true` that connects + to the cluster using the same single mongos as `s0`. + +4. Enable succeeded and failed command event monitoring for `client`. + +5. Execute an `insert` command with `client`. Assert that the command succeeded. + +6. Assert that exactly one failed command event and one succeeded command event occurred. Assert that both events + occurred on the same mongos. + +7. Disable the fail point on `s0`. + +## Changelog + +- 2024-05-30: Migrated from reStructuredText to Markdown. + +- 2024-02-27: Convert legacy retryable writes tests to unified format. + +- 2024-02-21: Update prose test 4 and 5 to workaround SDAM behavior preventing\ + execution of deprioritization code + paths. + +- 2024-01-05: Fix typo in prose test title. + +- 2024-01-03: Note server version requirements for fail point options and revise\ + tests to specify the `errorLabels` + option at the top-level instead of within `writeConcernError`. + +- 2023-08-26: Add prose tests for retrying in a sharded cluster. + +- 2022-08-30: Add prose test verifying correct error handling for errors with\ + the NoWritesPerformed label, which is to + return the original error. + +- 2022-04-22: Clarifications to `serverless` and `useMultipleMongoses`. + +- 2021-08-27: Add `serverless` to `runOn`. Clarify behavior of\ + `useMultipleMongoses` for `LoadBalanced` topologies. + +- 2021-04-23: Add `load-balanced` to test topology requirements. + +- 2021-03-24: Add prose test verifying `PoolClearedErrors` are retried. + +- 2019-10-21: Add `errorLabelsContain` and `errorLabelsContain` fields to\ + `result` + +- 2019-08-07: Add Prose Tests section + +- 2019-06-07: Mention $merge stage for aggregate alongside $out + +- 2019-03-01: Add top-level `runOn` field to denote server version and/or\ + topology requirements requirements for the + test file. Removes the `minServerVersion` and `maxServerVersion` top-level fields, which are now expressed within + `runOn` elements. + + Add test-level `useMultipleMongoses` field. diff --git a/test/spec/retryable-writes/tests/etc/templates/handshakeError.yml.template b/test/spec/retryable-writes/tests/etc/templates/handshakeError.yml.template new file mode 100644 index 00000000000..d9037d5b20e --- /dev/null +++ b/test/spec/retryable-writes/tests/etc/templates/handshakeError.yml.template @@ -0,0 +1,148 @@ +# Tests in this file are generated from handshakeError.yml.template. + +description: "retryable writes handshake failures" + +schemaVersion: "1.3" + +runOnRequirements: + - minServerVersion: "4.2" + topologies: [replicaset, sharded, load-balanced] + auth: true + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - connectionCheckOutStartedEvent + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName retryable-writes-handshake-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + +tests: + # Because setting a failPoint creates a connection in the connection pool, run + # a ping operation that fails immediately after the failPoint operation in + # order to discard the connection before running the actual operation to be + # tested. The saslContinue command is used to avoid SDAM errors. + # + # Description of events: + # - Failpoint operation. + # - Creates a connection in the connection pool that must be closed. + # - Ping operation. + # - Triggers failpoint (first time). + # - Closes the connection made by the fail point operation. + # - Test operation. + # - New connection is created. + # - Triggers failpoint (second time). + # - Tests whether operation successfully retries the handshake and succeeds. +{% for operation in operations %} + - description: "{{operation.object}}.{{operation.operation_name}} succeeds after retryable handshake network error" + {%- if (operation.operation_name == 'clientBulkWrite') %} + runOnRequirements: + - minServerVersion: "8.0" # `bulkWrite` added to server 8.0 + {%- endif %} + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: {{operation.operation_name}} + object: *{{operation.object}} + {%- if operation.arguments|length > 0 %} + arguments: + {%- for arg in operation.arguments %} + {{arg}} + {%- endfor -%} + {%- endif %} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: {{operation.command_name}} + - commandSucceededEvent: + commandName: {{operation.command_name}} + + - description: "{{operation.object}}.{{operation.operation_name}} succeeds after retryable handshake server error (ShutdownInProgress)" + {%- if (operation.operation_name == 'clientBulkWrite') %} + runOnRequirements: + - minServerVersion: "8.0" # `bulkWrite` added to server 8.0 + {%- endif %} + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: {{operation.operation_name}} + object: *{{operation.object}} + {%- if operation.arguments|length > 0 %} + arguments: + {%- for arg in operation.arguments %} + {{arg}} + {%- endfor -%} + {%- endif %} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: {{operation.command_name}} + - commandSucceededEvent: + commandName: {{operation.command_name}} +{% endfor -%} diff --git a/test/spec/retryable-writes/tests/unified/bulkWrite-errorLabels.json b/test/spec/retryable-writes/tests/unified/bulkWrite-errorLabels.json new file mode 100644 index 00000000000..13ba9bae757 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/bulkWrite-errorLabels.json @@ -0,0 +1,416 @@ +{ + "description": "bulkWrite-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "1": 3 + } + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "BulkWrite fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "BulkWrite succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "1": 3 + } + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "BulkWrite succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "1": 3 + } + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/bulkWrite-errorLabels.yml b/test/spec/retryable-writes/tests/unified/bulkWrite-errorLabels.yml new file mode 100644 index 00000000000..9adec6de718 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/bulkWrite-errorLabels.yml @@ -0,0 +1,222 @@ +description: bulkWrite-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'BulkWrite succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteOne: + filter: { _id: 1 } + - + insertOne: + document: { _id: 3, x: 33 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + # Driver retries operation and it succeeds + expectResult: + deletedCount: 1 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '1': 3 } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 23 } + - { _id: 3, x: 33 } + - + description: 'BulkWrite fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteOne: + filter: { _id: 1 } + - + insertOne: + document: { _id: 3, x: 33 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'BulkWrite succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteOne: + filter: { _id: 1 } + - + insertOne: + document: { _id: 3, x: 33 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + expectResult: + deletedCount: 1 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '1': 3 } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 23 } + - { _id: 3, x: 33 } + - + description: 'BulkWrite succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteOne: + filter: { _id: 1 } + - + insertOne: + document: { _id: 3, x: 33 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + expectResult: + deletedCount: 1 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '1': 3 } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 23 } + - { _id: 3, x: 33 } diff --git a/test/spec/retryable-writes/unified/bulkWrite-serverErrors.json b/test/spec/retryable-writes/tests/unified/bulkWrite-serverErrors.json similarity index 69% rename from test/spec/retryable-writes/unified/bulkWrite-serverErrors.json rename to test/spec/retryable-writes/tests/unified/bulkWrite-serverErrors.json index 737a1555e81..0a063ab4d99 100644 --- a/test/spec/retryable-writes/unified/bulkWrite-serverErrors.json +++ b/test/spec/retryable-writes/tests/unified/bulkWrite-serverErrors.json @@ -1,6 +1,6 @@ { "description": "retryable-writes bulkWrite serverErrors", - "schemaVersion": "1.0", + "schemaVersion": "1.3", "runOnRequirements": [ { "minServerVersion": "4.0", @@ -11,7 +11,8 @@ { "minServerVersion": "4.1.7", "topologies": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -197,6 +198,88 @@ ] } ] + }, + { + "description": "BulkWrite fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] } ] } diff --git a/test/spec/retryable-writes/unified/bulkWrite-serverErrors.yml b/test/spec/retryable-writes/tests/unified/bulkWrite-serverErrors.yml similarity index 70% rename from test/spec/retryable-writes/unified/bulkWrite-serverErrors.yml rename to test/spec/retryable-writes/tests/unified/bulkWrite-serverErrors.yml index 332dd5aae88..a88a2061231 100644 --- a/test/spec/retryable-writes/unified/bulkWrite-serverErrors.yml +++ b/test/spec/retryable-writes/tests/unified/bulkWrite-serverErrors.yml @@ -1,12 +1,12 @@ description: "retryable-writes bulkWrite serverErrors" -schemaVersion: "1.0" +schemaVersion: "1.3" runOnRequirements: - minServerVersion: "4.0" topologies: [ replicaset ] - minServerVersion: "4.1.7" - topologies: [ sharded ] + topologies: [ sharded, load-balanced ] createEntities: - client: @@ -93,3 +93,44 @@ tests: documents: - { _id: 1, x: 11 } - { _id: 3, x: 33 } # The write was still applied + - + description: 'BulkWrite fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ update ] + closeConnection: true + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteOne: + filter: { _id: 1 } + - + insertOne: + document: { _id: 3, x: 33 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/test/spec/retryable-writes/tests/unified/bulkWrite.json b/test/spec/retryable-writes/tests/unified/bulkWrite.json new file mode 100644 index 00000000000..691321746b2 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/bulkWrite.json @@ -0,0 +1,931 @@ +{ + "description": "bulkWrite", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "First command is retried", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 2 + } + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 23 + } + ] + } + ] + }, + { + "description": "All commands are retried", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 7 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 4, + "x": 44 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + } + }, + { + "insertOne": { + "document": { + "_id": 5, + "x": 55 + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 3 + }, + "replacement": { + "_id": 3, + "x": 333 + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 3, + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "2": 3, + "4": 5 + } + }, + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 1, + "upsertedIds": { + "3": 4 + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 333 + }, + { + "_id": 4, + "x": 45 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ] + }, + { + "description": "Both commands are retried after their first statement fails", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 2 + } + }, + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 23 + } + ] + } + ] + }, + { + "description": "Second command is retried after its second statement fails", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "skip": 2 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 2 + } + }, + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 23 + } + ] + } + ] + }, + { + "description": "BulkWrite with unordered execution", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + } + ], + "ordered": false + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 2, + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "First insertOne is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "Second updateOne is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "skip": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 2 + } + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "Third updateOne is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "skip": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "1": 2 + } + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "Single-document write following deleteMany is retried", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteMany": { + "filter": { + "x": 11 + } + } + }, + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "1": 2 + } + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "Single-document write following updateMany is retried", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "x": 11 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "1": 2 + } + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/bulkWrite.yml b/test/spec/retryable-writes/tests/unified/bulkWrite.yml new file mode 100644 index 00000000000..d064c9dbc52 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/bulkWrite.yml @@ -0,0 +1,495 @@ +description: bulkWrite + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + +tests: + - + description: 'First command is retried' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + - + deleteOne: + filter: { _id: 1 } + ordered: true + expectResult: + deletedCount: 1 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '0': 2 } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 23 } + - + # Write operations in this ordered batch are intentionally sequenced so that + # each write command consists of a single statement, which will fail on the + # first attempt and succeed on the second, retry attempt. + description: 'All commands are retried' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 7 } + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + - + insertOne: + document: { _id: 3, x: 33 } + - + updateOne: + filter: { _id: 4, x: 44 } + update: { $inc: { x: 1 } } + upsert: true + - + insertOne: + document: { _id: 5, x: 55 } + - + replaceOne: + filter: { _id: 3 } + replacement: { _id: 3, x: 333 } + - + deleteOne: + filter: { _id: 1 } + ordered: true + expectResult: + deletedCount: 1 + insertedCount: 3 + insertedIds: + $$unsetOrMatches: + '0': 2 + '2': 3 + '4': 5 + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 1 + upsertedIds: { '3': 4 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 23 } + - { _id: 3, x: 333 } + - { _id: 4, x: 45 } + - { _id: 5, x: 55 } + - + description: 'Both commands are retried after their first statement fails' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + updateOne: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + expectResult: + deletedCount: 0 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '0': 2 } } + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 23 } + - + description: 'Second command is retried after its second statement fails' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { skip: 2 } + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + updateOne: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + expectResult: + deletedCount: 0 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '0': 2 } } + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 23 } + - + description: 'BulkWrite with unordered execution' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + insertOne: + document: { _id: 3, x: 33 } + ordered: false + expectResult: + deletedCount: 0 + insertedCount: 2 + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'First insertOne is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + - + deleteOne: + filter: { _id: 1 } + ordered: true + expectError: + isError: true + expectResult: + deletedCount: 0 + insertedCount: 0 + insertedIds: + $$unsetOrMatches: { } + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'Second updateOne is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { skip: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + - + deleteOne: + filter: { _id: 1 } + ordered: true + expectError: + isError: true + expectResult: + deletedCount: 0 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '0': 2 } } + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'Third updateOne is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { skip: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + updateOne: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + - + insertOne: + document: { _id: 2, x: 22 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + expectError: + isError: true + expectResult: + deletedCount: 0 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '1': 2 } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + # The onPrimaryTransactionalWrite fail point only triggers for write + # operations that include a transaction ID. Therefore, it will not affect + # the initial deleteMany and will trigger once (and only once) for the first + # insertOne attempt. + description: 'Single-document write following deleteMany is retried' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteMany: + filter: { x: 11 } + - + insertOne: + document: { _id: 2, x: 22 } + ordered: true + expectResult: + deletedCount: 1 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '1': 2 } } + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + # The onPrimaryTransactionalWrite fail point only triggers for write + # operations that include a transaction ID. Therefore, it will not affect + # the initial updateMany and will trigger once (and only once) for the first + # insertOne attempt. + description: 'Single-document write following updateMany is retried' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + updateMany: + filter: { x: 11 } + update: { $inc: { x: 1 } } + - + insertOne: + document: { _id: 2, x: 22 } + ordered: true + expectResult: + deletedCount: 0 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '1': 2 } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/client-bulkWrite-clientErrors.json b/test/spec/retryable-writes/tests/unified/client-bulkWrite-clientErrors.json new file mode 100644 index 00000000000..e2c0fb9c0a5 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/client-bulkWrite-clientErrors.json @@ -0,0 +1,350 @@ +{ + "description": "client bulkWrite retryable writes with client errors", + "schemaVersion": "1.21", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ], + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "retryable-writes-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite with one network error succeeds after retry", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-tests.coll0", + "document": { + "_id": 4, + "x": 44 + } + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 4 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "client bulkWrite with two network errors fails after retry", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-tests.coll0", + "document": { + "_id": 4, + "x": 44 + } + } + } + ], + "verboseResults": true + }, + "expectError": { + "isClientError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/client-bulkWrite-clientErrors.yml b/test/spec/retryable-writes/tests/unified/client-bulkWrite-clientErrors.yml new file mode 100644 index 00000000000..85696e89db2 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/client-bulkWrite-clientErrors.yml @@ -0,0 +1,172 @@ +description: "client bulkWrite retryable writes with client errors" +schemaVersion: "1.21" +runOnRequirements: + - minServerVersion: "8.0" + topologies: + - replicaset + - sharded + - load-balanced + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name retryable-writes-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +_yamlAnchors: + namespace: &namespace "retryable-writes-tests.coll0" + +tests: + - description: "client bulkWrite with one network error succeeds after retry" + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: [ bulkWrite ] + closeConnection: true + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 4, x: 44 } + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 4 + updateResults: {} + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + nsInfo: + - ns: *namespace + # An implicit session is included with the transaction number: + lsid: { "$$exists": true } + txnNumber: { "$$exists": true } + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + nsInfo: + - ns: *namespace + # An implicit session is included with the transaction number: + lsid: { "$$exists": true } + txnNumber: { "$$exists": true } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - description: "client bulkWrite with two network errors fails after retry" + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: [ bulkWrite ] + closeConnection: true + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 4, x: 44 } + verboseResults: true + expectError: + isClientError: true + errorLabelsContain: ["RetryableWriteError"] # Error label added by driver. + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + nsInfo: + - ns: *namespace + # An implicit session is included with the transaction number: + lsid: { "$$exists": true } + txnNumber: { "$$exists": true } + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + nsInfo: + - ns: *namespace + # An implicit session is included with the transaction number: + lsid: { "$$exists": true } + txnNumber: { "$$exists": true } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/test/spec/retryable-writes/tests/unified/client-bulkWrite-serverErrors.json b/test/spec/retryable-writes/tests/unified/client-bulkWrite-serverErrors.json new file mode 100644 index 00000000000..4a0b210eb59 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/client-bulkWrite-serverErrors.json @@ -0,0 +1,872 @@ +{ + "description": "client bulkWrite retryable writes", + "schemaVersion": "1.21", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ], + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "clientRetryWritesFalse", + "uriOptions": { + "retryWrites": false + }, + "observeEvents": [ + "commandStartedEvent" + ], + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "retryable-writes-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite with no multi: true operations succeeds after retryable top-level error", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-tests.coll0", + "document": { + "_id": 4, + "x": 44 + } + } + }, + { + "updateOne": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "replaceOne": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 2 + }, + "replacement": { + "x": 222 + } + } + }, + { + "deleteOne": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 3 + } + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 2, + "modifiedCount": 2, + "deletedCount": 1, + "insertResults": { + "0": { + "insertedId": 4 + } + }, + "updateResults": { + "1": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + }, + "2": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + } + }, + "deleteResults": { + "3": { + "deletedCount": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + }, + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": false + }, + { + "update": 0, + "filter": { + "_id": 2 + }, + "updateMods": { + "x": 222 + }, + "multi": false + }, + { + "delete": 0, + "filter": { + "_id": 3 + }, + "multi": false + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + }, + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": false + }, + { + "update": 0, + "filter": { + "_id": 2 + }, + "updateMods": { + "x": 222 + }, + "multi": false + }, + { + "delete": 0, + "filter": { + "_id": 3 + }, + "multi": false + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 222 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "client bulkWrite with multi: true operations fails after retryable top-level error", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateMany": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteMany": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 3 + } + } + } + ] + }, + "expectError": { + "errorCode": 189, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": true, + "ordered": true, + "ops": [ + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": true + }, + { + "delete": 0, + "filter": { + "_id": 3 + }, + "multi": true + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ] + } + } + } + ] + } + ] + }, + { + "description": "client bulkWrite with no multi: true operations succeeds after retryable writeConcernError", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-tests.coll0", + "document": { + "_id": 4, + "x": 44 + } + } + }, + { + "updateOne": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "replaceOne": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 2 + }, + "replacement": { + "x": 222 + } + } + }, + { + "deleteOne": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 3 + } + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 2, + "modifiedCount": 2, + "deletedCount": 1, + "insertResults": { + "0": { + "insertedId": 4 + } + }, + "updateResults": { + "1": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + }, + "2": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + } + }, + "deleteResults": { + "3": { + "deletedCount": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + }, + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": false + }, + { + "update": 0, + "filter": { + "_id": 2 + }, + "updateMods": { + "x": 222 + }, + "multi": false + }, + { + "delete": 0, + "filter": { + "_id": 3 + }, + "multi": false + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + }, + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": false + }, + { + "update": 0, + "filter": { + "_id": 2 + }, + "updateMods": { + "x": 222 + }, + "multi": false + }, + { + "delete": 0, + "filter": { + "_id": 3 + }, + "multi": false + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "client bulkWrite with multi: true operations fails after retryable writeConcernError", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateMany": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteMany": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 3 + } + } + } + ] + }, + "expectError": { + "writeConcernErrors": [ + { + "code": 91, + "message": "Replication is being shut down" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": true, + "ordered": true, + "ops": [ + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": true + }, + { + "delete": 0, + "filter": { + "_id": 3 + }, + "multi": true + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ] + } + } + } + ] + } + ] + }, + { + "description": "client bulkWrite with retryWrites: false does not retry", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "clientRetryWritesFalse", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "clientRetryWritesFalse", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-tests.coll0", + "document": { + "_id": 4, + "x": 44 + } + } + } + ] + }, + "expectError": { + "errorCode": 189, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "expectEvents": [ + { + "client": "clientRetryWritesFalse", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": true, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/client-bulkWrite-serverErrors.yml b/test/spec/retryable-writes/tests/unified/client-bulkWrite-serverErrors.yml new file mode 100644 index 00000000000..23d2c622ee1 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/client-bulkWrite-serverErrors.yml @@ -0,0 +1,412 @@ +description: "client bulkWrite retryable writes" +schemaVersion: "1.21" +runOnRequirements: + - minServerVersion: "8.0" + topologies: + - replicaset + - sharded + - load-balanced + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + useMultipleMongoses: false + - client: + id: &clientRetryWritesFalse clientRetryWritesFalse + uriOptions: + retryWrites: false + observeEvents: [ commandStartedEvent ] + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name retryable-writes-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +_yamlAnchors: + namespace: &namespace "retryable-writes-tests.coll0" + +tests: + - description: "client bulkWrite with no multi: true operations succeeds after retryable top-level error" + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: [ bulkWrite ] + errorCode: 189 # PrimarySteppedDown + errorLabels: [ RetryableWriteError ] + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 4, x: 44 } + - updateOne: + namespace: *namespace + filter: { _id: 1 } + update: + $inc: { x: 1 } + - replaceOne: + namespace: *namespace + filter: { _id: 2 } + replacement: { x: 222 } + - deleteOne: + namespace: *namespace + filter: { _id: 3 } + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 2 + modifiedCount: 2 + deletedCount: 1 + insertResults: + 0: + insertedId: 4 + updateResults: + 1: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + 2: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + deleteResults: + 3: + deletedCount: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + - update: 0 + filter: { _id: 1 } + updateMods: + $inc: { x: 1 } + multi: false + - update: 0 + filter: { _id: 2 } + updateMods: { x: 222 } + multi: false + - delete: 0 + filter: { _id: 3 } + multi: false + nsInfo: + - ns: *namespace + lsid: { $$exists: true } + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + - update: 0 + filter: { _id: 1 } + updateMods: + $inc: { x: 1 } + multi: false + - update: 0 + filter: { _id: 2 } + updateMods: { x: 222 } + multi: false + - delete: 0 + filter: { _id: 3 } + multi: false + nsInfo: + - ns: *namespace + lsid: { $$exists: true } + txnNumber: { $$exists: true } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 222 } + - { _id: 4, x: 44 } + - description: "client bulkWrite with multi: true operations fails after retryable top-level error" + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: [ bulkWrite ] + errorCode: 189 # PrimarySteppedDown + errorLabels: [ RetryableWriteError ] + - object: *client0 + name: clientBulkWrite + arguments: + models: + - updateMany: + namespace: *namespace + filter: { _id: 1 } + update: + $inc: { x: 1 } + - deleteMany: + namespace: *namespace + filter: { _id: 3 } + expectError: + errorCode: 189 + errorLabelsContain: [ RetryableWriteError ] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: true + ordered: true + ops: + - update: 0 + filter: { _id: 1 } + updateMods: + $inc: { x: 1 } + multi: true + - delete: 0 + filter: { _id: 3 } + multi: true + nsInfo: + - ns: *namespace + - description: "client bulkWrite with no multi: true operations succeeds after retryable writeConcernError" + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: [ bulkWrite ] + errorLabels: [ RetryableWriteError ] + writeConcernError: + code: 91 + errmsg: "Replication is being shut down" + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 4, x: 44 } + - updateOne: + namespace: *namespace + filter: { _id: 1 } + update: + $inc: { x: 1 } + - replaceOne: + namespace: *namespace + filter: { _id: 2 } + replacement: { x: 222 } + - deleteOne: + namespace: *namespace + filter: { _id: 3 } + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 2 + modifiedCount: 2 + deletedCount: 1 + insertResults: + 0: + insertedId: 4 + updateResults: + 1: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + 2: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + deleteResults: + 3: + deletedCount: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + - update: 0 + filter: { _id: 1 } + updateMods: + $inc: { x: 1 } + multi: false + - update: 0 + filter: { _id: 2 } + updateMods: { x: 222 } + multi: false + - delete: 0 + filter: { _id: 3 } + multi: false + nsInfo: + - ns: *namespace + lsid: { $$exists: true } + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + - update: 0 + filter: { _id: 1 } + updateMods: + $inc: { x: 1 } + multi: false + - update: 0 + filter: { _id: 2 } + updateMods: { x: 222 } + multi: false + - delete: 0 + filter: { _id: 3 } + multi: false + nsInfo: + - ns: *namespace + lsid: { $$exists: true } + txnNumber: { $$exists: true } + - description: "client bulkWrite with multi: true operations fails after retryable writeConcernError" + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: [ bulkWrite ] + errorLabels: [ RetryableWriteError ] + writeConcernError: + code: 91 + errmsg: "Replication is being shut down" + - object: *client0 + name: clientBulkWrite + arguments: + models: + - updateMany: + namespace: *namespace + filter: { _id: 1 } + update: + $inc: { x: 1 } + - deleteMany: + namespace: *namespace + filter: { _id: 3 } + expectError: + writeConcernErrors: + - code: 91 + message: "Replication is being shut down" + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: true + ordered: true + ops: + - update: 0 + filter: { _id: 1 } + updateMods: + $inc: { x: 1 } + multi: true + - delete: 0 + filter: { _id: 3 } + multi: true + nsInfo: + - ns: *namespace + - description: "client bulkWrite with retryWrites: false does not retry" + operations: + - object: testRunner + name: failPoint + arguments: + client: *clientRetryWritesFalse + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: [ bulkWrite ] + errorCode: 189 # PrimarySteppedDown + errorLabels: [ RetryableWriteError ] + - object: *clientRetryWritesFalse + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 4, x: 44 } + expectError: + errorCode: 189 + errorLabelsContain: [ RetryableWriteError ] + expectEvents: + - client: *clientRetryWritesFalse + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: true + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + nsInfo: + - ns: *namespace diff --git a/test/spec/retryable-writes/tests/unified/deleteMany.json b/test/spec/retryable-writes/tests/unified/deleteMany.json new file mode 100644 index 00000000000..087576cc0f6 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/deleteMany.json @@ -0,0 +1,76 @@ +{ + "description": "deleteMany", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "DeleteMany ignores retryWrites", + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": {} + }, + "expectResult": { + "deletedCount": 2 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/deleteMany.yml b/test/spec/retryable-writes/tests/unified/deleteMany.yml new file mode 100644 index 00000000000..31a6e07864e --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/deleteMany.yml @@ -0,0 +1,49 @@ +description: deleteMany + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: true + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'DeleteMany ignores retryWrites' + operations: + - + object: *collection0 + name: deleteMany + arguments: + filter: { } + expectResult: + deletedCount: 2 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] diff --git a/test/spec/retryable-writes/tests/unified/deleteOne-errorLabels.json b/test/spec/retryable-writes/tests/unified/deleteOne-errorLabels.json new file mode 100644 index 00000000000..88920862ec5 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/deleteOne-errorLabels.json @@ -0,0 +1,266 @@ +{ + "description": "deleteOne-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "DeleteOne succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "DeleteOne fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "DeleteOne succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "DeleteOne succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/deleteOne-errorLabels.yml b/test/spec/retryable-writes/tests/unified/deleteOne-errorLabels.yml new file mode 100644 index 00000000000..08e700a9bb2 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/deleteOne-errorLabels.yml @@ -0,0 +1,157 @@ +description: deleteOne-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'DeleteOne succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ delete ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + # Driver retries operation and it succeeds + expectResult: + deletedCount: 1 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: 'DeleteOne fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ delete ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'DeleteOne succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ delete ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + expectResult: + deletedCount: 1 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: 'DeleteOne succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ delete ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + expectResult: + deletedCount: 1 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/deleteOne-serverErrors.json b/test/spec/retryable-writes/tests/unified/deleteOne-serverErrors.json new file mode 100644 index 00000000000..0808b7921de --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/deleteOne-serverErrors.json @@ -0,0 +1,114 @@ +{ + "description": "deleteOne-serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "DeleteOne fails with RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "delete" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/deleteOne-serverErrors.yml b/test/spec/retryable-writes/tests/unified/deleteOne-serverErrors.yml new file mode 100644 index 00000000000..2b63c43e37c --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/deleteOne-serverErrors.yml @@ -0,0 +1,67 @@ +description: deleteOne-serverErrors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: [ replicaset ] + - + minServerVersion: 4.1.7 + topologies: [ sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'DeleteOne fails with RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ delete ] + closeConnection: true + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/deleteOne.json b/test/spec/retryable-writes/tests/unified/deleteOne.json new file mode 100644 index 00000000000..c3aaf886555 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/deleteOne.json @@ -0,0 +1,188 @@ +{ + "description": "deleteOne", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "DeleteOne is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "DeleteOne is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "DeleteOne is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/deleteOne.yml b/test/spec/retryable-writes/tests/unified/deleteOne.yml new file mode 100644 index 00000000000..ee31a33248d --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/deleteOne.yml @@ -0,0 +1,111 @@ +description: deleteOne + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'DeleteOne is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + expectResult: + deletedCount: 1 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: 'DeleteOne is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + expectResult: + deletedCount: 1 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: 'DeleteOne is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/findOneAndDelete-errorLabels.json b/test/spec/retryable-writes/tests/unified/findOneAndDelete-errorLabels.json new file mode 100644 index 00000000000..8639873fca0 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndDelete-errorLabels.json @@ -0,0 +1,289 @@ +{ + "description": "findOneAndDelete-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndDelete succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndDelete fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndDelete succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndDelete succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/findOneAndDelete-errorLabels.yml b/test/spec/retryable-writes/tests/unified/findOneAndDelete-errorLabels.yml new file mode 100644 index 00000000000..cd712f2eb32 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndDelete-errorLabels.yml @@ -0,0 +1,158 @@ +description: findOneAndDelete-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndDelete succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + # Driver retries operation and it succeeds + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: 'FindOneAndDelete fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndDelete succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: 'FindOneAndDelete succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/findOneAndDelete-serverErrors.json b/test/spec/retryable-writes/tests/unified/findOneAndDelete-serverErrors.json new file mode 100644 index 00000000000..f6d8e9d69c7 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndDelete-serverErrors.json @@ -0,0 +1,119 @@ +{ + "description": "findOneAndDelete-serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndDelete fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/findOneAndDelete-serverErrors.yml b/test/spec/retryable-writes/tests/unified/findOneAndDelete-serverErrors.yml new file mode 100644 index 00000000000..a9da496c081 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndDelete-serverErrors.yml @@ -0,0 +1,68 @@ +description: findOneAndDelete-serverErrors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: [ replicaset ] + - + minServerVersion: 4.1.7 + topologies: [ sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndDelete fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ findAndModify ] + closeConnection: true + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/findOneAndDelete.json b/test/spec/retryable-writes/tests/unified/findOneAndDelete.json new file mode 100644 index 00000000000..89dbb9d6551 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndDelete.json @@ -0,0 +1,205 @@ +{ + "description": "findOneAndDelete", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndDelete is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndDelete is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndDelete is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/findOneAndDelete.yml b/test/spec/retryable-writes/tests/unified/findOneAndDelete.yml new file mode 100644 index 00000000000..464611a0f50 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndDelete.yml @@ -0,0 +1,112 @@ +description: findOneAndDelete + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndDelete is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: 'FindOneAndDelete is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: 'FindOneAndDelete is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/findOneAndReplace-errorLabels.json b/test/spec/retryable-writes/tests/unified/findOneAndReplace-errorLabels.json new file mode 100644 index 00000000000..78db52e75da --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndReplace-errorLabels.json @@ -0,0 +1,301 @@ +{ + "description": "findOneAndReplace-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndReplace succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/findOneAndReplace-errorLabels.yml b/test/spec/retryable-writes/tests/unified/findOneAndReplace-errorLabels.yml new file mode 100644 index 00000000000..254b61980db --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndReplace-errorLabels.yml @@ -0,0 +1,165 @@ +description: findOneAndReplace-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndReplace succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + # Driver retries operation and it succeeds + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndReplace fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndReplace succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndReplace succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/findOneAndReplace-serverErrors.json b/test/spec/retryable-writes/tests/unified/findOneAndReplace-serverErrors.json new file mode 100644 index 00000000000..1c355c3ebfb --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndReplace-serverErrors.json @@ -0,0 +1,119 @@ +{ + "description": "findOneAndReplace-serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndReplace fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/findOneAndReplace-serverErrors.yml b/test/spec/retryable-writes/tests/unified/findOneAndReplace-serverErrors.yml new file mode 100644 index 00000000000..090356baddf --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndReplace-serverErrors.yml @@ -0,0 +1,69 @@ +description: findOneAndReplace-serverErrors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: [ replicaset ] + - + minServerVersion: 4.1.7 + topologies: [ sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndReplace fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ findAndModify ] + closeConnection: true + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/findOneAndReplace.json b/test/spec/retryable-writes/tests/unified/findOneAndReplace.json new file mode 100644 index 00000000000..6d1cc17974d --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndReplace.json @@ -0,0 +1,213 @@ +{ + "description": "findOneAndReplace", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndReplace is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/findOneAndReplace.yml b/test/spec/retryable-writes/tests/unified/findOneAndReplace.yml new file mode 100644 index 00000000000..7a564143bd1 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndReplace.yml @@ -0,0 +1,117 @@ +description: findOneAndReplace + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndReplace is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndReplace is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndReplace is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.json b/test/spec/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.json new file mode 100644 index 00000000000..38b3f7ba44f --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.json @@ -0,0 +1,305 @@ +{ + "description": "findOneAndUpdate-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndUpdate succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.yml b/test/spec/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.yml new file mode 100644 index 00000000000..034edbe77e7 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.yml @@ -0,0 +1,165 @@ +description: findOneAndUpdate-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndUpdate succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + # Driver retries operation and it succeeds + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndUpdate fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndUpdate succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndUpdate succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.json b/test/spec/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.json new file mode 100644 index 00000000000..150012ac724 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.json @@ -0,0 +1,120 @@ +{ + "description": "findOneAndUpdate-serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndUpdate fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.yml b/test/spec/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.yml new file mode 100644 index 00000000000..8f9765fc195 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.yml @@ -0,0 +1,69 @@ +description: findOneAndUpdate-serverErrors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: [ replicaset ] + - + minServerVersion: 4.1.7 + topologies: [ sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndUpdate fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ findAndModify ] + closeConnection: true + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/findOneAndUpdate.json b/test/spec/retryable-writes/tests/unified/findOneAndUpdate.json new file mode 100644 index 00000000000..eb88fbe9b39 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndUpdate.json @@ -0,0 +1,215 @@ +{ + "description": "findOneAndUpdate", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndUpdate is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/findOneAndUpdate.yml b/test/spec/retryable-writes/tests/unified/findOneAndUpdate.yml new file mode 100644 index 00000000000..1a67f9b0477 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/findOneAndUpdate.yml @@ -0,0 +1,116 @@ +description: findOneAndUpdate + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndUpdate is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndUpdate is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndUpdate is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/unified/handshakeError.json b/test/spec/retryable-writes/tests/unified/handshakeError.json similarity index 89% rename from test/spec/retryable-writes/unified/handshakeError.json rename to test/spec/retryable-writes/tests/unified/handshakeError.json index df37bd72322..3c464637598 100644 --- a/test/spec/retryable-writes/unified/handshakeError.json +++ b/test/spec/retryable-writes/tests/unified/handshakeError.json @@ -53,6 +53,222 @@ } ], "tests": [ + { + "description": "client.clientBulkWrite succeeds after retryable handshake network error", + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "clientBulkWrite", + "object": "client", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-handshake-tests.coll", + "document": { + "_id": 8, + "x": 88 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite" + } + }, + { + "commandSucceededEvent": { + "commandName": "bulkWrite" + } + } + ] + } + ] + }, + { + "description": "client.clientBulkWrite succeeds after retryable handshake server error (ShutdownInProgress)", + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "clientBulkWrite", + "object": "client", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-handshake-tests.coll", + "document": { + "_id": 8, + "x": 88 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite" + } + }, + { + "commandSucceededEvent": { + "commandName": "bulkWrite" + } + } + ] + } + ] + }, { "description": "collection.insertOne succeeds after retryable handshake network error", "operations": [ diff --git a/test/spec/retryable-writes/unified/handshakeError.yml b/test/spec/retryable-writes/tests/unified/handshakeError.yml similarity index 89% rename from test/spec/retryable-writes/unified/handshakeError.yml rename to test/spec/retryable-writes/tests/unified/handshakeError.yml index 9b2774bc77e..131bbf2e5c7 100644 --- a/test/spec/retryable-writes/unified/handshakeError.yml +++ b/test/spec/retryable-writes/tests/unified/handshakeError.yml @@ -50,6 +50,96 @@ tests: # - Triggers failpoint (second time). # - Tests whether operation successfully retries the handshake and succeeds. + - description: "client.clientBulkWrite succeeds after retryable handshake network error" + runOnRequirements: + - minServerVersion: "8.0" # `bulkWrite` added to server 8.0 + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: clientBulkWrite + object: *client + arguments: + models: + - insertOne: + namespace: retryable-writes-handshake-tests.coll + document: { _id: 8, x: 88 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: bulkWrite + - commandSucceededEvent: + commandName: bulkWrite + + - description: "client.clientBulkWrite succeeds after retryable handshake server error (ShutdownInProgress)" + runOnRequirements: + - minServerVersion: "8.0" # `bulkWrite` added to server 8.0 + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: clientBulkWrite + object: *client + arguments: + models: + - insertOne: + namespace: retryable-writes-handshake-tests.coll + document: { _id: 8, x: 88 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: bulkWrite + - commandSucceededEvent: + commandName: bulkWrite + - description: "collection.insertOne succeeds after retryable handshake network error" operations: - name: failPoint diff --git a/test/spec/retryable-writes/tests/unified/insertMany-errorLabels.json b/test/spec/retryable-writes/tests/unified/insertMany-errorLabels.json new file mode 100644 index 00000000000..5254ba7cb2b --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/insertMany-errorLabels.json @@ -0,0 +1,335 @@ +{ + "description": "insertMany-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "InsertMany succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": true + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertMany fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": true + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertMany succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": true + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertMany succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": true + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/insertMany-errorLabels.yml b/test/spec/retryable-writes/tests/unified/insertMany-errorLabels.yml new file mode 100644 index 00000000000..631b3a5762e --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/insertMany-errorLabels.yml @@ -0,0 +1,185 @@ +description: insertMany-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + +tests: + - + description: 'InsertMany succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: true + # Driver retries operation and it succeeds + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'InsertMany fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: true + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertMany succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: true + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'InsertMany succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: true + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/test/spec/retryable-writes/tests/unified/insertMany-serverErrors.json b/test/spec/retryable-writes/tests/unified/insertMany-serverErrors.json new file mode 100644 index 00000000000..f5f513603c6 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/insertMany-serverErrors.json @@ -0,0 +1,114 @@ +{ + "description": "insertMany-serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "InsertMany fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": true + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/insertMany-serverErrors.yml b/test/spec/retryable-writes/tests/unified/insertMany-serverErrors.yml new file mode 100644 index 00000000000..f229428bd75 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/insertMany-serverErrors.yml @@ -0,0 +1,68 @@ +description: insertMany-serverErrors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: [ replicaset ] + - + minServerVersion: 4.1.7 + topologies: [ sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + +tests: + - + description: 'InsertMany fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ insert ] + closeConnection: true + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: true + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } diff --git a/test/spec/retryable-writes/tests/unified/insertMany.json b/test/spec/retryable-writes/tests/unified/insertMany.json new file mode 100644 index 00000000000..47181d0a9ee --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/insertMany.json @@ -0,0 +1,233 @@ +{ + "description": "insertMany", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "InsertMany succeeds after one network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": true + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertMany with unordered execution", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": false + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertMany fails after multiple network errors", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": "alwaysOn", + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ], + "ordered": true + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/insertMany.yml b/test/spec/retryable-writes/tests/unified/insertMany.yml new file mode 100644 index 00000000000..0656b0c7ca1 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/insertMany.yml @@ -0,0 +1,135 @@ +description: insertMany + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + +tests: + - + description: 'InsertMany succeeds after one network error' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: true + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'InsertMany with unordered execution' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: false + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'InsertMany fails after multiple network errors' + operations: + - + # Normally, a mongod will insert the documents as a batch with a single + # commit. If this fails, mongod may try to insert each document one at a + # time depending on the failure. Therefore our single insert command may + # trigger the failpoint twice on each driver attempt. This test + # permanently enables the fail point to ensure the retry attempt always + # fails. + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: alwaysOn + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + ordered: true + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } diff --git a/test/spec/retryable-writes/tests/unified/insertOne-errorLabels.json b/test/spec/retryable-writes/tests/unified/insertOne-errorLabels.json new file mode 100644 index 00000000000..39f31a8aa66 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/insertOne-errorLabels.json @@ -0,0 +1,1127 @@ +{ + "description": "insertOne-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "InsertOne succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [] + } + ] + }, + { + "description": "InsertOne succeeds after NotWritablePrimary", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 10107, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 13436, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 13435, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11602, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after InterruptedAtShutdown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11600, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 91, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after HostNotFound", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after HostUnreachable", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 6, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after SocketException", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 9001, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after NetworkTimeout", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 89, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after ExceededTimeLimit", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 262, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after WriteConcernError InterruptedAtShutdown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 11600, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after WriteConcernError InterruptedDueToReplStateChange", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 11602, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after WriteConcernError PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 189, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne fails after multiple retryable writeConcernErrors", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/insertOne-errorLabels.yml b/test/spec/retryable-writes/tests/unified/insertOne-errorLabels.yml new file mode 100644 index 00000000000..ce524217726 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/insertOne-errorLabels.yml @@ -0,0 +1,610 @@ +description: insertOne-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'InsertOne succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + # Driver retries operation and it succeeds + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'InsertOne succeeds after NotWritablePrimary' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 10107 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after NotPrimaryOrSecondary' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 13436 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after NotPrimaryNoSecondaryOk' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 13435 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after InterruptedDueToReplStateChange' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 11602 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after InterruptedAtShutdown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 11600 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 189 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 91 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after HostNotFound' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 7 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after HostUnreachable' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 6 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after SocketException' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 9001 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after NetworkTimeout' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 89 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after ExceededTimeLimit' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 262 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after WriteConcernError InterruptedAtShutdown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 11600 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after WriteConcernError InterruptedDueToReplStateChange' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 11602 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after WriteConcernError PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 189 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne fails after multiple retryable writeConcernErrors' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ insert ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } # The write was still applied. diff --git a/test/spec/retryable-writes/unified/insertOne-noWritesPerformedError.json b/test/spec/retryable-writes/tests/unified/insertOne-noWritesPerformedError.json similarity index 100% rename from test/spec/retryable-writes/unified/insertOne-noWritesPerformedError.json rename to test/spec/retryable-writes/tests/unified/insertOne-noWritesPerformedError.json diff --git a/test/spec/retryable-writes/unified/insertOne-noWritesPerformedError.yml b/test/spec/retryable-writes/tests/unified/insertOne-noWritesPerformedError.yml similarity index 90% rename from test/spec/retryable-writes/unified/insertOne-noWritesPerformedError.yml rename to test/spec/retryable-writes/tests/unified/insertOne-noWritesPerformedError.yml index 3295d153dd3..6d8e8e7d4b8 100644 --- a/test/spec/retryable-writes/unified/insertOne-noWritesPerformedError.yml +++ b/test/spec/retryable-writes/tests/unified/insertOne-noWritesPerformedError.yml @@ -29,11 +29,9 @@ tests: client: *client0 failPoint: configureFailPoint: failCommand - mode: - times: 2 + mode: { times: 2 } data: - failCommands: - - insert + failCommands: [ insert ] errorCode: 64 errorLabels: - NoWritesPerformed @@ -41,8 +39,7 @@ tests: - name: insertOne object: *collection0 arguments: - document: - x: 1 + document: { x: 1 } expectError: errorCode: 64 errorLabelsContain: diff --git a/test/spec/retryable-writes/unified/insertOne-serverErrors.json b/test/spec/retryable-writes/tests/unified/insertOne-serverErrors.json similarity index 54% rename from test/spec/retryable-writes/unified/insertOne-serverErrors.json rename to test/spec/retryable-writes/tests/unified/insertOne-serverErrors.json index 89827fcf3f0..f404adcaf42 100644 --- a/test/spec/retryable-writes/unified/insertOne-serverErrors.json +++ b/test/spec/retryable-writes/tests/unified/insertOne-serverErrors.json @@ -1,6 +1,6 @@ { "description": "retryable-writes insertOne serverErrors", - "schemaVersion": "1.0", + "schemaVersion": "1.9", "runOnRequirements": [ { "minServerVersion": "4.0", @@ -11,7 +11,8 @@ { "minServerVersion": "4.1.7", "topologies": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -468,6 +469,397 @@ ] } ] + }, + { + "description": "InsertOne succeeds after connection failure", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertOne fails after connection failure when retryWrites option is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryWrites": false + } + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "InsertOne fails after Interrupted", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11601, + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "InsertOne fails after WriteConcernError Interrupted", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "writeConcernError": { + "code": 11601, + "errmsg": "operation was interrupted" + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertOne fails after WriteConcernError WriteConcernFailed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "writeConcernError": { + "code": 64, + "codeName": "WriteConcernFailed", + "errmsg": "waiting for replication timed out", + "errInfo": { + "wtimeout": true + } + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertOne fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] } ] } diff --git a/test/spec/retryable-writes/unified/insertOne-serverErrors.yml b/test/spec/retryable-writes/tests/unified/insertOne-serverErrors.yml similarity index 52% rename from test/spec/retryable-writes/unified/insertOne-serverErrors.yml rename to test/spec/retryable-writes/tests/unified/insertOne-serverErrors.yml index 94205d404b6..95fa71ec790 100644 --- a/test/spec/retryable-writes/unified/insertOne-serverErrors.yml +++ b/test/spec/retryable-writes/tests/unified/insertOne-serverErrors.yml @@ -1,12 +1,12 @@ description: "retryable-writes insertOne serverErrors" -schemaVersion: "1.0" +schemaVersion: "1.9" runOnRequirements: - minServerVersion: "4.0" topologies: [ replicaset ] - minServerVersion: "4.1.7" - topologies: [ sharded ] + topologies: [ sharded, load-balanced ] createEntities: - client: @@ -195,3 +195,212 @@ tests: - { _id: 2, x: 22 } # writeConcernError doesn't prevent the server from applying the write - { _id: 3, x: 33 } + - + description: 'InsertOne succeeds after connection failure' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + closeConnection: true + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } + outcome: + - + collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'InsertOne fails after connection failure when retryWrites option is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryWrites: false + - database: + id: &database1 database1 + client: *client1 + databaseName: *databaseName + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collectionName + - + name: failPoint + object: testRunner + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + closeConnection: true + - + object: *collection1 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectError: + isError: true + # If retryWrites is false, the driver should not add the + # RetryableWriteError label to the error. + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'InsertOne fails after Interrupted' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 11601 + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'InsertOne fails after WriteConcernError Interrupted' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + writeConcernError: + code: 11601 + errmsg: 'operation was interrupted' + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } # The write was still applied. + - + description: 'InsertOne fails after WriteConcernError WriteConcernFailed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + writeConcernError: + code: 64 + codeName: WriteConcernFailed + errmsg: 'waiting for replication timed out' + errInfo: + wtimeout: true + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } # The write was still applied. + - + description: 'InsertOne fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ insert ] + closeConnection: true + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/insertOne.json b/test/spec/retryable-writes/tests/unified/insertOne.json new file mode 100644 index 00000000000..61957415ed6 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/insertOne.json @@ -0,0 +1,215 @@ +{ + "description": "insertOne", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "InsertOne is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertOne is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertOne is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/insertOne.yml b/test/spec/retryable-writes/tests/unified/insertOne.yml new file mode 100644 index 00000000000..50d7766b9c9 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/insertOne.yml @@ -0,0 +1,115 @@ +description: insertOne + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'InsertOne is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'InsertOne is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'InsertOne is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/replaceOne-errorLabels.json b/test/spec/retryable-writes/tests/unified/replaceOne-errorLabels.json new file mode 100644 index 00000000000..22c4561ae7b --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/replaceOne-errorLabels.json @@ -0,0 +1,300 @@ +{ + "description": "replaceOne-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "ReplaceOne succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "ReplaceOne fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "ReplaceOne succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "ReplaceOne succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/replaceOne-errorLabels.yml b/test/spec/retryable-writes/tests/unified/replaceOne-errorLabels.yml new file mode 100644 index 00000000000..38f271d5639 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/replaceOne-errorLabels.yml @@ -0,0 +1,170 @@ +description: replaceOne-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'ReplaceOne succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + # Driver retries operation and it succeeds + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - + description: 'ReplaceOne fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'ReplaceOne succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - + description: 'ReplaceOne succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/replaceOne-serverErrors.json b/test/spec/retryable-writes/tests/unified/replaceOne-serverErrors.json new file mode 100644 index 00000000000..c957db7244a --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/replaceOne-serverErrors.json @@ -0,0 +1,118 @@ +{ + "description": "replaceOne-serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "ReplaceOne fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/replaceOne-serverErrors.yml b/test/spec/retryable-writes/tests/unified/replaceOne-serverErrors.yml new file mode 100644 index 00000000000..b6f2f65bdf9 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/replaceOne-serverErrors.yml @@ -0,0 +1,68 @@ +description: replaceOne-serverErrors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: [ replicaset ] + - + minServerVersion: 4.1.7 + topologies: [ sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'ReplaceOne fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ update ] + closeConnection: true + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/replaceOne.json b/test/spec/retryable-writes/tests/unified/replaceOne.json new file mode 100644 index 00000000000..e58625bb5e2 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/replaceOne.json @@ -0,0 +1,212 @@ +{ + "description": "replaceOne", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "ReplaceOne is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "ReplaceOne is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "ReplaceOne is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/replaceOne.yml b/test/spec/retryable-writes/tests/unified/replaceOne.yml new file mode 100644 index 00000000000..0cc92fb28fb --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/replaceOne.yml @@ -0,0 +1,120 @@ +description: replaceOne + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'ReplaceOne is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - + description: 'ReplaceOne is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - + description: 'ReplaceOne is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/updateMany.json b/test/spec/retryable-writes/tests/unified/updateMany.json new file mode 100644 index 00000000000..260b7ad1c66 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/updateMany.json @@ -0,0 +1,92 @@ +{ + "description": "updateMany", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "UpdateMany ignores retryWrites", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": {}, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 23 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/updateMany.yml b/test/spec/retryable-writes/tests/unified/updateMany.yml new file mode 100644 index 00000000000..a1843b9d2af --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/updateMany.yml @@ -0,0 +1,54 @@ +description: updateMany + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: true + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'UpdateMany ignores retryWrites' + operations: + - + object: *collection0 + name: updateMany + arguments: + filter: { } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 23 } diff --git a/test/spec/retryable-writes/tests/unified/updateOne-errorLabels.json b/test/spec/retryable-writes/tests/unified/updateOne-errorLabels.json new file mode 100644 index 00000000000..e44cef45f64 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/updateOne-errorLabels.json @@ -0,0 +1,304 @@ +{ + "description": "updateOne-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/updateOne-errorLabels.yml b/test/spec/retryable-writes/tests/unified/updateOne-errorLabels.yml new file mode 100644 index 00000000000..f530e8dba41 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/updateOne-errorLabels.yml @@ -0,0 +1,170 @@ +description: updateOne-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'UpdateOne succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + # Driver retries operation and it succeeds + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + description: 'UpdateOne fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'UpdateOne succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + description: 'UpdateOne succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/updateOne-serverErrors.json b/test/spec/retryable-writes/tests/unified/updateOne-serverErrors.json new file mode 100644 index 00000000000..648834ada4f --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/updateOne-serverErrors.json @@ -0,0 +1,119 @@ +{ + "description": "updateOne-serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/updateOne-serverErrors.yml b/test/spec/retryable-writes/tests/unified/updateOne-serverErrors.yml new file mode 100644 index 00000000000..6cd7281db17 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/updateOne-serverErrors.yml @@ -0,0 +1,71 @@ +# This file was created automatically using mongodb-spec-converter. +# Please review the generated file, then remove this notice. + +description: updateOne-serverErrors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: [ replicaset ] + - + minServerVersion: 4.1.7 + topologies: [ sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'UpdateOne fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ update ] + closeConnection: true + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/test/spec/retryable-writes/tests/unified/updateOne.json b/test/spec/retryable-writes/tests/unified/updateOne.json new file mode 100644 index 00000000000..7947cef3c09 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/updateOne.json @@ -0,0 +1,394 @@ +{ + "description": "updateOne", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne with upsert is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 3, + "x": 33 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + }, + "expectResult": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 1, + "upsertedId": 3 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 34 + } + ] + } + ] + }, + { + "description": "UpdateOne with upsert is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 3, + "x": 33 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + }, + "expectResult": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 1, + "upsertedId": 3 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 34 + } + ] + } + ] + }, + { + "description": "UpdateOne with upsert is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 3, + "x": 33 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/test/spec/retryable-writes/tests/unified/updateOne.yml b/test/spec/retryable-writes/tests/unified/updateOne.yml new file mode 100644 index 00000000000..55c42c1f4f7 --- /dev/null +++ b/test/spec/retryable-writes/tests/unified/updateOne.yml @@ -0,0 +1,213 @@ +description: updateOne + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'UpdateOne is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + description: 'UpdateOne is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + description: 'UpdateOne is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'UpdateOne with upsert is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 3, x: 33 } + update: { $inc: { x: 1 } } + upsert: true + expectResult: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 1 + upsertedId: 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 34 } + - + description: 'UpdateOne with upsert is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 3, x: 33 } + update: { $inc: { x: 1 } } + upsert: true + expectResult: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 1 + upsertedId: 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 34 } + - + description: 'UpdateOne with upsert is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 3, x: 33 } + update: { $inc: { x: 1 } } + upsert: true + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 }