diff --git a/test/asynchronous/test_retryable_writes.py b/test/asynchronous/test_retryable_writes.py index accbbd003f..ca2f0a5422 100644 --- a/test/asynchronous/test_retryable_writes.py +++ b/test/asynchronous/test_retryable_writes.py @@ -1,4 +1,4 @@ -# Copyright 2017 MongoDB, Inc. +# 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. @@ -43,7 +43,6 @@ from bson.int64 import Int64 from bson.raw_bson import RawBSONDocument from bson.son import SON -from pymongo.asynchronous.mongo_client import AsyncMongoClient from pymongo.errors import ( AutoReconnect, ConnectionFailure, @@ -226,47 +225,6 @@ async def test_supported_single_statement_no_retry(self): f"{msg} sent txnNumber with {event.command_name}", ) - @async_client_context.require_no_standalone - async def test_supported_single_statement_supported_cluster(self): - for method, args, kwargs in retryable_single_statement_ops(self.db.retryable_write_test): - msg = f"{method.__name__}(*{args!r}, **{kwargs!r})" - self.listener.reset() - await method(*args, **kwargs) - commands_started = self.listener.started_events - self.assertEqual(len(self.listener.succeeded_events), 1, msg) - first_attempt = commands_started[0] - self.assertIn( - "lsid", - first_attempt.command, - f"{msg} sent no lsid with {first_attempt.command_name}", - ) - initial_session_id = first_attempt.command["lsid"] - self.assertIn( - "txnNumber", - first_attempt.command, - f"{msg} sent no txnNumber with {first_attempt.command_name}", - ) - - # There should be no retry when the failpoint is not active. - if async_client_context.is_mongos or not async_client_context.test_commands_enabled: - self.assertEqual(len(commands_started), 1) - continue - - initial_transaction_id = first_attempt.command["txnNumber"] - retry_attempt = commands_started[1] - self.assertIn( - "lsid", - retry_attempt.command, - f"{msg} sent no lsid with {first_attempt.command_name}", - ) - self.assertEqual(retry_attempt.command["lsid"], initial_session_id, msg) - self.assertIn( - "txnNumber", - retry_attempt.command, - f"{msg} sent no txnNumber with {first_attempt.command_name}", - ) - self.assertEqual(retry_attempt.command["txnNumber"], initial_transaction_id, msg) - async def test_supported_single_statement_unsupported_cluster(self): if async_client_context.is_rs or async_client_context.is_mongos: raise SkipTest("This cluster supports retryable writes") diff --git a/test/retryable_writes/unified/aggregate-out-merge.json b/test/retryable_writes/unified/aggregate-out-merge.json new file mode 100644 index 0000000000..c46bf8c31f --- /dev/null +++ b/test/retryable_writes/unified/aggregate-out-merge.json @@ -0,0 +1,144 @@ +{ + "description": "aggregate with $out/$merge 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" + } + } + ], + "initialData": [ + { + "collectionName": "mergeCollection", + "databaseName": "retryable-writes-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "aggregate with $out does not set txnNumber", + "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/test/retryable_writes/unified/bulkWrite.json b/test/retryable_writes/unified/bulkWrite.json index 691321746b..f2bd9e0eb8 100644 --- a/test/retryable_writes/unified/bulkWrite.json +++ b/test/retryable_writes/unified/bulkWrite.json @@ -13,7 +13,10 @@ { "client": { "id": "client0", - "useMultipleMongoses": false + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] } }, { @@ -121,6 +124,53 @@ } ] } + ], + "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 + } + } + } + } + ] + } ] }, { @@ -510,6 +560,33 @@ } ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } ] }, { @@ -926,6 +1003,81 @@ ] } ] + }, + { + "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/test/retryable_writes/unified/client-bulkWrite-serverErrors.json b/test/retryable_writes/unified/client-bulkWrite-serverErrors.json index f58c82bcc7..a1f7c8152a 100644 --- a/test/retryable_writes/unified/client-bulkWrite-serverErrors.json +++ b/test/retryable_writes/unified/client-bulkWrite-serverErrors.json @@ -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/test/retryable_writes/unified/deleteMany.json b/test/retryable_writes/unified/deleteMany.json index 087576cc0f..381f377954 100644 --- a/test/retryable_writes/unified/deleteMany.json +++ b/test/retryable_writes/unified/deleteMany.json @@ -15,7 +15,10 @@ { "client": { "id": "client0", - "useMultipleMongoses": true + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] } }, { @@ -70,6 +73,23 @@ "databaseName": "retryable-writes-tests", "documents": [] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } ] } ] diff --git a/test/retryable_writes/unified/deleteOne.json b/test/retryable_writes/unified/deleteOne.json index c3aaf88655..9e37ff8bcf 100644 --- a/test/retryable_writes/unified/deleteOne.json +++ b/test/retryable_writes/unified/deleteOne.json @@ -13,7 +13,10 @@ { "client": { "id": "client0", - "useMultipleMongoses": false + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] } }, { @@ -88,6 +91,33 @@ } ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } ] }, { diff --git a/test/retryable_writes/unified/findOneAndDelete.json b/test/retryable_writes/unified/findOneAndDelete.json index 89dbb9d655..ebfb8ce665 100644 --- a/test/retryable_writes/unified/findOneAndDelete.json +++ b/test/retryable_writes/unified/findOneAndDelete.json @@ -13,7 +13,10 @@ { "client": { "id": "client0", - "useMultipleMongoses": false + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] } }, { @@ -94,6 +97,33 @@ } ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } ] }, { diff --git a/test/retryable_writes/unified/findOneAndReplace.json b/test/retryable_writes/unified/findOneAndReplace.json index 6d1cc17974..638d15a41d 100644 --- a/test/retryable_writes/unified/findOneAndReplace.json +++ b/test/retryable_writes/unified/findOneAndReplace.json @@ -13,7 +13,10 @@ { "client": { "id": "client0", - "useMultipleMongoses": false + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] } }, { @@ -98,6 +101,33 @@ } ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } ] }, { diff --git a/test/retryable_writes/unified/findOneAndUpdate.json b/test/retryable_writes/unified/findOneAndUpdate.json index eb88fbe9b3..eefe98ae11 100644 --- a/test/retryable_writes/unified/findOneAndUpdate.json +++ b/test/retryable_writes/unified/findOneAndUpdate.json @@ -13,7 +13,10 @@ { "client": { "id": "client0", - "useMultipleMongoses": false + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] } }, { @@ -99,6 +102,33 @@ } ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } ] }, { diff --git a/test/retryable_writes/unified/insertMany.json b/test/retryable_writes/unified/insertMany.json index 47181d0a9e..35a18c46c6 100644 --- a/test/retryable_writes/unified/insertMany.json +++ b/test/retryable_writes/unified/insertMany.json @@ -13,7 +13,10 @@ { "client": { "id": "client0", - "useMultipleMongoses": false + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] } }, { @@ -107,6 +110,33 @@ } ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } ] }, { @@ -172,6 +202,33 @@ } ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } ] }, { diff --git a/test/retryable_writes/unified/insertOne.json b/test/retryable_writes/unified/insertOne.json index 61957415ed..a6afdbf224 100644 --- a/test/retryable_writes/unified/insertOne.json +++ b/test/retryable_writes/unified/insertOne.json @@ -13,7 +13,10 @@ { "client": { "id": "client0", - "useMultipleMongoses": false + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] } }, { @@ -101,6 +104,33 @@ } ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } ] }, { diff --git a/test/retryable_writes/unified/replaceOne.json b/test/retryable_writes/unified/replaceOne.json index e58625bb5e..ee6e37d3bb 100644 --- a/test/retryable_writes/unified/replaceOne.json +++ b/test/retryable_writes/unified/replaceOne.json @@ -13,7 +13,10 @@ { "client": { "id": "client0", - "useMultipleMongoses": false + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] } }, { @@ -98,6 +101,33 @@ } ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } ] }, { diff --git a/test/retryable_writes/unified/unacknowledged-write-concern.json b/test/retryable_writes/unified/unacknowledged-write-concern.json new file mode 100644 index 0000000000..eaa114acfd --- /dev/null +++ b/test/retryable_writes/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/test/retryable_writes/unified/updateMany.json b/test/retryable_writes/unified/updateMany.json index 260b7ad1c6..12c5204ee9 100644 --- a/test/retryable_writes/unified/updateMany.json +++ b/test/retryable_writes/unified/updateMany.json @@ -15,7 +15,10 @@ { "client": { "id": "client0", - "useMultipleMongoses": true + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] } }, { @@ -86,6 +89,23 @@ } ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } ] } ] diff --git a/test/retryable_writes/unified/updateOne.json b/test/retryable_writes/unified/updateOne.json index 7947cef3c0..99ffba8e21 100644 --- a/test/retryable_writes/unified/updateOne.json +++ b/test/retryable_writes/unified/updateOne.json @@ -13,7 +13,10 @@ { "client": { "id": "client0", - "useMultipleMongoses": false + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] } }, { @@ -99,6 +102,33 @@ } ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } ] }, { diff --git a/test/test_retryable_writes.py b/test/test_retryable_writes.py index 5df6c41f7a..74f3c23e51 100644 --- a/test/test_retryable_writes.py +++ b/test/test_retryable_writes.py @@ -1,4 +1,4 @@ -# Copyright 2017 MongoDB, Inc. +# 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. @@ -65,7 +65,6 @@ UpdateMany, UpdateOne, ) -from pymongo.synchronous.mongo_client import MongoClient from pymongo.write_concern import WriteConcern _IS_SYNC = True @@ -226,47 +225,6 @@ def test_supported_single_statement_no_retry(self): f"{msg} sent txnNumber with {event.command_name}", ) - @client_context.require_no_standalone - def test_supported_single_statement_supported_cluster(self): - for method, args, kwargs in retryable_single_statement_ops(self.db.retryable_write_test): - msg = f"{method.__name__}(*{args!r}, **{kwargs!r})" - self.listener.reset() - method(*args, **kwargs) - commands_started = self.listener.started_events - self.assertEqual(len(self.listener.succeeded_events), 1, msg) - first_attempt = commands_started[0] - self.assertIn( - "lsid", - first_attempt.command, - f"{msg} sent no lsid with {first_attempt.command_name}", - ) - initial_session_id = first_attempt.command["lsid"] - self.assertIn( - "txnNumber", - first_attempt.command, - f"{msg} sent no txnNumber with {first_attempt.command_name}", - ) - - # There should be no retry when the failpoint is not active. - if client_context.is_mongos or not client_context.test_commands_enabled: - self.assertEqual(len(commands_started), 1) - continue - - initial_transaction_id = first_attempt.command["txnNumber"] - retry_attempt = commands_started[1] - self.assertIn( - "lsid", - retry_attempt.command, - f"{msg} sent no lsid with {first_attempt.command_name}", - ) - self.assertEqual(retry_attempt.command["lsid"], initial_session_id, msg) - self.assertIn( - "txnNumber", - retry_attempt.command, - f"{msg} sent no txnNumber with {first_attempt.command_name}", - ) - self.assertEqual(retry_attempt.command["txnNumber"], initial_transaction_id, msg) - def test_supported_single_statement_unsupported_cluster(self): if client_context.is_rs or client_context.is_mongos: raise SkipTest("This cluster supports retryable writes")