diff --git a/specifications/retryable-writes/tests/legacy/README.rst b/specifications/retryable-writes/tests/legacy/README.rst deleted file mode 100644 index c7d51280d23..00000000000 --- a/specifications/retryable-writes/tests/legacy/README.rst +++ /dev/null @@ -1,388 +0,0 @@ -===================== -Retryable Write Tests -===================== - -.. contents:: - ----- - -Introduction -============ - -The YAML and JSON files in this directory tree are platform-independent tests -that drivers can use to prove their conformance to the Retryable Writes spec. - -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"]``). - -- ``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``, 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. This field has - no effect for non-sharded topologies. - - - ``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 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 Monitoring: ../../command-monitoring/command-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. - - -Changelog -========= - -: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/specifications/retryable-writes/tests/legacy/bulkWrite-errorLabels.json b/specifications/retryable-writes/tests/legacy/bulkWrite-errorLabels.json deleted file mode 100644 index 66c3ecb336a..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/bulkWrite-errorLabels.yml b/specifications/retryable-writes/tests/legacy/bulkWrite-errorLabels.yml deleted file mode 100644 index fb9d7e47e06..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/bulkWrite-serverErrors.json b/specifications/retryable-writes/tests/legacy/bulkWrite-serverErrors.json deleted file mode 100644 index 9d792ceafbb..00000000000 --- a/specifications/retryable-writes/tests/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" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down", - "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 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/specifications/retryable-writes/tests/legacy/bulkWrite-serverErrors.yml b/specifications/retryable-writes/tests/legacy/bulkWrite-serverErrors.yml deleted file mode 100644 index 0235a8a1ce8..00000000000 --- a/specifications/retryable-writes/tests/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"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 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/specifications/retryable-writes/tests/legacy/bulkWrite.json b/specifications/retryable-writes/tests/legacy/bulkWrite.json deleted file mode 100644 index 72a8d018939..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/bulkWrite.yml b/specifications/retryable-writes/tests/legacy/bulkWrite.yml deleted file mode 100644 index 939dacf7728..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/deleteMany.json b/specifications/retryable-writes/tests/legacy/deleteMany.json deleted file mode 100644 index faa21c44f1e..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/deleteMany.yml b/specifications/retryable-writes/tests/legacy/deleteMany.yml deleted file mode 100644 index 4743953fffe..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/deleteOne-errorLabels.json b/specifications/retryable-writes/tests/legacy/deleteOne-errorLabels.json deleted file mode 100644 index c14692fd1a8..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/deleteOne-errorLabels.yml b/specifications/retryable-writes/tests/legacy/deleteOne-errorLabels.yml deleted file mode 100644 index 9ee5c7426e4..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/deleteOne-serverErrors.json b/specifications/retryable-writes/tests/legacy/deleteOne-serverErrors.json deleted file mode 100644 index 4eab2fa296a..00000000000 --- a/specifications/retryable-writes/tests/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" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] - } - } - }, - "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/specifications/retryable-writes/tests/legacy/deleteOne-serverErrors.yml b/specifications/retryable-writes/tests/legacy/deleteOne-serverErrors.yml deleted file mode 100644 index 73cab927e72..00000000000 --- a/specifications/retryable-writes/tests/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"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - errorLabels: ["RetryableWriteError"] - 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/specifications/retryable-writes/tests/legacy/deleteOne.json b/specifications/retryable-writes/tests/legacy/deleteOne.json deleted file mode 100644 index 592937acedd..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/deleteOne.yml b/specifications/retryable-writes/tests/legacy/deleteOne.yml deleted file mode 100644 index b15c991cda7..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/findOneAndDelete-errorLabels.json b/specifications/retryable-writes/tests/legacy/findOneAndDelete-errorLabels.json deleted file mode 100644 index 60e6e0a7bc1..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/findOneAndDelete-errorLabels.yml b/specifications/retryable-writes/tests/legacy/findOneAndDelete-errorLabels.yml deleted file mode 100644 index 5192c5adfef..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/findOneAndDelete-serverErrors.json b/specifications/retryable-writes/tests/legacy/findOneAndDelete-serverErrors.json deleted file mode 100644 index 4c108616141..00000000000 --- a/specifications/retryable-writes/tests/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" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down", - "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 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/specifications/retryable-writes/tests/legacy/findOneAndDelete-serverErrors.yml b/specifications/retryable-writes/tests/legacy/findOneAndDelete-serverErrors.yml deleted file mode 100644 index 5926026f083..00000000000 --- a/specifications/retryable-writes/tests/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"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 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/specifications/retryable-writes/tests/legacy/findOneAndDelete.json b/specifications/retryable-writes/tests/legacy/findOneAndDelete.json deleted file mode 100644 index 0cbe18108bd..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/findOneAndDelete.yml b/specifications/retryable-writes/tests/legacy/findOneAndDelete.yml deleted file mode 100644 index 1456ad71626..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/findOneAndReplace-errorLabels.json b/specifications/retryable-writes/tests/legacy/findOneAndReplace-errorLabels.json deleted file mode 100644 index afa2f47af44..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/findOneAndReplace-errorLabels.yml b/specifications/retryable-writes/tests/legacy/findOneAndReplace-errorLabels.yml deleted file mode 100644 index 184366163f9..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/findOneAndReplace-serverErrors.json b/specifications/retryable-writes/tests/legacy/findOneAndReplace-serverErrors.json deleted file mode 100644 index 64c69e2f6d7..00000000000 --- a/specifications/retryable-writes/tests/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" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down", - "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 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/specifications/retryable-writes/tests/legacy/findOneAndReplace-serverErrors.yml b/specifications/retryable-writes/tests/legacy/findOneAndReplace-serverErrors.yml deleted file mode 100644 index 5a483e60b6d..00000000000 --- a/specifications/retryable-writes/tests/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"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 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/specifications/retryable-writes/tests/legacy/findOneAndReplace.json b/specifications/retryable-writes/tests/legacy/findOneAndReplace.json deleted file mode 100644 index e1f9ab7f8c3..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/findOneAndReplace.yml b/specifications/retryable-writes/tests/legacy/findOneAndReplace.yml deleted file mode 100644 index 36d81d461ed..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/findOneAndUpdate-errorLabels.json b/specifications/retryable-writes/tests/legacy/findOneAndUpdate-errorLabels.json deleted file mode 100644 index 19b3a9e771c..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/findOneAndUpdate-errorLabels.yml b/specifications/retryable-writes/tests/legacy/findOneAndUpdate-errorLabels.yml deleted file mode 100644 index 03751d568ba..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/findOneAndUpdate-serverErrors.json b/specifications/retryable-writes/tests/legacy/findOneAndUpdate-serverErrors.json deleted file mode 100644 index 9f546049924..00000000000 --- a/specifications/retryable-writes/tests/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" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down", - "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 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/specifications/retryable-writes/tests/legacy/findOneAndUpdate-serverErrors.yml b/specifications/retryable-writes/tests/legacy/findOneAndUpdate-serverErrors.yml deleted file mode 100644 index 3c1fbe82269..00000000000 --- a/specifications/retryable-writes/tests/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"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 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/specifications/retryable-writes/tests/legacy/findOneAndUpdate.json b/specifications/retryable-writes/tests/legacy/findOneAndUpdate.json deleted file mode 100644 index 9ae2d87d821..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/findOneAndUpdate.yml b/specifications/retryable-writes/tests/legacy/findOneAndUpdate.yml deleted file mode 100644 index 9235526be75..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/insertMany-errorLabels.json b/specifications/retryable-writes/tests/legacy/insertMany-errorLabels.json deleted file mode 100644 index 65fd377fa60..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/insertMany-errorLabels.yml b/specifications/retryable-writes/tests/legacy/insertMany-errorLabels.yml deleted file mode 100644 index 9f5e1636235..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/insertMany-serverErrors.json b/specifications/retryable-writes/tests/legacy/insertMany-serverErrors.json deleted file mode 100644 index 7b45b506c93..00000000000 --- a/specifications/retryable-writes/tests/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" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down", - "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 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/specifications/retryable-writes/tests/legacy/insertMany-serverErrors.yml b/specifications/retryable-writes/tests/legacy/insertMany-serverErrors.yml deleted file mode 100644 index 140329fcb8d..00000000000 --- a/specifications/retryable-writes/tests/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"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 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/specifications/retryable-writes/tests/legacy/insertMany.json b/specifications/retryable-writes/tests/legacy/insertMany.json deleted file mode 100644 index 0ad326e2dc9..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/insertMany.yml b/specifications/retryable-writes/tests/legacy/insertMany.yml deleted file mode 100644 index eed450e0a3e..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/insertOne-errorLabels.json b/specifications/retryable-writes/tests/legacy/insertOne-errorLabels.json deleted file mode 100644 index d90ac5dfbd8..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/insertOne-errorLabels.yml b/specifications/retryable-writes/tests/legacy/insertOne-errorLabels.yml deleted file mode 100644 index 87100aa5cf3..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/insertOne-serverErrors.json b/specifications/retryable-writes/tests/legacy/insertOne-serverErrors.json deleted file mode 100644 index e8571f8cf9a..00000000000 --- a/specifications/retryable-writes/tests/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" - ], - "writeConcernError": { - "code": 11600, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] - } - } - }, - "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" - ], - "writeConcernError": { - "code": 11602, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] - } - } - }, - "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" - ], - "writeConcernError": { - "code": 189, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] - } - } - }, - "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" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] - } - } - }, - "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" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] - } - } - }, - "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/specifications/retryable-writes/tests/legacy/insertOne-serverErrors.yml b/specifications/retryable-writes/tests/legacy/insertOne-serverErrors.yml deleted file mode 100644 index af50e761ee7..00000000000 --- a/specifications/retryable-writes/tests/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"] - writeConcernError: - code: 11600 - errmsg: Replication is being shut down - errorLabels: ["RetryableWriteError"] - 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"] - writeConcernError: - code: 11602 - errmsg: Replication is being shut down - errorLabels: ["RetryableWriteError"] - 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"] - writeConcernError: - code: 189 - errmsg: Replication is being shut down - errorLabels: ["RetryableWriteError"] - 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"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - errorLabels: ["RetryableWriteError"] - 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"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - errorLabels: ["RetryableWriteError"] - 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/specifications/retryable-writes/tests/legacy/insertOne.json b/specifications/retryable-writes/tests/legacy/insertOne.json deleted file mode 100644 index 04dee6dd68a..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/insertOne.yml b/specifications/retryable-writes/tests/legacy/insertOne.yml deleted file mode 100644 index ebfdf23e662..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/replaceOne-errorLabels.json b/specifications/retryable-writes/tests/legacy/replaceOne-errorLabels.json deleted file mode 100644 index 6029b875dcf..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/replaceOne-errorLabels.yml b/specifications/retryable-writes/tests/legacy/replaceOne-errorLabels.yml deleted file mode 100644 index 41939092934..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/replaceOne-serverErrors.json b/specifications/retryable-writes/tests/legacy/replaceOne-serverErrors.json deleted file mode 100644 index 7457228cd75..00000000000 --- a/specifications/retryable-writes/tests/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" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down", - "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 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/specifications/retryable-writes/tests/legacy/replaceOne-serverErrors.yml b/specifications/retryable-writes/tests/legacy/replaceOne-serverErrors.yml deleted file mode 100644 index 292f96ecb0e..00000000000 --- a/specifications/retryable-writes/tests/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"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 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/specifications/retryable-writes/tests/legacy/replaceOne.json b/specifications/retryable-writes/tests/legacy/replaceOne.json deleted file mode 100644 index e5b8cf8eabb..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/replaceOne.yml b/specifications/retryable-writes/tests/legacy/replaceOne.yml deleted file mode 100644 index 0000904a401..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/updateMany.json b/specifications/retryable-writes/tests/legacy/updateMany.json deleted file mode 100644 index 46fef73e742..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/updateMany.yml b/specifications/retryable-writes/tests/legacy/updateMany.yml deleted file mode 100644 index f3ab39faa32..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/updateOne-errorLabels.json b/specifications/retryable-writes/tests/legacy/updateOne-errorLabels.json deleted file mode 100644 index 5bd00cde904..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/updateOne-errorLabels.yml b/specifications/retryable-writes/tests/legacy/updateOne-errorLabels.yml deleted file mode 100644 index 6bfef3b1299..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/updateOne-serverErrors.json b/specifications/retryable-writes/tests/legacy/updateOne-serverErrors.json deleted file mode 100644 index 11601980199..00000000000 --- a/specifications/retryable-writes/tests/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" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down", - "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 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/specifications/retryable-writes/tests/legacy/updateOne-serverErrors.yml b/specifications/retryable-writes/tests/legacy/updateOne-serverErrors.yml deleted file mode 100644 index 35ed406a540..00000000000 --- a/specifications/retryable-writes/tests/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"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - 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 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/specifications/retryable-writes/tests/legacy/updateOne.json b/specifications/retryable-writes/tests/legacy/updateOne.json deleted file mode 100644 index 0f806dc3d84..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/legacy/updateOne.yml b/specifications/retryable-writes/tests/legacy/updateOne.yml deleted file mode 100644 index 56b7d822b4f..00000000000 --- a/specifications/retryable-writes/tests/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/specifications/retryable-writes/tests/unified/aggregate-out-merge.json b/specifications/retryable-writes/tests/unified/aggregate-out-merge.json new file mode 100644 index 00000000000..fd25c345ac3 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/aggregate-out-merge.json @@ -0,0 +1,149 @@ +{ + "description": "aggregate with $out/$merge does not set txnNumber", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "mergeCollection", + "databaseName": "retryable-writes-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "aggregate with $out does not set txnNumber", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "outCollection" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "aggregate with $merge does not set txnNumber", + "runOnRequirements": [ + { + "minServerVersion": "4.1.11" + } + ], + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$merge": { + "into": "mergeCollection" + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/specifications/retryable-writes/tests/unified/aggregate-out-merge.yml b/specifications/retryable-writes/tests/unified/aggregate-out-merge.yml new file mode 100644 index 00000000000..5114b31633d --- /dev/null +++ b/specifications/retryable-writes/tests/unified/aggregate-out-merge.yml @@ -0,0 +1,67 @@ +description: "aggregate with $out/$merge does not set txnNumber" + +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "3.6" + topologies: + - replicaset + - sharded + - load-balanced + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name retryable-writes-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + # The output collection must already exist for $merge on a sharded cluster + - collectionName: &mergeCollection mergeCollection + databaseName: *database0Name + documents: [] + +tests: + - description: "aggregate with $out does not set txnNumber" + runOnRequirements: + - serverless: forbid # $out is not supported on serverless + operations: + - object: *collection0 + name: aggregate + arguments: + pipeline: + - { $sort: { x: 1 } } + - { $match: { _id: { $gt: 1 } } } + - { $out: outCollection } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: aggregate + command: + txnNumber: { $$exists: false } + - description: "aggregate with $merge does not set txnNumber" + runOnRequirements: + - minServerVersion: "4.1.11" + operations: + - object: *collection0 + name: aggregate + arguments: + pipeline: + - { $sort: { x: 1 } } + - { $match: { _id: { $gt: 1 } } } + - { $merge: { into: *mergeCollection } } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: aggregate + command: + txnNumber: { $$exists: false } diff --git a/specifications/retryable-writes/tests/unified/bulkWrite-errorLabels.json b/specifications/retryable-writes/tests/unified/bulkWrite-errorLabels.json new file mode 100644 index 00000000000..13ba9bae757 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/bulkWrite-errorLabels.yml b/specifications/retryable-writes/tests/unified/bulkWrite-errorLabels.yml new file mode 100644 index 00000000000..9adec6de718 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.json b/specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.json index 23cf2869a6f..0a063ab4d99 100644 --- a/specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.json +++ b/specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.json @@ -1,12 +1,19 @@ { "description": "retryable-writes bulkWrite serverErrors", - "schemaVersion": "1.0", + "schemaVersion": "1.3", "runOnRequirements": [ { - "minServerVersion": "3.6", + "minServerVersion": "4.0", "topologies": [ "replicaset" ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] } ], "createEntities": [ @@ -55,16 +62,7 @@ "description": "BulkWrite succeeds after retryable writeConcernError in first batch", "runOnRequirements": [ { - "minServerVersion": "4.0", - "topologies": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topologies": [ - "sharded-replicaset" - ] + "minServerVersion": "4.3.1" } ], "operations": [ @@ -200,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/specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.yml b/specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.yml index cb67304c748..a88a2061231 100644 --- a/specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.yml +++ b/specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.yml @@ -1,10 +1,12 @@ description: "retryable-writes bulkWrite serverErrors" -schemaVersion: "1.0" +schemaVersion: "1.3" runOnRequirements: - - minServerVersion: "3.6" + - minServerVersion: "4.0" topologies: [ replicaset ] + - minServerVersion: "4.1.7" + topologies: [ sharded, load-balanced ] createEntities: - client: @@ -30,10 +32,7 @@ initialData: tests: - description: "BulkWrite succeeds after retryable writeConcernError in first batch" runOnRequirements: - - minServerVersion: "4.0" - topologies: [ replicaset ] - - minServerVersion: "4.1.7" - topologies: [ sharded-replicaset ] + - minServerVersion: "4.3.1" # failCommand errorLabels option operations: - name: failPoint object: testRunner @@ -94,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/specifications/retryable-writes/tests/unified/bulkWrite.json b/specifications/retryable-writes/tests/unified/bulkWrite.json new file mode 100644 index 00000000000..f2bd9e0eb85 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/bulkWrite.json @@ -0,0 +1,1083 @@ +{ + "description": "bulkWrite", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "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 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "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 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "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 + } + ] + } + ] + }, + { + "description": "collection bulkWrite with updateMany does not set txnNumber", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "collection bulkWrite with deleteMany does not set txnNumber", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteMany": { + "filter": {} + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/specifications/retryable-writes/tests/unified/bulkWrite.yml b/specifications/retryable-writes/tests/unified/bulkWrite.yml new file mode 100644 index 00000000000..c92fd4b74ab --- /dev/null +++ b/specifications/retryable-writes/tests/unified/bulkWrite.yml @@ -0,0 +1,559 @@ +description: bulkWrite + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + 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 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: update + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: delete + command: + txnNumber: { $$exists: true } + - + # 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 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - + 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 } + - description: "collection bulkWrite with updateMany does not set txnNumber" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - + updateMany: + filter: {} + update: { $set: { x: 1 } } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: update + command: + txnNumber: { $$exists: false } + - description: "collection bulkWrite with deleteMany does not set txnNumber" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteMany: + filter: {} + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: delete + command: + txnNumber: { $$exists: false } \ No newline at end of file diff --git a/specifications/retryable-writes/tests/unified/client-bulkWrite-clientErrors.json b/specifications/retryable-writes/tests/unified/client-bulkWrite-clientErrors.json index 0107f70022a..d16e0c9c8d6 100644 --- a/specifications/retryable-writes/tests/unified/client-bulkWrite-clientErrors.json +++ b/specifications/retryable-writes/tests/unified/client-bulkWrite-clientErrors.json @@ -1,4 +1,4 @@ -{ +{ "description": "client bulkWrite retryable writes with client errors", "schemaVersion": "1.21", "runOnRequirements": [ diff --git a/specifications/retryable-writes/tests/unified/client-bulkWrite-serverErrors.json b/specifications/retryable-writes/tests/unified/client-bulkWrite-serverErrors.json index 2b439aa3613..a1f7c8152a8 100644 --- a/specifications/retryable-writes/tests/unified/client-bulkWrite-serverErrors.json +++ b/specifications/retryable-writes/tests/unified/client-bulkWrite-serverErrors.json @@ -1,4 +1,4 @@ -{ +{ "description": "client bulkWrite retryable writes", "schemaVersion": "1.21", "runOnRequirements": [ @@ -428,7 +428,10 @@ { "ns": "retryable-writes-tests.coll0" } - ] + ], + "txnNumber": { + "$$exists": false + } } } } @@ -779,7 +782,10 @@ { "ns": "retryable-writes-tests.coll0" } - ] + ], + "txnNumber": { + "$$exists": false + } } } } @@ -861,7 +867,10 @@ { "ns": "retryable-writes-tests.coll0" } - ] + ], + "txnNumber": { + "$$exists": false + } } } } diff --git a/specifications/retryable-writes/tests/unified/client-bulkWrite-serverErrors.yml b/specifications/retryable-writes/tests/unified/client-bulkWrite-serverErrors.yml index 722e5cc8e02..95106823174 100644 --- a/specifications/retryable-writes/tests/unified/client-bulkWrite-serverErrors.yml +++ b/specifications/retryable-writes/tests/unified/client-bulkWrite-serverErrors.yml @@ -207,6 +207,7 @@ tests: multi: true nsInfo: - ns: *namespace + txnNumber: { $$exists: false } - description: "client bulkWrite with no multi: true operations succeeds after retryable writeConcernError" operations: - object: testRunner @@ -372,6 +373,7 @@ tests: multi: true nsInfo: - ns: *namespace + txnNumber: { $$exists: false } - description: "client bulkWrite with retryWrites: false does not retry" operations: - object: testRunner @@ -411,3 +413,4 @@ tests: document: { _id: 4, x: 44 } nsInfo: - ns: *namespace + txnNumber: { $$exists: false } diff --git a/specifications/retryable-writes/tests/unified/deleteMany.json b/specifications/retryable-writes/tests/unified/deleteMany.json new file mode 100644 index 00000000000..381f3779541 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/deleteMany.json @@ -0,0 +1,96 @@ +{ + "description": "deleteMany", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "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": [] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/specifications/retryable-writes/tests/unified/deleteMany.yml b/specifications/retryable-writes/tests/unified/deleteMany.yml new file mode 100644 index 00000000000..a05fe807694 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/deleteMany.yml @@ -0,0 +1,57 @@ +description: deleteMany + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: [ commandStartedEvent ] + - + 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: [] + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: delete + command: + txnNumber: { $$exists: false } diff --git a/specifications/retryable-writes/tests/unified/deleteOne-errorLabels.json b/specifications/retryable-writes/tests/unified/deleteOne-errorLabels.json new file mode 100644 index 00000000000..88920862ec5 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/deleteOne-errorLabels.yml b/specifications/retryable-writes/tests/unified/deleteOne-errorLabels.yml new file mode 100644 index 00000000000..08e700a9bb2 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/deleteOne-serverErrors.json b/specifications/retryable-writes/tests/unified/deleteOne-serverErrors.json new file mode 100644 index 00000000000..0808b7921de --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/deleteOne-serverErrors.yml b/specifications/retryable-writes/tests/unified/deleteOne-serverErrors.yml new file mode 100644 index 00000000000..2b63c43e37c --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/deleteOne.json b/specifications/retryable-writes/tests/unified/deleteOne.json new file mode 100644 index 00000000000..9e37ff8bcf5 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/deleteOne.json @@ -0,0 +1,218 @@ +{ + "description": "deleteOne", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "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 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "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/specifications/retryable-writes/tests/unified/deleteOne.yml b/specifications/retryable-writes/tests/unified/deleteOne.yml new file mode 100644 index 00000000000..5a176f8293f --- /dev/null +++ b/specifications/retryable-writes/tests/unified/deleteOne.yml @@ -0,0 +1,123 @@ +description: deleteOne + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + 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 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: delete + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: delete + command: + txnNumber: { $$exists: true } + - + 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/specifications/retryable-writes/tests/unified/findOneAndDelete-errorLabels.json b/specifications/retryable-writes/tests/unified/findOneAndDelete-errorLabels.json new file mode 100644 index 00000000000..8639873fca0 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/findOneAndDelete-errorLabels.yml b/specifications/retryable-writes/tests/unified/findOneAndDelete-errorLabels.yml new file mode 100644 index 00000000000..cd712f2eb32 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/findOneAndDelete-serverErrors.json b/specifications/retryable-writes/tests/unified/findOneAndDelete-serverErrors.json new file mode 100644 index 00000000000..f6d8e9d69c7 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/findOneAndDelete-serverErrors.yml b/specifications/retryable-writes/tests/unified/findOneAndDelete-serverErrors.yml new file mode 100644 index 00000000000..a9da496c081 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/findOneAndDelete.json b/specifications/retryable-writes/tests/unified/findOneAndDelete.json new file mode 100644 index 00000000000..ebfb8ce6651 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/findOneAndDelete.json @@ -0,0 +1,235 @@ +{ + "description": "findOneAndDelete", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "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 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "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/specifications/retryable-writes/tests/unified/findOneAndDelete.yml b/specifications/retryable-writes/tests/unified/findOneAndDelete.yml new file mode 100644 index 00000000000..3788733216d --- /dev/null +++ b/specifications/retryable-writes/tests/unified/findOneAndDelete.yml @@ -0,0 +1,124 @@ +description: findOneAndDelete + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + 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 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: findAndModify + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: findAndModify + command: + txnNumber: { $$exists: true } + - + 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/specifications/retryable-writes/tests/unified/findOneAndReplace-errorLabels.json b/specifications/retryable-writes/tests/unified/findOneAndReplace-errorLabels.json new file mode 100644 index 00000000000..78db52e75da --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/findOneAndReplace-errorLabels.yml b/specifications/retryable-writes/tests/unified/findOneAndReplace-errorLabels.yml new file mode 100644 index 00000000000..254b61980db --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/findOneAndReplace-serverErrors.json b/specifications/retryable-writes/tests/unified/findOneAndReplace-serverErrors.json new file mode 100644 index 00000000000..1c355c3ebfb --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/findOneAndReplace-serverErrors.yml b/specifications/retryable-writes/tests/unified/findOneAndReplace-serverErrors.yml new file mode 100644 index 00000000000..090356baddf --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/findOneAndReplace.json b/specifications/retryable-writes/tests/unified/findOneAndReplace.json new file mode 100644 index 00000000000..638d15a41d4 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/findOneAndReplace.json @@ -0,0 +1,243 @@ +{ + "description": "findOneAndReplace", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "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 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "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/specifications/retryable-writes/tests/unified/findOneAndReplace.yml b/specifications/retryable-writes/tests/unified/findOneAndReplace.yml new file mode 100644 index 00000000000..cc2468264c4 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/findOneAndReplace.yml @@ -0,0 +1,129 @@ +description: findOneAndReplace + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + 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 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: findAndModify + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: findAndModify + command: + txnNumber: { $$exists: true } + - + 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/specifications/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.json b/specifications/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.json new file mode 100644 index 00000000000..38b3f7ba44f --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.yml b/specifications/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.yml new file mode 100644 index 00000000000..034edbe77e7 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.json b/specifications/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.json new file mode 100644 index 00000000000..150012ac724 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.yml b/specifications/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.yml new file mode 100644 index 00000000000..8f9765fc195 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/findOneAndUpdate.json b/specifications/retryable-writes/tests/unified/findOneAndUpdate.json new file mode 100644 index 00000000000..eefe98ae113 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/findOneAndUpdate.json @@ -0,0 +1,245 @@ +{ + "description": "findOneAndUpdate", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "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 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "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/specifications/retryable-writes/tests/unified/findOneAndUpdate.yml b/specifications/retryable-writes/tests/unified/findOneAndUpdate.yml new file mode 100644 index 00000000000..51c77f108c1 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/findOneAndUpdate.yml @@ -0,0 +1,128 @@ +description: findOneAndUpdate + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + 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 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: findAndModify + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: findAndModify + command: + txnNumber: { $$exists: true } + - + 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/specifications/retryable-writes/tests/unified/handshakeError.json b/specifications/retryable-writes/tests/unified/handshakeError.json index df37bd72322..93cb2e849ec 100644 --- a/specifications/retryable-writes/tests/unified/handshakeError.json +++ b/specifications/retryable-writes/tests/unified/handshakeError.json @@ -1,6 +1,6 @@ { "description": "retryable writes handshake failures", - "schemaVersion": "1.3", + "schemaVersion": "1.4", "runOnRequirements": [ { "minServerVersion": "4.2", @@ -53,6 +53,224 @@ } ], "tests": [ + { + "description": "client.clientBulkWrite succeeds after retryable handshake network error", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "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", + "serverless": "forbid" + } + ], + "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/specifications/retryable-writes/tests/unified/handshakeError.yml b/specifications/retryable-writes/tests/unified/handshakeError.yml index 9b2774bc77e..1743463370b 100644 --- a/specifications/retryable-writes/tests/unified/handshakeError.yml +++ b/specifications/retryable-writes/tests/unified/handshakeError.yml @@ -2,7 +2,7 @@ description: "retryable writes handshake failures" -schemaVersion: "1.3" +schemaVersion: "1.4" # For `serverless: forbid` runOnRequirements: - minServerVersion: "4.2" @@ -50,6 +50,98 @@ 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 + serverless: forbid + 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 + serverless: forbid + 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/specifications/retryable-writes/tests/unified/insertMany-errorLabels.json b/specifications/retryable-writes/tests/unified/insertMany-errorLabels.json new file mode 100644 index 00000000000..5254ba7cb2b --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/insertMany-errorLabels.yml b/specifications/retryable-writes/tests/unified/insertMany-errorLabels.yml new file mode 100644 index 00000000000..631b3a5762e --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/insertMany-serverErrors.json b/specifications/retryable-writes/tests/unified/insertMany-serverErrors.json new file mode 100644 index 00000000000..f5f513603c6 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/insertMany-serverErrors.yml b/specifications/retryable-writes/tests/unified/insertMany-serverErrors.yml new file mode 100644 index 00000000000..f229428bd75 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/insertMany.json b/specifications/retryable-writes/tests/unified/insertMany.json new file mode 100644 index 00000000000..35a18c46c69 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/insertMany.json @@ -0,0 +1,290 @@ +{ + "description": "insertMany", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "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 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "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 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "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/specifications/retryable-writes/tests/unified/insertMany.yml b/specifications/retryable-writes/tests/unified/insertMany.yml new file mode 100644 index 00000000000..0ead50da7d0 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/insertMany.yml @@ -0,0 +1,158 @@ +description: insertMany + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + 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 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - + 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 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - + 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/specifications/retryable-writes/tests/unified/insertOne-errorLabels.json b/specifications/retryable-writes/tests/unified/insertOne-errorLabels.json new file mode 100644 index 00000000000..39f31a8aa66 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/insertOne-errorLabels.yml b/specifications/retryable-writes/tests/unified/insertOne-errorLabels.yml new file mode 100644 index 00000000000..ce524217726 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/insertOne-noWritesPerformedError.json b/specifications/retryable-writes/tests/unified/insertOne-noWritesPerformedError.json new file mode 100644 index 00000000000..3194e91c5ca --- /dev/null +++ b/specifications/retryable-writes/tests/unified/insertOne-noWritesPerformedError.json @@ -0,0 +1,90 @@ +{ + "description": "retryable-writes insertOne noWritesPerformedErrors", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "6.0", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "no-writes-performed-collection" + } + } + ], + "tests": [ + { + "description": "InsertOne fails after NoWritesPerformed error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 64, + "errorLabels": [ + "NoWritesPerformed", + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "errorCode": 64, + "errorLabelsContain": [ + "NoWritesPerformed", + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "no-writes-performed-collection", + "databaseName": "retryable-writes-tests", + "documents": [] + } + ] + } + ] +} diff --git a/specifications/retryable-writes/tests/unified/insertOne-noWritesPerformedError.yml b/specifications/retryable-writes/tests/unified/insertOne-noWritesPerformedError.yml new file mode 100644 index 00000000000..6d8e8e7d4b8 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/insertOne-noWritesPerformedError.yml @@ -0,0 +1,51 @@ +description: "retryable-writes insertOne noWritesPerformedErrors" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "6.0" + topologies: [ replicaset ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandFailedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName retryable-writes-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName no-writes-performed-collection + +tests: + - description: "InsertOne fails after NoWritesPerformed error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ insert ] + errorCode: 64 + errorLabels: + - NoWritesPerformed + - RetryableWriteError + - name: insertOne + object: *collection0 + arguments: + document: { x: 1 } + expectError: + errorCode: 64 + errorLabelsContain: + - NoWritesPerformed + - RetryableWriteError + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] diff --git a/specifications/retryable-writes/tests/unified/insertOne.json b/specifications/retryable-writes/tests/unified/insertOne.json new file mode 100644 index 00000000000..a6afdbf224e --- /dev/null +++ b/specifications/retryable-writes/tests/unified/insertOne.json @@ -0,0 +1,245 @@ +{ + "description": "insertOne", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "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 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "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/specifications/retryable-writes/tests/unified/insertOne.yml b/specifications/retryable-writes/tests/unified/insertOne.yml new file mode 100644 index 00000000000..9b45634823f --- /dev/null +++ b/specifications/retryable-writes/tests/unified/insertOne.yml @@ -0,0 +1,127 @@ +description: insertOne + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + 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 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - + 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/specifications/retryable-writes/tests/unified/replaceOne-errorLabels.json b/specifications/retryable-writes/tests/unified/replaceOne-errorLabels.json new file mode 100644 index 00000000000..22c4561ae7b --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/replaceOne-errorLabels.yml b/specifications/retryable-writes/tests/unified/replaceOne-errorLabels.yml new file mode 100644 index 00000000000..38f271d5639 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/replaceOne-serverErrors.json b/specifications/retryable-writes/tests/unified/replaceOne-serverErrors.json new file mode 100644 index 00000000000..c957db7244a --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/replaceOne-serverErrors.yml b/specifications/retryable-writes/tests/unified/replaceOne-serverErrors.yml new file mode 100644 index 00000000000..b6f2f65bdf9 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/replaceOne.json b/specifications/retryable-writes/tests/unified/replaceOne.json new file mode 100644 index 00000000000..ee6e37d3bba --- /dev/null +++ b/specifications/retryable-writes/tests/unified/replaceOne.json @@ -0,0 +1,242 @@ +{ + "description": "replaceOne", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "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 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "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/specifications/retryable-writes/tests/unified/replaceOne.yml b/specifications/retryable-writes/tests/unified/replaceOne.yml new file mode 100644 index 00000000000..90fc559037f --- /dev/null +++ b/specifications/retryable-writes/tests/unified/replaceOne.yml @@ -0,0 +1,132 @@ +description: replaceOne + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + 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 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: update + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: update + command: + txnNumber: { $$exists: true } + - + 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/specifications/retryable-writes/tests/unified/unacknowledged-write-concern.json b/specifications/retryable-writes/tests/unified/unacknowledged-write-concern.json new file mode 100644 index 00000000000..eaa114acfd4 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/unacknowledged-write-concern.json @@ -0,0 +1,77 @@ +{ + "description": "unacknowledged write does not set txnNumber", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "tests": [ + { + "description": "unacknowledged write does not set txnNumber", + "operations": [ + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/specifications/retryable-writes/tests/unified/unacknowledged-write-concern.yml b/specifications/retryable-writes/tests/unified/unacknowledged-write-concern.yml new file mode 100644 index 00000000000..3a0cce6ae81 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/unacknowledged-write-concern.yml @@ -0,0 +1,40 @@ +description: "unacknowledged write does not set txnNumber" + +schemaVersion: "1.3" + +runOnRequirements: + - minServerVersion: "3.6" + topologies: + - replicaset + - sharded + - load-balanced + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name retryable-writes-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + +tests: + - description: "unacknowledged write does not set txnNumber" + operations: + - object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: false } diff --git a/specifications/retryable-writes/tests/unified/updateMany.json b/specifications/retryable-writes/tests/unified/updateMany.json new file mode 100644 index 00000000000..12c5204ee98 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/updateMany.json @@ -0,0 +1,112 @@ +{ + "description": "updateMany", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "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 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/specifications/retryable-writes/tests/unified/updateMany.yml b/specifications/retryable-writes/tests/unified/updateMany.yml new file mode 100644 index 00000000000..d1febec3097 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/updateMany.yml @@ -0,0 +1,62 @@ +description: updateMany + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: [ commandStartedEvent ] + - + 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 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: update + command: + txnNumber: { $$exists: false } diff --git a/specifications/retryable-writes/tests/unified/updateOne-errorLabels.json b/specifications/retryable-writes/tests/unified/updateOne-errorLabels.json new file mode 100644 index 00000000000..e44cef45f64 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/updateOne-errorLabels.yml b/specifications/retryable-writes/tests/unified/updateOne-errorLabels.yml new file mode 100644 index 00000000000..f530e8dba41 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/updateOne-serverErrors.json b/specifications/retryable-writes/tests/unified/updateOne-serverErrors.json new file mode 100644 index 00000000000..648834ada4f --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/updateOne-serverErrors.yml b/specifications/retryable-writes/tests/unified/updateOne-serverErrors.yml new file mode 100644 index 00000000000..6cd7281db17 --- /dev/null +++ b/specifications/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/specifications/retryable-writes/tests/unified/updateOne.json b/specifications/retryable-writes/tests/unified/updateOne.json new file mode 100644 index 00000000000..99ffba8e217 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/updateOne.json @@ -0,0 +1,424 @@ +{ + "description": "updateOne", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "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 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "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/specifications/retryable-writes/tests/unified/updateOne.yml b/specifications/retryable-writes/tests/unified/updateOne.yml new file mode 100644 index 00000000000..5c255b0da8b --- /dev/null +++ b/specifications/retryable-writes/tests/unified/updateOne.yml @@ -0,0 +1,225 @@ +description: updateOne + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + 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 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: update + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: update + command: + txnNumber: { $$exists: true } + - + 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 } diff --git a/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs index 06519d9dd3f..213cb4aecbc 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs @@ -27,6 +27,7 @@ using MongoDB.TestHelpers.XunitExtensions; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; namespace MongoDB.Driver.Tests.Specifications { @@ -126,7 +127,16 @@ public void LoadBalancers(JsonDrivenTestCase testCase) [Category("Serverless", "SupportLoadBalancing")] [UnifiedTestsTheory("retryable_writes.tests.unified")] - public void RetryableWrites(JsonDrivenTestCase testCase) => Run(testCase); + public void RetryableWrites(JsonDrivenTestCase testCase) + { + if (testCase.Name.Contains("bulkWrite.json") && testCase.Name.Contains("is never committed")) + { + // Unskip the tests once CSHARP-5444 is fixed. + throw new SkipException("This test is skipped because csharp driver has bug with handling connection closing while mixedBulkWrite operation."); + } + + Run(testCase); + } [Category("SDAM", "SupportLoadBalancing")] [UnifiedTestsTheory("server_discovery_and_monitoring.tests.unified")] diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/BulkWriteTest.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/BulkWriteTest.cs deleted file mode 100644 index 6f24904eb16..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/BulkWriteTest.cs +++ /dev/null @@ -1,223 +0,0 @@ -/* Copyright 2017-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using FluentAssertions; -using MongoDB.Bson; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes -{ - public class BulkWriteTest : RetryableWriteTestBase - { - // private fields - private IEnumerable> _requests; - private BulkWriteOptions _options = new BulkWriteOptions(); - private BulkWriteResult _result; - - // public methods - public override void Initialize(BsonDocument operation) - { - VerifyFields(operation, "name", "arguments"); - - foreach (var argument in operation["arguments"].AsBsonDocument) - { - switch (argument.Name) - { - case "requests": - _requests = ParseRequests(argument.Value.AsBsonArray); - break; - - case "options": - _options = ParseOptions(argument.Value.AsBsonDocument); - break; - - default: - throw new ArgumentException($"Unexpected argument: {argument.Name}."); - } - } - } - - // protected methods - protected override void ExecuteAsync(IMongoCollection collection) - { - _result = collection.BulkWriteAsync(_requests, _options).GetAwaiter().GetResult(); - } - - protected override void ExecuteSync(IMongoCollection collection) - { - _result = collection.BulkWrite(_requests, _options); - } - - protected override void VerifyResult(BsonDocument result) - { - var expectedResult = ParseResult(result, out var expectedInsertedIds); - foreach (var insertedId in expectedInsertedIds) - { - var insertModel = (InsertOneModel)_result.ProcessedRequests[insertedId.Key]; - insertModel.Document["_id"].Should().Be(insertedId.Value); - } - _result.DeletedCount.Should().Be(expectedResult.DeletedCount); - _result.InsertedCount.Should().Be(expectedResult.InsertedCount); - _result.MatchedCount.Should().Be(expectedResult.MatchedCount); - _result.ModifiedCount.Should().Be(expectedResult.ModifiedCount); - _result.Upserts.Should().Equal(expectedResult.Upserts, UpsertEquals); - } - - // private methods - private IEnumerable> ParseRequests(BsonArray requests) - { - foreach (var request in requests.Cast()) - { - yield return ParseRequest(request); - } - } - - private WriteModel ParseRequest(BsonDocument request) - { - VerifyFields(request, "name", "arguments"); - var name = request["name"].AsString; - var arguments = request["arguments"].AsBsonDocument; - - switch (name) - { - case "deleteMany": - return ParseDeleteMany(arguments); - - case "deleteOne": - return ParseDeleteOne(arguments); - - case "insertOne": - return ParseInsertOne(arguments); - - case "replaceOne": - return ParseReplaceOne(arguments); - - case "updateMany": - return ParseUpdateMany(arguments); - - case "updateOne": - return ParseUpdateOne(arguments); - - default: - throw new ArgumentException($"Unexpected request: {name}."); - } - } - - private DeleteManyModel ParseDeleteMany(BsonDocument arguments) - { - VerifyFields(arguments, "filter"); - var filter = arguments["filter"].AsBsonDocument; - return new DeleteManyModel(filter); - } - - private DeleteOneModel ParseDeleteOne(BsonDocument arguments) - { - VerifyFields(arguments, "filter"); - var filter = arguments["filter"].AsBsonDocument; - return new DeleteOneModel(filter); - } - - private InsertOneModel ParseInsertOne(BsonDocument arguments) - { - VerifyFields(arguments, "document"); - var document = arguments["document"].AsBsonDocument; - return new InsertOneModel(document); - } - - private ReplaceOneModel ParseReplaceOne(BsonDocument arguments) - { - VerifyFields(arguments, "filter", "replacement"); - var filter = arguments["filter"].AsBsonDocument; - var replacement = arguments["replacement"].AsBsonDocument; - return new ReplaceOneModel(filter, replacement); - } - - private UpdateManyModel ParseUpdateMany(BsonDocument arguments) - { - VerifyFields(arguments, "filter", "update", "upsert"); - var filter = arguments["filter"].AsBsonDocument; - var update = arguments["update"].AsBsonDocument; - var isUpsert = arguments.GetValue("upsert", false).ToBoolean(); - return new UpdateManyModel(filter, update) { IsUpsert = isUpsert }; - } - - private UpdateOneModel ParseUpdateOne(BsonDocument arguments) - { - VerifyFields(arguments, "filter", "update", "upsert"); - var filter = arguments["filter"].AsBsonDocument; - var update = arguments["update"].AsBsonDocument; - var isUpsert = arguments.GetValue("upsert", false).ToBoolean(); - return new UpdateOneModel(filter, update) { IsUpsert = isUpsert }; - } - - private BulkWriteOptions ParseOptions(BsonDocument options) - { - var result = new BulkWriteOptions(); - - foreach (var option in options) - { - switch (option.Name) - { - case "ordered": - result.IsOrdered = option.Value.ToBoolean(); - break; - - default: - throw new ArgumentException($"Unexpected option: {option.Name}."); - } - } - - return result; - } - - private BulkWriteResult ParseResult(BsonDocument result, out Dictionary expectedInsertedIds) - { - VerifyFields(result, "deletedCount", "insertedCount", "insertedIds", "matchedCount", "modifiedCount", "upsertedCount", "upsertedIds"); - - var deletedCount = result["deletedCount"].ToInt64(); - var insertedCount = result["insertedCount"].ToInt64(); - expectedInsertedIds = result["insertedIds"] - .AsBsonDocument - .ToDictionary(k => Convert.ToInt32(k.Name), v => v.Value.ToInt32()); - var matchedCount = result["matchedCount"].ToInt64(); - var modifiedCount = result["modifiedCount"].ToInt64(); - var processedRequests = new List>(_requests); - var requestCount = _requests.Count(); - var upsertedCount = result.GetValue("upsertedCount", 0).ToInt32(); - var upserts = new List(); - if (result.Contains("upsertedIds")) - { - foreach (var element in result["upsertedIds"].AsBsonDocument.Elements) - { - var index = int.Parse(element.Name, NumberFormatInfo.InvariantInfo); - var id = element.Value; - var upsert = new BulkWriteUpsert(index, id); - upserts.Add(upsert); - } - } - upserts.Count.Should().Be(upsertedCount); - - return new BulkWriteResult.Acknowledged(requestCount, matchedCount, deletedCount, insertedCount, modifiedCount, processedRequests, upserts); - } - - private bool UpsertEquals(BulkWriteUpsert x, BulkWriteUpsert y) - { - return x.Index == y.Index && x.Id.Equals(y.Id); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/DeleteManyTest.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/DeleteManyTest.cs deleted file mode 100644 index fe467fb7cd5..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/DeleteManyTest.cs +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright 2019-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using FluentAssertions; -using MongoDB.Bson; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes -{ - public class DeleteManyTest : RetryableWriteTestBase - { - // private fields - private FilterDefinition _filter; - private DeleteResult _result; - - // public methods - public override void Initialize(BsonDocument operation) - { - VerifyFields(operation, "name", "arguments"); - - foreach (var argument in operation["arguments"].AsBsonDocument) - { - switch (argument.Name) - { - case "filter": - _filter = argument.Value.AsBsonDocument; - break; - - default: - throw new ArgumentException($"Unexpected argument: {argument.Name}."); - } - } - } - - // protected methods - protected override void ExecuteAsync(IMongoCollection collection) - { - _result = collection.DeleteManyAsync(_filter).GetAwaiter().GetResult(); - } - - protected override void ExecuteSync(IMongoCollection collection) - { - _result = collection.DeleteMany(_filter); - } - - protected override void VerifyResult(BsonDocument result) - { - var expectedResult = ParseResult(result); - _result.DeletedCount.Should().Be(expectedResult.DeletedCount); - } - - // private methods - private DeleteResult ParseResult(BsonDocument result) - { - VerifyFields(result, "deletedCount"); - var deletedCount = result["deletedCount"].ToInt64(); - return new DeleteResult.Acknowledged(deletedCount); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/DeleteOneTest.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/DeleteOneTest.cs deleted file mode 100644 index 3e61ac1fb89..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/DeleteOneTest.cs +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright 2017-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using FluentAssertions; -using MongoDB.Bson; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes -{ - public class DeleteOneTest : RetryableWriteTestBase - { - // private fields - private FilterDefinition _filter; - private DeleteResult _result; - - // public methods - public override void Initialize(BsonDocument operation) - { - VerifyFields(operation, "name", "arguments"); - - foreach (var argument in operation["arguments"].AsBsonDocument) - { - switch (argument.Name) - { - case "filter": - _filter = argument.Value.AsBsonDocument; - break; - - default: - throw new ArgumentException($"Unexpected argument: {argument.Name}."); - } - } - } - - // protected methods - protected override void ExecuteAsync(IMongoCollection collection) - { - _result = collection.DeleteOneAsync(_filter).GetAwaiter().GetResult(); - } - - protected override void ExecuteSync(IMongoCollection collection) - { - _result = collection.DeleteOne(_filter); - } - - protected override void VerifyResult(BsonDocument result) - { - var expectedResult = ParseResult(result); - _result.DeletedCount.Should().Be(expectedResult.DeletedCount); - } - - // private methods - private DeleteResult ParseResult(BsonDocument result) - { - VerifyFields(result, "deletedCount"); - var deletedCount = result["deletedCount"].ToInt64(); - return new DeleteResult.Acknowledged(deletedCount); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/FindOneAndDeleteTest.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/FindOneAndDeleteTest.cs deleted file mode 100644 index 54458dab799..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/FindOneAndDeleteTest.cs +++ /dev/null @@ -1,80 +0,0 @@ -/* Copyright 2017-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using FluentAssertions; -using MongoDB.Bson; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes -{ - public class FindOneAndDeleteTest : RetryableWriteTestBase - { - // private fields - private BsonDocument _filter; - private FindOneAndDeleteOptions _options = new FindOneAndDeleteOptions(); - private BsonDocument _result; - - // public methods - public override void Initialize(BsonDocument operation) - { - VerifyFields(operation, "name", "arguments"); - - foreach (var argument in operation["arguments"].AsBsonDocument) - { - switch (argument.Name) - { - case "filter": - _filter = argument.Value.AsBsonDocument; - break; - - case "projection": - _options.Projection = argument.Value.AsBsonDocument; - break; - - case "sort": - _options.Sort = argument.Value.AsBsonDocument; - break; - - case "collation": - _options.Collation = Collation.FromBsonDocument(argument.Value.AsBsonDocument); - break; - - case "hint": - _options.Hint = argument.Value; - break; - - default: - throw new ArgumentException($"Unexpected argument: {argument.Name}."); - } - } - } - - // protected methods - protected override void ExecuteAsync(IMongoCollection collection) - { - _result = collection.FindOneAndDeleteAsync(_filter, _options).GetAwaiter().GetResult(); - } - - protected override void ExecuteSync(IMongoCollection collection) - { - _result = collection.FindOneAndDelete(_filter, _options); - } - - protected override void VerifyResult(BsonDocument result) - { - _result.Should().Be(result); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/FindOneAndReplaceTest.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/FindOneAndReplaceTest.cs deleted file mode 100644 index 2fa62698263..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/FindOneAndReplaceTest.cs +++ /dev/null @@ -1,93 +0,0 @@ -/* Copyright 2017-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using FluentAssertions; -using MongoDB.Bson; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes -{ - public class FindOneAndReplaceTest : RetryableWriteTestBase - { - // private fields - private BsonDocument _filter; - private BsonDocument _replacement; - private FindOneAndReplaceOptions _options = new FindOneAndReplaceOptions(); - private BsonDocument _result; - - // public methods - public override void Initialize(BsonDocument operation) - { - VerifyFields(operation, "name", "arguments"); - - foreach (var argument in operation["arguments"].AsBsonDocument) - { - switch (argument.Name) - { - case "filter": - _filter = argument.Value.AsBsonDocument; - break; - - case "replacement": - _replacement = argument.Value.AsBsonDocument; - break; - - case "projection": - _options.Projection = argument.Value.AsBsonDocument; - break; - - case "sort": - _options.Sort = argument.Value.AsBsonDocument; - break; - - case "upsert": - _options.IsUpsert = argument.Value.ToBoolean(); - break; - - case "returnDocument": - _options.ReturnDocument = (ReturnDocument)Enum.Parse(typeof(ReturnDocument), argument.Value.AsString); - break; - - case "collation": - _options.Collation = Collation.FromBsonDocument(argument.Value.AsBsonDocument); - break; - - case "hint": - _options.Hint = argument.Value; - break; - - default: - throw new ArgumentException($"Unexpected argument: {argument.Name}."); - } - } - } - - // protected methods - protected override void ExecuteAsync(IMongoCollection collection) - { - _result = collection.FindOneAndReplaceAsync(_filter, _replacement, _options).GetAwaiter().GetResult(); - } - - protected override void ExecuteSync(IMongoCollection collection) - { - _result = collection.FindOneAndReplace(_filter, _replacement, _options); - } - - protected override void VerifyResult(BsonDocument result) - { - _result.Should().Be(result); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/FindOneAndUpdateTest.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/FindOneAndUpdateTest.cs deleted file mode 100644 index dc352323d93..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/FindOneAndUpdateTest.cs +++ /dev/null @@ -1,104 +0,0 @@ -/* Copyright 2017-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Collections.Generic; -using FluentAssertions; -using MongoDB.Bson; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes -{ - public class FindOneAndUpdateTest : RetryableWriteTestBase - { - // private fields - private BsonDocument _filter; - private BsonDocument _update; - private FindOneAndUpdateOptions _options = new FindOneAndUpdateOptions(); - private BsonDocument _result; - - // public methods - public override void Initialize(BsonDocument operation) - { - VerifyFields(operation, "name", "arguments"); - - foreach (var argument in operation["arguments"].AsBsonDocument) - { - switch (argument.Name) - { - case "filter": - _filter = argument.Value.AsBsonDocument; - break; - - case "update": - _update = argument.Value.AsBsonDocument; - break; - - case "projection": - _options.Projection = argument.Value.AsBsonDocument; - break; - - case "sort": - _options.Sort = argument.Value.AsBsonDocument; - break; - - case "upsert": - _options.IsUpsert = argument.Value.ToBoolean(); - break; - - case "returnDocument": - _options.ReturnDocument = (ReturnDocument)Enum.Parse(typeof(ReturnDocument), argument.Value.AsString); - break; - - case "collation": - _options.Collation = Collation.FromBsonDocument(argument.Value.AsBsonDocument); - break; - - case "arrayFilters": - var arrayFilters = new List(); - foreach (var arrayFilter in argument.Value.AsBsonArray) - { - var arrayFilterDefinition = new BsonDocumentArrayFilterDefinition(arrayFilter.AsBsonDocument); - arrayFilters.Add(arrayFilterDefinition); - } - _options.ArrayFilters = arrayFilters; - break; - - case "hint": - _options.Hint = argument.Value; - break; - - default: - throw new ArgumentException($"Unexpected argument: {argument.Name}."); - } - } - } - - // protected methods - protected override void ExecuteAsync(IMongoCollection collection) - { - _result = collection.FindOneAndUpdateAsync(_filter, _update, _options).GetAwaiter().GetResult(); - } - - protected override void ExecuteSync(IMongoCollection collection) - { - _result = collection.FindOneAndUpdate(_filter, _update, _options); - } - - protected override void VerifyResult(BsonDocument result) - { - _result.Should().Be(result); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/IRetryableWriteTest.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/IRetryableWriteTest.cs deleted file mode 100644 index 784e19e3211..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/IRetryableWriteTest.cs +++ /dev/null @@ -1,26 +0,0 @@ -/* Copyright 2017-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using MongoDB.Bson; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes -{ - public interface IRetryableWriteTest - { - void Execute(IMongoCollection collection, bool async); - void Initialize(BsonDocument operation); - void VerifyOutcome(IMongoCollection collection, BsonDocument outcome); - } -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/InsertManyTest.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/InsertManyTest.cs deleted file mode 100644 index e9dc9649afb..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/InsertManyTest.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright 2017-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using MongoDB.Bson; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes -{ - public class InsertManyTest : RetryableWriteTestBase - { - // private fields - private IEnumerable _documents; - private InsertManyOptions _options; - - // public methods - public override void Initialize(BsonDocument operation) - { - VerifyFields(operation, "name", "arguments"); - - foreach (var argument in operation["arguments"].AsBsonDocument) - { - switch (argument.Name) - { - case "documents": - _documents = argument.Value.AsBsonArray.Cast(); - break; - - case "options": - _options = ParseOptions(argument.Value.AsBsonDocument); - break; - - default: - throw new ArgumentException($"Unexpected argument: {argument.Name}."); - } - } - } - - // protected methods - protected override void ExecuteAsync(IMongoCollection collection) - { - collection.InsertManyAsync(_documents, _options).GetAwaiter().GetResult(); - } - - protected override void ExecuteSync(IMongoCollection collection) - { - collection.InsertMany(_documents, _options); - } - - protected override void VerifyResult(BsonDocument result) - { - // test specifies a result but in the .NET driver InsertMany is a void method - } - - // private methods - private InsertManyOptions ParseOptions(BsonDocument optionsDocument) - { - var options = new InsertManyOptions(); - - foreach (var option in optionsDocument) - { - switch (option.Name) - { - case "ordered": - options.IsOrdered = option.Value.ToBoolean(); - break; - - default: - throw new ArgumentException($"Unexpected option: {option.Name}."); - } - } - - return options; - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/InsertOneTest.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/InsertOneTest.cs deleted file mode 100644 index fe7572becaa..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/InsertOneTest.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* Copyright 2017-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using MongoDB.Bson; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes -{ - public class InsertOneTest : RetryableWriteTestBase - { - // private fields - private BsonDocument _document; - - // public methods - public override void Initialize(BsonDocument operation) - { - VerifyFields(operation, "name", "arguments"); - - foreach (var argument in operation["arguments"].AsBsonDocument) - { - switch (argument.Name) - { - case "document": - _document = argument.Value.AsBsonDocument; - break; - - default: - throw new ArgumentException($"Unexpected argument: {argument.Name}."); - } - } - } - - // protected methods - protected override void ExecuteAsync(IMongoCollection collection) - { - collection.InsertOneAsync(_document).GetAwaiter().GetResult(); - } - - protected override void ExecuteSync(IMongoCollection collection) - { - collection.InsertOne(_document); - } - - protected override void VerifyResult(BsonDocument result) - { - // test specifies a result but in the .NET driver InsertOne is a void method - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/ReplaceOneTest.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/ReplaceOneTest.cs deleted file mode 100644 index 09b38218dfe..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/ReplaceOneTest.cs +++ /dev/null @@ -1,83 +0,0 @@ -/* Copyright 2017-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using FluentAssertions; -using MongoDB.Bson; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes -{ - public class ReplaceOneTest : RetryableWriteTestBase - { - // private fields - FilterDefinition _filter; - BsonDocument _replacement; - ReplaceOneResult _result; - - // public methods - public override void Initialize(BsonDocument operation) - { - VerifyFields(operation, "name", "arguments"); - - foreach (var argument in operation["arguments"].AsBsonDocument) - { - switch (argument.Name) - { - case "filter": - _filter = argument.Value.AsBsonDocument; - break; - - case "replacement": - _replacement = argument.Value.AsBsonDocument; - break; - - default: - throw new ArgumentException($"Unexpected argument: {argument.Name}."); - } - } - } - - - // protected methods - protected override void ExecuteAsync(IMongoCollection collection) - { - _result = collection.ReplaceOneAsync(_filter, _replacement).GetAwaiter().GetResult(); - } - - protected override void ExecuteSync(IMongoCollection collection) - { - _result = collection.ReplaceOne(_filter, _replacement); - } - - protected override void VerifyResult(BsonDocument result) - { - var expectedResult = ParseResult(result); - _result.MatchedCount.Should().Be(expectedResult.MatchedCount); - _result.ModifiedCount.Should().Be(expectedResult.ModifiedCount); - } - - // private methods - private ReplaceOneResult ParseResult(BsonValue result) - { - VerifyFields((BsonDocument)result, "matchedCount", "modifiedCount", "upsertedCount"); - var matchedCount = result["matchedCount"].ToInt64(); - var modifiedCount = result["modifiedCount"].ToInt64(); - var upsertedCount = result["upsertedCount"].ToInt64(); - upsertedCount.Should().Be(0); - - return new ReplaceOneResult.Acknowledged(matchedCount, modifiedCount, null); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWriteTestBase.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWriteTestBase.cs deleted file mode 100644 index e0d79ed3ec4..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWriteTestBase.cs +++ /dev/null @@ -1,125 +0,0 @@ -/* Copyright 2017-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using FluentAssertions; -using MongoDB.Bson; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes -{ - public abstract class RetryableWriteTestBase : IRetryableWriteTest - { - // protected fields - protected Exception _exception; - - // public methods - public virtual void Execute(IMongoCollection collection, bool async) - { - try - { - if (async) - { - ExecuteAsync(collection); - } - else - { - ExecuteSync(collection); - } - } - catch (Exception ex) - { - _exception = ex; - } - } - - public abstract void Initialize(BsonDocument operation); - - public virtual void VerifyOutcome(IMongoCollection collection, BsonDocument outcome) - { - VerifyFields(outcome, "error", "result", "collection"); - - if (outcome.GetValue("error", false).ToBoolean()) - { - if (_exception == null) - { - throw new Exception("Was expecting an exception but none was thrown."); - } - } - else - { - if (_exception != null) - { - throw new Exception("Was not expecting an exception but one was thrown.", _exception); - } - } - - BsonValue result; - if (outcome.TryGetValue("result", out result) && _exception == null) - { - VerifyResult(result.AsBsonDocument); - } - - BsonValue collectionValue; - if (outcome.TryGetValue("collection", out collectionValue)) - { - var collectionDocument = collectionValue.AsBsonDocument; - VerifyFields(collectionDocument, "data"); - var data = collectionDocument["data"].AsBsonArray; - - var actualContents = ReadContents(collection); - var expectedContents = ParseExpectedContents(data); - VerifyCollectionContents(actualContents, expectedContents); - } - } - - // protected methods - protected abstract void ExecuteAsync(IMongoCollection collection); - - protected abstract void ExecuteSync(IMongoCollection collection); - - protected virtual List ParseExpectedContents(BsonArray data) - { - return data.Cast().ToList(); - } - - protected virtual List ReadContents(IMongoCollection collection) - { - return collection.FindSync("{ }").ToList(); - } - - protected virtual void VerifyCollectionContents(List actualContents, List expectedContents) - { - actualContents.Should().BeEquivalentTo(expectedContents); - } - - protected void VerifyFields(BsonDocument document, params string[] expectedNames) - { - foreach (var name in document.Names) - { - if (!expectedNames.Contains(name)) - { - throw new FormatException($"Unexpected field: {name}."); - } - } - } - - protected virtual void VerifyResult(BsonDocument result) - { - throw new NotImplementedException(); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWriteTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWriteTestRunner.cs deleted file mode 100644 index a1e17f8c676..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWriteTestRunner.cs +++ /dev/null @@ -1,320 +0,0 @@ -/* Copyright 2017-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using Microsoft.Extensions.Logging; -using MongoDB.Bson; -using MongoDB.Driver.Core.TestHelpers.Logging; -using MongoDB.Driver.Core.TestHelpers.XunitExtensions; -using MongoDB.Driver.TestHelpers; -using MongoDB.TestHelpers.XunitExtensions; -using Xunit; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes -{ - [Category("Serverless", "SupportLoadBalancing")] - public class RetryableWriteTestRunner : LoggableTestClass - { - private readonly string _databaseName = DriverTestConfiguration.DatabaseNamespace.DatabaseName; - private readonly string _collectionName = DriverTestConfiguration.CollectionNamespace.CollectionName; - - // public constructors - public RetryableWriteTestRunner(ITestOutputHelper testOutputHelper) - : base(testOutputHelper) - { - } - - // public methods - [Theory] - [ClassData(typeof(TestCaseFactory))] - public void RunTestDefinition(TestCase testCase) - { - var definition = testCase.Definition; - var test = testCase.Test; - var async = testCase.Async; - - VerifyServerRequirements(definition); - VerifyFields(definition, "path", "data", "runOn", "tests"); - - InitializeCollection(definition); - RunTest(test, async); - } - - // private methods - private void VerifyServerRequirements(BsonDocument definition) - { - if (definition.TryGetValue("runOn", out var runOn)) - { - RequireServer.Check().RunOn(runOn.AsBsonArray); - } - - if (CoreTestConfiguration.StorageEngine == "mmapv1") - { - throw new SkipException("Test skipped because mmapv1 does not support retryable writes."); - } - } - - private void VerifyFields(BsonDocument document, params string[] expectedNames) - { - foreach (var name in document.Names) - { - if (!expectedNames.Contains(name)) - { - throw new FormatException($"Unexpected field: \"{name}\"."); - } - } - } - - private void InitializeCollection(BsonDocument definition) - { - var client = DriverTestConfiguration.Client; - var database = client.GetDatabase(_databaseName); - var collection = database.GetCollection(_collectionName); - - Logger.LogDebug("Dropping colleciton"); - - database.DropCollection(collection.CollectionNamespace.CollectionName); - if (definition.TryGetValue("data", out var data) && - data is BsonArray dataArray && - dataArray.Count > 0) - { - collection.InsertMany(dataArray.Cast()); - } - } - - private void RunTest(BsonDocument test, bool async) - { - Logger.LogDebug("Running test"); - - var useMultipleShardRouters = test.GetValue("useMultipleMongoses", false).AsBoolean; - RequireServer.Check().MultipleMongosesIfSharded(required: useMultipleShardRouters); - - VerifyFields(test, "description", "clientOptions", "failPoint", "operation", "outcome", "useMultipleMongoses"); - var failPoint = (BsonDocument)test.GetValue("failPoint", null); - var operation = test["operation"].AsBsonDocument; - var outcome = test["outcome"].AsBsonDocument; - - using (var client = CreateMongoClient(test, useMultipleShardRouters)) - { - var database = client.GetDatabase(_databaseName); - var collection = database.GetCollection(_collectionName); - var executableTest = CreateExecutableTest(operation); - - using (ConfigureFailPoint(client, failPoint)) - { - - executableTest.Execute(collection, async); - } - - executableTest.VerifyOutcome(collection, outcome); - } - } - - private IMongoClient CreateMongoClient(BsonDocument test, bool useMultipleShardRouters) - { - var clientOptions = (BsonDocument)test.GetValue("clientOptions", null); - - return DriverTestConfiguration.CreateMongoClient( - settings => - { - settings.HeartbeatInterval = TimeSpan.FromMilliseconds(5); // the default value for spec tests - settings.LoggingSettings = LoggingSettings; - ParseClientOptions(settings, clientOptions); - }, - useMultipleShardRouters); - } - - private void ParseClientOptions(MongoClientSettings settings, BsonDocument clientOptions) - { - if (clientOptions == null) - { - settings.RetryWrites = true; - } - else - { - foreach (var element in clientOptions) - { - var name = element.Name; - var value = element.Value; - - switch (name) - { - case "retryWrites": - settings.RetryWrites = value.ToBoolean(); - break; - - default: - throw new FormatException($"Invalid clientOptions field: {name}."); - } - } - } - } - - private IRetryableWriteTest CreateExecutableTest(BsonDocument operation) - { - var operationName = operation["name"].AsString; - - IRetryableWriteTest executableTest; - switch (operationName) - { - case "bulkWrite": executableTest = new BulkWriteTest(); break; - case "deleteOne": executableTest = new DeleteOneTest(); break; - case "deleteMany": executableTest = new DeleteManyTest(); break; - case "findOneAndDelete": executableTest = new FindOneAndDeleteTest(); break; - case "findOneAndReplace": executableTest = new FindOneAndReplaceTest(); break; - case "findOneAndUpdate": executableTest = new FindOneAndUpdateTest(); break; - case "insertOne": executableTest = new InsertOneTest(); break; - case "insertMany": executableTest = new InsertManyTest(); break; - case "replaceOne": executableTest = new ReplaceOneTest(); break; - case "updateOne": executableTest = new UpdateOneTest(); break; - case "updateMany": executableTest = new UpdateManyTest(); break; - default: throw new ArgumentException($"Unexpected operation name: {operationName}."); - } - executableTest.Initialize(operation); - - return executableTest; - } - - private IDisposable ConfigureFailPoint(IMongoClient client, BsonDocument failPoint) - { - if (failPoint == null) - { - return null; - } - else - { - var adminDatabase = client.GetDatabase("admin"); - adminDatabase.RunCommand(failPoint); - - var failPointName = failPoint["configureFailPoint"].AsString; - var disableFailPointCommand = new BsonDocument - { - { "configureFailPoint", failPointName }, - { "mode", "off" } - }; - return new ActionDisposer(() => adminDatabase.RunCommand(disableFailPointCommand)); - } - } - - // nested types - public class TestCase : IXunitSerializable - { - public string Name; - public BsonDocument Definition; - public BsonDocument Test; - public bool Async; - - public TestCase() - { - } - - public TestCase(string name, BsonDocument definition, BsonDocument test, bool async) - { - Name = name; - Definition = definition; - Test = test; - Async = async; - } - - public void Deserialize(IXunitSerializationInfo info) - { - Name = info.GetValue(nameof(Name)); - Definition = BsonDocument.Parse(info.GetValue(nameof(Definition))); - Test = BsonDocument.Parse(info.GetValue(nameof(Test))); - Async = info.GetValue(nameof(Async)); - } - - public void Serialize(IXunitSerializationInfo info) - { - info.AddValue(nameof(Name), Name); - info.AddValue(nameof(Definition), Definition.ToString()); - info.AddValue(nameof(Test), Test.ToString()); - info.AddValue(nameof(Async), Async); - } - - public override string ToString() - { - return Async ? $"{Name}(Async)" : Name; - } - } - - private class TestCaseFactory : IEnumerable - { - public IEnumerator GetEnumerator() - { - const string prefix = "MongoDB.Driver.Tests.Specifications.retryable_writes.tests.legacy"; - var definitions = typeof(TestCaseFactory).GetTypeInfo().Assembly - .GetManifestResourceNames() - .Where(path => path.StartsWith(prefix) && path.EndsWith(".json")) - .Select(path => ReadDefinition(path)); - - var testCases = new List(); - foreach (var definition in definitions) - { - foreach (BsonDocument test in definition["tests"].AsBsonArray) - { - foreach (var async in new[] { false, true }) - { - var name = test["description"].ToString(); - var testCase = new object[] { new TestCase(name, definition, test, async) }; - testCases.Add(testCase); - } - } - } - - return testCases.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - private static BsonDocument ReadDefinition(string path) - { - using (var definitionStream = typeof(TestCaseFactory).GetTypeInfo().Assembly.GetManifestResourceStream(path)) - using (var definitionStringReader = new StreamReader(definitionStream)) - { - var definitionString = definitionStringReader.ReadToEnd(); - var definition = BsonDocument.Parse(definitionString); - definition.InsertAt(0, new BsonElement("path", path)); - return definition; - } - } - } - - private class ActionDisposer : IDisposable - { - private readonly Action _action; - - public ActionDisposer(Action action) - { - _action = action; - } - - public void Dispose() - { - _action(); - } - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/UpdateManyTest.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/UpdateManyTest.cs deleted file mode 100644 index 99d1255d493..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/UpdateManyTest.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright 2019-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using FluentAssertions; -using MongoDB.Bson; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes -{ - public class UpdateManyTest : RetryableWriteTestBase - { - private FilterDefinition _filter; - private UpdateDefinition _update; - private readonly UpdateOptions _options = new UpdateOptions(); - private UpdateResult _result; - - // public methods - public override void Initialize(BsonDocument operation) - { - VerifyFields(operation, "name", "arguments"); - - foreach (var argument in operation["arguments"].AsBsonDocument) - { - switch (argument.Name) - { - case "filter": - _filter = argument.Value.AsBsonDocument; - break; - - case "update": - _update = argument.Value.AsBsonDocument; - break; - - case "upsert": - _options.IsUpsert = argument.Value.ToBoolean(); - break; - - default: - throw new ArgumentException($"Unexpected argument: {argument.Name}."); - } - } - } - - // protected methods - protected override void ExecuteAsync(IMongoCollection collection) - { - _result = collection.UpdateManyAsync(_filter, _update, _options).GetAwaiter().GetResult(); - } - - protected override void ExecuteSync(IMongoCollection collection) - { - _result = collection.UpdateMany(_filter, _update, _options); - } - - protected override void VerifyResult(BsonDocument result) - { - var expectedResult = ParseResult(result); - _result.MatchedCount.Should().Be(expectedResult.MatchedCount); - _result.ModifiedCount.Should().Be(expectedResult.ModifiedCount); - _result.UpsertedId.Should().Be(expectedResult.UpsertedId); - } - - // private methods - private UpdateResult ParseResult(BsonDocument result) - { - VerifyFields(result, "matchedCount", "modifiedCount", "upsertedCount", "upsertedId"); - - var matchedCount = result["matchedCount"].ToInt64(); - var modifiedCount = result["modifiedCount"].ToInt64(); - var upsertedCount = result["upsertedCount"].ToInt32(); - var upsertedId = result.GetValue("upsertedId", null); - upsertedCount.Should().Be(upsertedId == null ? 0 : 1); - - return new UpdateResult.Acknowledged(matchedCount, modifiedCount, upsertedId); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/UpdateOneTest.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/UpdateOneTest.cs deleted file mode 100644 index 51c317969d4..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/UpdateOneTest.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright 2017-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using FluentAssertions; -using MongoDB.Bson; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes -{ - public class UpdateOneTest : RetryableWriteTestBase - { - FilterDefinition _filter; - UpdateDefinition _update; - UpdateOptions _options = new UpdateOptions(); - UpdateResult _result; - - // public methods - public override void Initialize(BsonDocument operation) - { - VerifyFields(operation, "name", "arguments"); - - foreach (var argument in operation["arguments"].AsBsonDocument) - { - switch (argument.Name) - { - case "filter": - _filter = argument.Value.AsBsonDocument; - break; - - case "update": - _update = argument.Value.AsBsonDocument; - break; - - case "upsert": - _options.IsUpsert = argument.Value.ToBoolean(); - break; - - default: - throw new ArgumentException($"Unexpected argument: {argument.Name}."); - } - } - } - - // protected methods - protected override void ExecuteAsync(IMongoCollection collection) - { - _result = collection.UpdateOneAsync(_filter, _update, _options).GetAwaiter().GetResult(); - } - - protected override void ExecuteSync(IMongoCollection collection) - { - _result = collection.UpdateOne(_filter, _update, _options); - } - - protected override void VerifyResult(BsonDocument result) - { - var expectedResult = ParseResult(result); - _result.MatchedCount.Should().Be(expectedResult.MatchedCount); - _result.ModifiedCount.Should().Be(expectedResult.ModifiedCount); - _result.UpsertedId.Should().Be(expectedResult.UpsertedId); - } - - // private methods - private UpdateResult ParseResult(BsonDocument result) - { - VerifyFields(result, "matchedCount", "modifiedCount", "upsertedCount", "upsertedId"); - - var matchedCount = result["matchedCount"].ToInt64(); - var modifiedCount = result["modifiedCount"].ToInt64(); - var upsertedCount = result["upsertedCount"].ToInt32(); - var upsertedId = result.GetValue("upsertedId", null); - upsertedCount.Should().Be(upsertedId == null ? 0 : 1); - - return new UpdateResult.Acknowledged(matchedCount, modifiedCount, upsertedId); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/CommandConstructionTests.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/CommandConstructionTests.cs deleted file mode 100644 index fa2c951133f..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/CommandConstructionTests.cs +++ /dev/null @@ -1,496 +0,0 @@ -/* Copyright 2020-present MongoDB Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using FluentAssertions; -using Microsoft.Extensions.Logging; -using MongoDB.Bson; -using MongoDB.Driver.Core; -using MongoDB.Driver.Core.Clusters; -using MongoDB.Driver.Core.Events; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.TestHelpers.Logging; -using MongoDB.Driver.Core.TestHelpers.XunitExtensions; -using MongoDB.TestHelpers.XunitExtensions; -using Xunit; -using Xunit.Abstractions; - -namespace MongoDB.Driver.Tests.Specifications.retryable_writes.prose_tests -{ - [Trait("Category", "Serverless")] - public class CommandConstructionTests : LoggableTestClass - { - private readonly string _collectionName = CoreTestConfiguration.GetCollectionNamespaceForTestClass(typeof(CommandConstructionTests)).CollectionName; - private readonly string _databaseName = CoreTestConfiguration.DatabaseNamespace.DatabaseName; - - // public constructors - public CommandConstructionTests(ITestOutputHelper testOutputHelper) - : base(testOutputHelper) - { - } - - // public methods - [Theory] - [ParameterAttributeData] - public void Unacknowledged_writes_should_not_have_transaction_id( - [Values("delete", "insert", "update")] string operation, - [Values(false, true)] bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded); - - DropCollection(); - var eventCapturer = CreateEventCapturer(); - using (var client = CreateMongoClient(eventCapturer)) - { - var database = client.GetDatabase(_databaseName); - var collection = database.GetCollection(_collectionName).WithWriteConcern(WriteConcern.Unacknowledged); - - switch (operation) - { - case "delete": - var deleteFilter = Builders.Filter.Eq("_id", 1); - if (async) - { - collection.DeleteOneAsync(deleteFilter).GetAwaiter().GetResult(); - } - else - { - collection.DeleteOne(deleteFilter); - } - break; - - case "insert": - var document = new BsonDocument("_id", 1); - if (async) - { - collection.InsertOneAsync(document).GetAwaiter().GetResult(); ; - } - else - { - collection.InsertOne(document); - } - SpinUntilCollectionIsNotEmpty(); // wait for unacknowledged insert to complete so it won't execute later while another test is running - break; - - case "update": - var updateFilter = Builders.Filter.Eq("_id", 1); - var update = Builders.Update.Set("x", 1); - if (async) - { - collection.UpdateOneAsync(updateFilter, update).GetAwaiter().GetResult(); - } - else - { - collection.UpdateOne(updateFilter, update); - } - break; - - default: - throw new Exception($"Unexpected operation: {operation}."); - } - - AssertCommandDoesNotHaveTransactionId(eventCapturer); - } - } - - [Theory] - [ParameterAttributeData] - public void Unsupported_single_statement_writes_should_not_have_transaction_id( - [Values("deleteMany", "updateMany")] string operation, - [Values(false, true)] bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded); - - DropCollection(); - var eventCapturer = CreateEventCapturer(); - using (var client = CreateMongoClient(eventCapturer)) - { - var database = client.GetDatabase(_databaseName); - var collection = database.GetCollection(_collectionName); - - switch (operation) - { - case "deleteMany": - var deleteManyFilter = Builders.Filter.Eq("_id", 1); - if (async) - { - collection.DeleteManyAsync(deleteManyFilter).GetAwaiter().GetResult(); - } - else - { - collection.DeleteMany(deleteManyFilter); - } - break; - - case "updateMany": - var updateManyFilter = Builders.Filter.Eq("_id", 1); - var update = Builders.Update.Set("x", 1); - if (async) - { - collection.UpdateManyAsync(updateManyFilter, update).GetAwaiter().GetResult(); - } - else - { - collection.UpdateMany(updateManyFilter, update); - } - break; - - default: - throw new Exception($"Unexpected operation: {operation}."); - } - - AssertCommandDoesNotHaveTransactionId(eventCapturer); - } - } - - [Theory] - [ParameterAttributeData] - public void Unsupported_multi_statement_writes_should_not_have_transaction_id( - [Values("deleteMany", "updateMany")] string operation, - [Values(false, true)] bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded); - - DropCollection(); - var eventCapturer = CreateEventCapturer(); - using (var client = CreateMongoClient(eventCapturer)) - { - var database = client.GetDatabase(_databaseName); - var collection = database.GetCollection(_collectionName); - - WriteModel[] requests; - switch (operation) - { - case "deleteMany": - var deleteManyFilter = Builders.Filter.Eq("_id", 1); - requests = new[] { new DeleteManyModel(deleteManyFilter) }; - break; - - case "updateMany": - var updateManyFilter = Builders.Filter.Eq("_id", 1); - var update = Builders.Update.Set("x", 1); - requests = new[] { new UpdateManyModel(updateManyFilter, update) }; - break; - - default: - throw new Exception($"Unexpected operation: {operation}."); - } - if (async) - { - collection.BulkWriteAsync(requests).GetAwaiter().GetResult(); - } - else - { - collection.BulkWrite(requests); - } - - AssertCommandDoesNotHaveTransactionId(eventCapturer); - } - } - - [Theory] - [ParameterAttributeData] - public void Aggregate_with_write_stage_should_not_have_transaction_id( - [Values("$out", "$merge")] string outStage, - [Values(false, true)] bool async) - { - RequireServer - .Check() - .ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded) - .Serverless(false); // $out and $merge are not supported on serverless. - - if (outStage == "$merge") - { - RequireServer.Check().Supports(Feature.AggregateMerge); - } - - DropAndCreateCollection(); - - var eventCapturer = CreateEventCapturer(); - using (var client = CreateMongoClient(eventCapturer)) - { - var database = client.GetDatabase(_databaseName); - var collection = database.GetCollection(_collectionName); - - PipelineDefinition pipeline = new EmptyPipelineDefinition(); - var outputCollection = database.GetCollection(_collectionName + "-outputCollection"); - switch (outStage) - { - case "$out": - pipeline = pipeline.Out(outputCollection); - break; - - case "$merge": - var mergeOptions = new MergeStageOptions(); - pipeline = pipeline.Merge(outputCollection, mergeOptions); - break; - - default: - throw new Exception($"Unexpected outStage: {outStage}."); - } - if (async) - { - collection.AggregateAsync(pipeline).GetAwaiter().GetResult(); - } - else - { - collection.Aggregate(pipeline); - } - - AssertCommandDoesNotHaveTransactionId(eventCapturer); - } - } - - [Theory] - [ParameterAttributeData] - public void Supported_single_statement_writes_should_have_transaction_id( - [Values("insertOne", "updateOne", "replaceOne", "deleteOne", "findOneAndDelete", "findOneAndReplace", "findOneAndUpdate")] string operation, - [Values(false, true)] bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded); - - DropCollection(); - var eventCapturer = CreateEventCapturer(); - using (var client = CreateMongoClient(eventCapturer)) - { - var database = client.GetDatabase(_databaseName); - var collection = database.GetCollection(_collectionName); - - switch (operation) - { - case "deleteOne": - var deleteOneFilter = Builders.Filter.Eq("_id", 1); - if (async) - { - collection.DeleteOneAsync(deleteOneFilter).GetAwaiter().GetResult(); - } - else - { - collection.DeleteOne(deleteOneFilter); - } - break; - - case "findOneAndDelete": - var findOneAndDeleteFilter = Builders.Filter.Eq("_id", 1); - if (async) - { - collection.FindOneAndDeleteAsync(findOneAndDeleteFilter).GetAwaiter().GetResult(); - } - else - { - collection.FindOneAndDelete(findOneAndDeleteFilter); - } - break; - - case "findOneAndReplace": - var findOneAndReplaceFilter = Builders.Filter.Eq("_id", 1); - var findOneAndReplaceReplacement = new BsonDocument("_id", 1); - if (async) - { - collection.FindOneAndReplaceAsync(findOneAndReplaceFilter, findOneAndReplaceReplacement).GetAwaiter().GetResult(); - } - else - { - collection.FindOneAndReplace(findOneAndReplaceFilter, findOneAndReplaceReplacement); - } - break; - - case "findOneAndUpdate": - var findOneAndUpdateFilter = Builders.Filter.Eq("_id", 1); - var findOneAndUpdateUpdate = Builders.Update.Set("x", 2); - if (async) - { - collection.FindOneAndUpdateAsync(findOneAndUpdateFilter, findOneAndUpdateUpdate).GetAwaiter().GetResult(); - } - else - { - collection.FindOneAndUpdate(findOneAndUpdateFilter, findOneAndUpdateUpdate); - } - break; - - case "insertOne": - var document = new BsonDocument("_id", 1); - if (async) - { - collection.InsertOneAsync(document).GetAwaiter().GetResult(); - } - else - { - collection.InsertOne(document); - } - break; - - case "replaceOne": - var replaceOneFilter = Builders.Filter.Eq("_id", 1); - var replacement = new BsonDocument("_id", 1); - if (async) - { - collection.ReplaceOneAsync(replaceOneFilter, replacement).GetAwaiter().GetResult(); - } - else - { - collection.ReplaceOne(replaceOneFilter, replacement); - } - break; - - case "updateOne": - var updateOneFilter = Builders.Filter.Eq("_id", 1); - var updateOne = Builders.Update.Set("x", 2); - if (async) - { - collection.UpdateOneAsync(updateOneFilter, updateOne).GetAwaiter().GetResult(); - } - else - { - collection.UpdateOne(updateOneFilter, updateOne); - } - break; - - default: - throw new Exception($"Unexpected operation: {operation}."); - } - - AssertCommandHasTransactionId(eventCapturer); - } - } - - [Theory] - [ParameterAttributeData] - public void Supported_multi_statement_writes_should_have_transaction_id( - [Values("insertMany", "bulkWrite")] string operation, - [Values(false, true)] bool ordered, - [Values(false, true)] bool async) - { - RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded); - - DropCollection(); - var eventCapturer = CreateEventCapturer(); - using (var client = CreateMongoClient(eventCapturer)) - { - var database = client.GetDatabase(_databaseName); - var collection = database.GetCollection(_collectionName); - - switch (operation) - { - case "bulkWrite": - var requests = new[] { new InsertOneModel(new BsonDocument("_id", 1)) }; - var bulkWriteOptions = new BulkWriteOptions { IsOrdered = ordered }; - if (async) - { - collection.BulkWriteAsync(requests, bulkWriteOptions).GetAwaiter().GetResult(); - } - else - { - collection.BulkWrite(requests, bulkWriteOptions); - } - break; - - case "insertMany": - var documents = new[] { new BsonDocument("_id", 1) }; - var insertManyOptions = new InsertManyOptions { IsOrdered = ordered }; - if (async) - { - collection.InsertManyAsync(documents, insertManyOptions).GetAwaiter().GetResult(); - } - else - { - collection.InsertMany(documents, insertManyOptions); - } - break; - - default: - throw new Exception($"Unexpected operation: {operation}."); - } - - AssertCommandHasTransactionId(eventCapturer); - } - } - - // private methods - private void AssertCommandDoesNotHaveTransactionId(EventCapturer eventCapturer) - { - var commandStartedEvent = eventCapturer.Events.OfType().Single(); - var command = commandStartedEvent.Command; - command.Should().NotContain("txnNumber"); - } - - private void AssertCommandHasTransactionId(EventCapturer eventCapturer) - { - var commandStartedEvent = eventCapturer.Events.OfType().Single(); - var command = commandStartedEvent.Command; - command.Should().Contain("txnNumber"); - } - - private IMongoClient CreateMongoClient(EventCapturer eventCapturer) => - DriverTestConfiguration.CreateMongoClient((MongoClientSettings settings) => - { - settings.ClusterConfigurator = c => c.Subscribe(eventCapturer); - settings.RetryWrites = true; - settings.LoggingSettings = LoggingSettings; - }); - - private EventCapturer CreateEventCapturer() - { - var commandsToNotCapture = new HashSet - { - "hello", - OppressiveLanguageConstants.LegacyHelloCommandName, - "getLastError", - "authenticate", - "saslStart", - "saslContinue", - "getnonce" - }; - - return - new EventCapturer() - .Capture(e => !commandsToNotCapture.Contains(e.CommandName)); - } - - private void DropAndCreateCollection() - { - var client = DriverTestConfiguration.Client; - var database = client.GetDatabase(_databaseName); - - Logger.LogDebug("Dropping collection {0}", _collectionName); - database.DropCollection(_collectionName); - - Logger.LogDebug("Creating collection {0}", _collectionName); - database.CreateCollection(_collectionName); - - Logger.LogDebug("Created collection {0}", _collectionName); - } - - private void DropCollection() - { - var client = DriverTestConfiguration.Client; - var database = client.GetDatabase(_databaseName); - - Logger.LogDebug("Dropping collection {0}", _collectionName); - database.DropCollection(_collectionName); - } - - private void SpinUntilCollectionIsNotEmpty() - { - var client = DriverTestConfiguration.Client; - var database = client.GetDatabase(_databaseName); - var collection = database.GetCollection(_collectionName); - SpinWait.SpinUntil(() => collection.CountDocuments("{}") > 0, TimeSpan.FromSeconds(10)).Should().BeTrue(); - } - } -} diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedErrorMatcher.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedErrorMatcher.cs index d44372cf8e0..05212e01138 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedErrorMatcher.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedErrorMatcher.cs @@ -144,7 +144,17 @@ private void AssertExpectResult(Exception actualException, BsonDocument expected break; default: - throw new NotSupportedException($"Unrecognized exception type: '{actualException.GetType().FullName}'."); + actualResult = new BsonDocument + { + { "insertedCount", 0 }, + { "upsertedCount", 0 }, + { "matchedCount", 0 }, + { "modifiedCount", 0 }, + { "deletedCount", 0 }, + { "insertedIds", new BsonDocument() }, + { "upsertedIds", new BsonDocument() } + }; + break; } new UnifiedValueMatcher(_entityMap).AssertValuesMatch(actualResult, expectedResult); diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedFindOneAndDeleteOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedFindOneAndDeleteOperation.cs index 995cb0a93cd..651b846ff03 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedFindOneAndDeleteOperation.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedFindOneAndDeleteOperation.cs @@ -100,6 +100,10 @@ public UnifiedFindOneAndDeleteOperation Build(string targetCollectionId, BsonDoc options ??= new FindOneAndDeleteOptions(); options.Let = argument.Value.AsBsonDocument; break; + case "sort": + options ??= new FindOneAndDeleteOptions(); + options.Sort = argument.Value.AsBsonDocument; + break; default: throw new FormatException($"Invalid FindOneAndDeleteOperation argument name: '{argument.Name}'."); } diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedFindOneAndReplaceOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedFindOneAndReplaceOperation.cs index e0c513c5195..b81510cea59 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedFindOneAndReplaceOperation.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedFindOneAndReplaceOperation.cs @@ -107,6 +107,10 @@ public UnifiedFindOneAndReplaceOperation Build(string targetCollectionId, BsonDo case "replacement": replacement = argument.Value.AsBsonDocument; break; + case "returnDocument": + options ??= new FindOneAndReplaceOptions(); + options.ReturnDocument = (ReturnDocument)Enum.Parse(typeof(ReturnDocument), argument.Value.AsString, true); + break; default: throw new FormatException($"Invalid FindOneAndReplaceOperation argument name: '{argument.Name}'."); } diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedUpdateOneOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedUpdateOneOperation.cs index 2f7002dcb12..6daa2089b19 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedUpdateOneOperation.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedUpdateOneOperation.cs @@ -169,6 +169,7 @@ public OperationResult Convert(UpdateResult result) { "matchedCount", result.MatchedCount }, { "modifiedCount", result.ModifiedCount }, { "upsertedCount", result.UpsertedId == null ? 0 : 1 }, + { "upsertedId", result.UpsertedId, result.UpsertedId != null} }; return OperationResult.FromResult(document);