diff --git a/test/spec/retryable-writes/README.rst b/test/spec/retryable-writes/README.rst index 7cc234d51ce..5ddfb6a2d06 100644 --- a/test/spec/retryable-writes/README.rst +++ b/test/spec/retryable-writes/README.rst @@ -169,17 +169,21 @@ Each YAML file has the following keys: the default is all topologies (i.e. ``["single", "replicaset", "sharded", "load-balanced"]``). - - ``serverless``: Optional string. Whether or not the test should be run on - serverless instances imitating sharded clusters. Valid values are "require", - "forbid", and "allow". If "require", the test MUST only be run on serverless - instances. If "forbid", the test MUST NOT be run on serverless instances. If - omitted or "allow", this option has no effect. - - The test runner MUST be informed whether or not serverless is being used in - order to determine if this requirement is met (e.g. through an environment - variable or configuration option). Since the serverless proxy imitates a - mongos, the runner is not capable of determining this by issuing a server - command such as ``buildInfo`` or ``hello``. + - ``serverless``: (optional): Whether or not the test should be run on Atlas + Serverless instances. Valid values are "require", "forbid", and "allow". If + "require", the test MUST only be run on Atlas Serverless instances. If + "forbid", the test MUST NOT be run on Atlas Serverless instances. If omitted + or "allow", this option has no effect. + + The test runner MUST be informed whether or not Atlas Serverless is being + used in order to determine if this requirement is met (e.g. through an + environment variable or configuration option). + + Note: the Atlas Serverless proxy imitates mongos, so the test runner is not + capable of determining if Atlas Serverless is in use by issuing commands + such as ``buildInfo`` or ``hello``. Furthermore, connections to Atlas + Serverless use a load balancer, so the topology will appear as + "load-balanced". - ``data``: The data that should exist in the collection under test before each test run. @@ -196,13 +200,14 @@ Each YAML file has the following keys: mongos seed addresses. If ``false`` or omitted, only a single mongos address should be specified. - If ``true``, and the topology type is ``LoadBalanced``, the MongoClient for - this test should be initialized with the URI of the load balancer fronting - multiple servers. If ``false`` or omitted, the MongoClient for this test - should be initialized with the URI of the load balancer fronting a single - server. + If ``true``, the topology type is ``LoadBalanced``, and Atlas Serverless is + not being used, the MongoClient for this test should be initialized with the + URI of the load balancer fronting multiple servers. If ``false`` or omitted, + the MongoClient for this test should be initialized with the URI of the load + balancer fronting a single server. - ``useMultipleMongoses`` only affects ``Sharded`` and ``LoadBalanced`` topologies. + ``useMultipleMongoses`` only affects ``Sharded`` and ``LoadBalanced`` + topologies (excluding Atlas Serverless). - ``failPoint`` (optional): The ``configureFailPoint`` command document to run to configure a fail point on the primary server. Drivers must ensure that @@ -286,11 +291,11 @@ 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 +supported. `Command Logging and Monitoring`_ may be used to check for the presence of a ``txnNumber`` field in the command document. Note that command documents may always include an ``lsid`` field per the `Driver Session`_ specification. -.. _Command Monitoring: ../../command-monitoring/command-monitoring.rst +.. _Command Logging and Monitoring: ../../command-logging-and-monitoring/command-logging-and-monitoring.rst .. _Driver Session: ../../sessions/driver-sessions.rst These tests may be run against both a replica set and shard cluster. @@ -348,7 +353,7 @@ and sharded clusters. 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. @@ -390,11 +395,75 @@ and sharded clusters. 9. Disable the failpoint. +#. Test that drivers return the original error after encountering a + WriteConcernError with a RetryableWriteError label. This test MUST + + 1. be implemented by any driver that implements the Command Monitoring + specification, + + 2. only run against replica sets as mongos does not propagate the + NoWritesPerformed label to the drivers. + + 3. be run against server versions 6.0 and above. + + Additionally, this test requires drivers to set a fail point after an + ``insertOne`` operation but before the subsequent retry. Drivers that are + unable to set a failCommand after the CommandSucceededEvent SHOULD use + mocking or write a unit test to cover the same sequence of events. + + + 1. Create a client with ``retryWrites=true``. + + 2. Configure a fail point with error code ``91`` (ShutdownInProgress):: + + db.adminCommand({ + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + writeConcernError: { + code: 91, + errorLabels: ["RetryableWriteError"], + }, + failCommands: ["insert"], + }, + }); + + 3. Via the command monitoring CommandSucceededEvent, configure a fail point + with error code ``10107`` (NotWritablePrimary) and a NoWritesPerformed + label:: + + db.adminCommand({ + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + errorCode: 10107, + errorLabels: ["RetryableWriteError", "NoWritesPerformed"], + failCommands: ["insert"], + }, + }); + + Drivers SHOULD only configure the ``10107`` fail point command if the the + succeeded event is for the ``91`` error configured in step 2. + + 4. Attempt an ``insertOne`` operation on any record for any database and + collection. For the resulting error, assert that the associated error code + is ``91``. + + 5. Disable the fail point:: + + db.adminCommand({ + configureFailPoint: "failCommand", + mode: "off", + }) Changelog ========= +:2022-08-30: Add prose test verifying correct error handling for errors with + the NoWritesPerformed label, which is to return the original + error. +:2022-04-22: Clarifications to ``serverless`` and ``useMultipleMongoses``. :2021-08-27: Add ``serverless`` to ``runOn``. Clarify behavior of ``useMultipleMongoses`` for ``LoadBalanced`` topologies. diff --git a/test/spec/retryable-writes/unified/handshakeError.yml b/test/spec/retryable-writes/unified/handshakeError.yml index e1b69e82633..9b2774bc77e 100644 --- a/test/spec/retryable-writes/unified/handshakeError.yml +++ b/test/spec/retryable-writes/unified/handshakeError.yml @@ -1,3 +1,5 @@ +# Tests in this file are generated from handshakeError.yml.template. + description: "retryable writes handshake failures" schemaVersion: "1.3" @@ -9,16 +11,20 @@ runOnRequirements: createEntities: - client: - id: &client0 client0 + id: &client client useMultipleMongoses: false - observeEvents: [commandStartedEvent, connectionCheckOutStartedEvent] + observeEvents: + - connectionCheckOutStartedEvent + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent - database: - id: &database0 database0 - client: *client0 - databaseName: &databaseName retryable-handshake-tests + id: &database database + client: *client + databaseName: &databaseName retryable-writes-handshake-tests - collection: - id: &collection0 collection0 - database: *database0 + id: &collection collection + database: *database collectionName: &collectionName coll initialData: @@ -28,109 +34,752 @@ initialData: - { _id: 1, x: 11 } tests: - - description: "InsertOne succeeds after retryable handshake error" + # Because setting a failPoint creates a connection in the connection pool, run + # a ping operation that fails immediately after the failPoint operation in + # order to discard the connection before running the actual operation to be + # tested. The saslContinue command is used to avoid SDAM errors. + # + # Description of events: + # - Failpoint operation. + # - Creates a connection in the connection pool that must be closed. + # - Ping operation. + # - Triggers failpoint (first time). + # - Closes the connection made by the fail point operation. + # - Test operation. + # - New connection is created. + # - Triggers failpoint (second time). + # - Tests whether operation successfully retries the handshake and succeeds. + + - description: "collection.insertOne succeeds after retryable handshake network error" operations: - - name: failPoint # fail the next connection establishment + - name: failPoint object: testRunner arguments: - client: *client0 + client: *client failPoint: configureFailPoint: failCommand mode: { times: 2 } data: - failCommands: [saslContinue, ping] + failCommands: [ping, saslContinue] closeConnection: true - - name: runCommand - object: *database0 + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: insertOne + object: *collection arguments: - commandName: ping - command: { ping: 1 } - expectError: - isError: true + document: { _id: 2, x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert + - description: "collection.insertOne succeeds after retryable handshake server error (ShutdownInProgress)" + 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: insertOne - object: *collection0 + object: *collection arguments: document: { _id: 2, x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert + - description: "collection.insertMany succeeds after retryable handshake network error" + 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: insertMany + object: *collection + arguments: + documents: + - { _id: 2, x: 22 } expectEvents: - - client: *client0 + - client: *client eventType: cmap events: - { connectionCheckOutStartedEvent: {} } - { connectionCheckOutStartedEvent: {} } - { connectionCheckOutStartedEvent: {} } - { connectionCheckOutStartedEvent: {} } - - client: *client0 + - client: *client events: - commandStartedEvent: - command: - ping: 1 + command: { ping: 1 } databaseName: *databaseName + - commandFailedEvent: + commandName: ping - commandStartedEvent: - command: - insert: *collectionName - documents: [{ _id: 2, x: 22 }] commandName: insert + - commandSucceededEvent: + commandName: insert + + - description: "collection.insertMany succeeds after retryable handshake server error (ShutdownInProgress)" + 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: insertMany + object: *collection + arguments: + documents: + - { _id: 2, x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert - outcome: - - collectionName: *collectionName - databaseName: *databaseName - documents: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } # The write was still applied + - description: "collection.deleteOne succeeds after retryable handshake network error" + 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: deleteOne + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: delete + - commandSucceededEvent: + commandName: delete - - description: "InsertOne succeeds after retryable handshake error ShutdownInProgress" + - description: "collection.deleteOne succeeds after retryable handshake server error (ShutdownInProgress)" operations: - - name: failPoint # fail the next connection establishment + - name: failPoint object: testRunner arguments: - client: *client0 + client: *client failPoint: configureFailPoint: failCommand mode: { times: 2 } data: - failCommands: [saslContinue, ping] - errorCode: 91 # ShutdownInProgress + failCommands: [ping, saslContinue] + closeConnection: true - name: runCommand - object: *database0 + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: deleteOne + object: *collection arguments: - commandName: ping - command: { ping: 1 } - expectError: - isError: true + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: delete + - commandSucceededEvent: + commandName: delete - - name: insertOne - object: *collection0 + - description: "collection.replaceOne succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner arguments: - document: { _id: 2, x: 22 } + 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: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: update + - commandSucceededEvent: + commandName: update + + - description: "collection.replaceOne succeeds after retryable handshake server error (ShutdownInProgress)" + 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: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: update + - commandSucceededEvent: + commandName: update + - description: "collection.updateOne succeeds after retryable handshake network error" + 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: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 22 } } expectEvents: - - client: *client0 + - client: *client eventType: cmap events: - { connectionCheckOutStartedEvent: {} } - { connectionCheckOutStartedEvent: {} } - { connectionCheckOutStartedEvent: {} } - { connectionCheckOutStartedEvent: {} } - - client: *client0 + - client: *client events: - commandStartedEvent: - command: - ping: 1 + command: { ping: 1 } databaseName: *databaseName + - commandFailedEvent: + commandName: ping - commandStartedEvent: - command: - insert: *collectionName - documents: [{ _id: 2, x: 22 }] - commandName: insert + commandName: update + - commandSucceededEvent: + commandName: update + + - description: "collection.updateOne succeeds after retryable handshake server error (ShutdownInProgress)" + 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: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 22 } } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: update + - commandSucceededEvent: + commandName: update + + - description: "collection.findOneAndDelete succeeds after retryable handshake network error" + 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: findOneAndDelete + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify + + - description: "collection.findOneAndDelete succeeds after retryable handshake server error (ShutdownInProgress)" + 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: findOneAndDelete + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify - outcome: - - collectionName: *collectionName - databaseName: *databaseName - documents: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } # The write was still applied + - description: "collection.findOneAndReplace succeeds after retryable handshake network error" + 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: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify + + - description: "collection.findOneAndReplace succeeds after retryable handshake server error (ShutdownInProgress)" + 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: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify + + - description: "collection.findOneAndUpdate succeeds after retryable handshake network error" + 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: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 22 } } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify + + - description: "collection.findOneAndUpdate succeeds after retryable handshake server error (ShutdownInProgress)" + 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: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 22 } } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify + + - description: "collection.bulkWrite succeeds after retryable handshake network error" + 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: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 2, x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert + + - description: "collection.bulkWrite succeeds after retryable handshake server error (ShutdownInProgress)" + 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: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 2, x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert diff --git a/test/spec/retryable-writes/unified/insertOne-noWritesPerformedError.json b/test/spec/retryable-writes/unified/insertOne-noWritesPerformedError.json new file mode 100644 index 00000000000..3194e91c5ca --- /dev/null +++ b/test/spec/retryable-writes/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/test/spec/retryable-writes/unified/insertOne-noWritesPerformedError.yml b/test/spec/retryable-writes/unified/insertOne-noWritesPerformedError.yml new file mode 100644 index 00000000000..3295d153dd3 --- /dev/null +++ b/test/spec/retryable-writes/unified/insertOne-noWritesPerformedError.yml @@ -0,0 +1,54 @@ +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: []