From 79991005c924e8ac85bc28ba61f15af135c1b4bc Mon Sep 17 00:00:00 2001 From: Matt Dale <9760375+matthewdale@users.noreply.github.com> Date: Tue, 5 May 2026 20:59:51 -0700 Subject: [PATCH 1/8] GODRIVER-3874 PoC for afterClusterTime. --- .../integration/unified/unified_spec_test.go | 1 + .../causal-consistency-clientBulkWrite.json | 146 ++ .../causal-consistency-clientBulkWrite.yml | 74 ++ .../causal-consistency-write-commands.json | 1183 +++++++++++++++++ .../causal-consistency-write-commands.yml | 401 ++++++ testdata/specifications | 2 +- x/mongo/driver/operation.go | 10 +- 7 files changed, 1815 insertions(+), 2 deletions(-) create mode 100644 testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.json create mode 100644 testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.yml create mode 100644 testdata/causal-consistency/tests/causal-consistency-write-commands.json create mode 100644 testdata/causal-consistency/tests/causal-consistency-write-commands.yml diff --git a/internal/integration/unified/unified_spec_test.go b/internal/integration/unified/unified_spec_test.go index f92a05386..8775b93bb 100644 --- a/internal/integration/unified/unified_spec_test.go +++ b/internal/integration/unified/unified_spec_test.go @@ -39,6 +39,7 @@ var ( "mongodb-handshake/tests/unified", "client-backpressure/tests", "transactions/tests/unified", + "../../causal-consistency/tests", } failDirectories = []string{ "unified-test-format/tests/valid-fail", diff --git a/testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.json b/testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.json new file mode 100644 index 000000000..e83f53775 --- /dev/null +++ b/testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.json @@ -0,0 +1,146 @@ +{ + "description": "causal consistency write commands include afterClusterTime", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "uriOptions": { + "retryWrites": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "causal-consistency-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0", + "sessionOptions": { + "causalConsistency": true + } + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "causal-consistency-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "clientBulkWrite includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "clientBulkWrite", + "object": "client0", + "arguments": { + "session": "session0", + "models": [ + { + "insertOne": { + "namespace": "causal-consistency-tests.test", + "document": { + "_id": 4 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "command": { + "bulkWrite": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + } + ] +} diff --git a/testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.yml b/testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.yml new file mode 100644 index 000000000..e908aa872 --- /dev/null +++ b/testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.yml @@ -0,0 +1,74 @@ +description: "causal consistency write commands include afterClusterTime" + +schemaVersion: "1.3" + +runOnRequirements: + - minServerVersion: "8.0" + topologies: [replicaset, sharded, load-balanced] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + uriOptions: + retryWrites: false + observeEvents: [commandStartedEvent] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName causal-consistency-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - session: + id: &session0 session0 + client: *client0 + sessionOptions: + causalConsistency: true + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +# In a causally consistent session, once an operationTime has been established by a prior +# operation, subsequent write commands MUST include readConcern.afterClusterTime so the +# server can apply the write causally after the previously-observed data. + +tests: + - description: "clientBulkWrite includes afterClusterTime in causally consistent session" + operations: + - name: findOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectResult: { _id: 1, x: 11 } + - name: clientBulkWrite + object: *client0 + arguments: + session: *session0 + models: + - insertOne: + namespace: causal-consistency-tests.test + document: { _id: 4 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: find + command: + find: *collectionName + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + - commandStartedEvent: + commandName: bulkWrite + command: + bulkWrite: 1 + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } diff --git a/testdata/causal-consistency/tests/causal-consistency-write-commands.json b/testdata/causal-consistency/tests/causal-consistency-write-commands.json new file mode 100644 index 000000000..f99582a3d --- /dev/null +++ b/testdata/causal-consistency/tests/causal-consistency-write-commands.json @@ -0,0 +1,1183 @@ +{ + "description": "causal consistency write commands include afterClusterTime", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "uriOptions": { + "retryWrites": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "causal-consistency-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0", + "sessionOptions": { + "causalConsistency": true + } + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "causal-consistency-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "insertOne includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 4 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 4 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "insert": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "insertMany includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "session": "session0", + "documents": [ + { + "_id": 4 + }, + { + "_id": 5 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "insert": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "updateOne includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 100 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "update": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "updateMany includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": { + "$gt": 0 + } + }, + "update": { + "$set": { + "updated": true + } + } + }, + "expectResult": { + "matchedCount": 3, + "modifiedCount": 3, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "update": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "replaceOne includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "replacement": { + "x": 100 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "update": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "deleteOne includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "delete": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "deleteMany includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": { + "$gt": 0 + } + } + }, + "expectResult": { + "deletedCount": 3 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "delete": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "findOneAndUpdate includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 100 + } + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "findAndModify": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "findOneAndDelete includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "findAndModify": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "findOneAndReplace includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "replacement": { + "x": 100 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "findAndModify": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "bulkWrite includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "session": "session0", + "requests": [ + { + "insertOne": { + "document": { + "_id": 4 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$set": { + "x": 100 + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 3 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "insert": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "update": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "delete": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "create includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "session": "session0", + "collection": "newcoll" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "create", + "command": { + "create": "newcoll", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "createIndexes includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "createIndex", + "object": "collection0", + "arguments": { + "session": "session0", + "keys": { + "x": 1 + }, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "createIndexes", + "command": { + "createIndexes": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "drop includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "session": "session0", + "collection": "test" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "drop", + "command": { + "drop": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "dropIndexes includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "findOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "dropIndexes", + "object": "collection0", + "arguments": { + "session": "session0" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "command": { + "dropIndexes": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + } + ] +} diff --git a/testdata/causal-consistency/tests/causal-consistency-write-commands.yml b/testdata/causal-consistency/tests/causal-consistency-write-commands.yml new file mode 100644 index 000000000..7cbc19c70 --- /dev/null +++ b/testdata/causal-consistency/tests/causal-consistency-write-commands.yml @@ -0,0 +1,401 @@ +description: "causal consistency write commands include afterClusterTime" + +schemaVersion: "1.3" + +runOnRequirements: + - topologies: [replicaset, sharded, load-balanced] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + uriOptions: + retryWrites: false + observeEvents: [commandStartedEvent] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName causal-consistency-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - session: + id: &session0 session0 + client: *client0 + sessionOptions: + causalConsistency: true + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +# In a causally consistent session, once an operationTime has been established by a prior +# operation, subsequent write commands MUST include readConcern.afterClusterTime so the +# server can apply the write causally after the previously-observed data. + +tests: + - description: "insertOne includes afterClusterTime in causally consistent session" + operations: + - &findOne + name: findOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectResult: { _id: 1, x: 11 } + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 4 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 4 } } + expectEvents: + - client: *client0 + events: + - &findEvent + commandStartedEvent: + commandName: find + command: + find: *collectionName + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + - commandStartedEvent: + commandName: insert + command: + insert: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + + - description: "insertMany includes afterClusterTime in causally consistent session" + operations: + - *findOne + - name: insertMany + object: *collection0 + arguments: + session: *session0 + documents: + - { _id: 4 } + - { _id: 5 } + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: insert + command: + insert: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + + - description: "updateOne includes afterClusterTime in causally consistent session" + operations: + - *findOne + - name: updateOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + update: { $set: { x: 100 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: update + command: + update: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + + - description: "updateMany includes afterClusterTime in causally consistent session" + operations: + - *findOne + - name: updateMany + object: *collection0 + arguments: + session: *session0 + filter: { _id: { $gt: 0 } } + update: { $set: { updated: true } } + expectResult: + matchedCount: 3 + modifiedCount: 3 + upsertedCount: 0 + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: update + command: + update: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + + - description: "replaceOne includes afterClusterTime in causally consistent session" + operations: + - *findOne + - name: replaceOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + replacement: { x: 100 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: update + command: + update: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + + - description: "deleteOne includes afterClusterTime in causally consistent session" + operations: + - *findOne + - name: deleteOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectResult: + deletedCount: 1 + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: delete + command: + delete: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + + - description: "deleteMany includes afterClusterTime in causally consistent session" + operations: + - *findOne + - name: deleteMany + object: *collection0 + arguments: + session: *session0 + filter: { _id: { $gt: 0 } } + expectResult: + deletedCount: 3 + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: delete + command: + delete: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + + - description: "findOneAndUpdate includes afterClusterTime in causally consistent session" + operations: + - *findOne + - name: findOneAndUpdate + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + update: { $set: { x: 100 } } + expectResult: { _id: 1, x: 11 } + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: findAndModify + command: + findAndModify: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + + - description: "findOneAndDelete includes afterClusterTime in causally consistent session" + operations: + - *findOne + - name: findOneAndDelete + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectResult: { _id: 1, x: 11 } + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: findAndModify + command: + findAndModify: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + + - description: "findOneAndReplace includes afterClusterTime in causally consistent session" + operations: + - *findOne + - name: findOneAndReplace + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + replacement: { x: 100 } + expectResult: { _id: 1, x: 11 } + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: findAndModify + command: + findAndModify: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + + - description: "bulkWrite includes afterClusterTime in causally consistent session" + operations: + - *findOne + - name: bulkWrite + object: *collection0 + arguments: + session: *session0 + requests: + - insertOne: + document: { _id: 4 } + - updateOne: + filter: { _id: 2 } + update: { $set: { x: 100 } } + - deleteOne: + filter: { _id: 3 } + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: insert + command: + insert: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + - commandStartedEvent: + commandName: update + command: + update: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + - commandStartedEvent: + commandName: delete + command: + delete: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + + - description: "create includes afterClusterTime in causally consistent session" + operations: + - *findOne + - name: createCollection + object: *database0 + arguments: + session: *session0 + collection: &newCollectionName newcoll + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: create + command: + create: *newCollectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + + - description: "createIndexes includes afterClusterTime in causally consistent session" + operations: + - *findOne + - name: createIndex + object: *collection0 + arguments: + session: *session0 + keys: { x: 1 } + name: x_1 + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: createIndexes + command: + createIndexes: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + + - description: "drop includes afterClusterTime in causally consistent session" + operations: + - *findOne + - name: dropCollection + object: *database0 + arguments: + session: *session0 + collection: *collectionName + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: drop + command: + drop: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + + - description: "dropIndexes includes afterClusterTime in causally consistent session" + operations: + - *findOne + - name: dropIndexes + object: *collection0 + arguments: + session: *session0 + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: dropIndexes + command: + dropIndexes: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } diff --git a/testdata/specifications b/testdata/specifications index b519824da..0f48bb56c 160000 --- a/testdata/specifications +++ b/testdata/specifications @@ -1 +1 @@ -Subproject commit b519824da64005cdf99ca680fc49c4e278af0ef3 +Subproject commit 0f48bb56c2046528554aa0899b2bf661bbd4a7bb diff --git a/x/mongo/driver/operation.go b/x/mongo/driver/operation.go index b8be12ebb..665d73908 100644 --- a/x/mongo/driver/operation.go +++ b/x/mongo/driver/operation.go @@ -1590,9 +1590,17 @@ func (op Operation) addReadConcern(dst []byte, desc description.SelectedServer) rc = readconcern.Snapshot() } + causalConsistency := client != nil && client.Consistent + _, data, err := MarshalBSONReadConcern(rc) // always returns a document if errors.Is(err, ErrEmptyReadConcern) { - return dst, nil + if !causalConsistency { + return dst, nil + } + err = nil + if data == nil { + data = bsoncore.NewDocumentBuilder().Build() + } } if err != nil { From de2ac755025dad32fefdb643eed374d21e1df6ef Mon Sep 17 00:00:00 2001 From: Matt Dale <9760375+matthewdale@users.noreply.github.com> Date: Wed, 6 May 2026 09:58:06 -0700 Subject: [PATCH 2/8] Correct logic for adding afterClusterTime to write ops. --- x/mongo/driver/operation.go | 25 ++++++++++++++++--------- x/mongo/driver/operation/create.go | 1 + 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/x/mongo/driver/operation.go b/x/mongo/driver/operation.go index 665d73908..d4413b5e4 100644 --- a/x/mongo/driver/operation.go +++ b/x/mongo/driver/operation.go @@ -1583,6 +1583,21 @@ func (op Operation) addReadConcern(dst []byte, desc description.SelectedServer) rc = &readconcern.ReadConcern{} } + // If this is a write operation (which in this case, includes database, + // collection, and index modification operations), then we add an empty read + // concern so the following code can set "afterClusterTime". That avoids a + // data correctness problem that can happen when there is a network + // partition in a sharded cluster. See DRIVERS-3274 for more details. + isWrite := op.Type == Write || + op.Name == driverutil.CreateOp || + op.Name == driverutil.CreateIndexesOp || + op.Name == driverutil.DropOp || + op.Name == driverutil.DropDatabaseOp || + op.Name == driverutil.DropIndexesOp + if rc == nil && client != nil && !client.TransactionRunning() && client.Consistent && client.OperationTime != nil && isWrite { + rc = &readconcern.ReadConcern{} + } + if client != nil && client.Snapshot { if desc.WireVersion.Max < readSnapshotMinWireVersion { return dst, errors.New("snapshot reads require MongoDB 5.0 or later") @@ -1590,17 +1605,9 @@ func (op Operation) addReadConcern(dst []byte, desc description.SelectedServer) rc = readconcern.Snapshot() } - causalConsistency := client != nil && client.Consistent - _, data, err := MarshalBSONReadConcern(rc) // always returns a document if errors.Is(err, ErrEmptyReadConcern) { - if !causalConsistency { - return dst, nil - } - err = nil - if data == nil { - data = bsoncore.NewDocumentBuilder().Build() - } + return dst, nil } if err != nil { diff --git a/x/mongo/driver/operation/create.go b/x/mongo/driver/operation/create.go index f9a406dbc..c1d4f8479 100644 --- a/x/mongo/driver/operation/create.go +++ b/x/mongo/driver/operation/create.go @@ -80,6 +80,7 @@ func (c *Create) Execute(ctx context.Context) error { WriteConcern: c.writeConcern, ServerAPI: c.serverAPI, Authenticator: c.authenticator, + Name: driverutil.CreateOp, }.Execute(ctx) } From 5d687502bc6cd5cdf81b6afcf63c907fbcb7b9a5 Mon Sep 17 00:00:00 2001 From: Matt Dale <9760375+matthewdale@users.noreply.github.com> Date: Thu, 7 May 2026 00:58:36 -0700 Subject: [PATCH 3/8] Fix which write ops get afterClusterTime. Update spec submodule to point at fork. --- .gitmodules | 4 +- .../integration/unified/unified_spec_test.go | 2 +- .../causal-consistency-clientBulkWrite.json | 146 -- .../causal-consistency-clientBulkWrite.yml | 74 -- .../causal-consistency-write-commands.json | 1183 ----------------- .../causal-consistency-write-commands.yml | 401 ------ testdata/specifications | 2 +- x/mongo/driver/operation.go | 21 +- 8 files changed, 18 insertions(+), 1815 deletions(-) delete mode 100644 testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.json delete mode 100644 testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.yml delete mode 100644 testdata/causal-consistency/tests/causal-consistency-write-commands.json delete mode 100644 testdata/causal-consistency/tests/causal-consistency-write-commands.yml diff --git a/.gitmodules b/.gitmodules index e3b5c7b3e..0aa121ccb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "specifications"] +[submodule "testdata/specifications"] path = testdata/specifications - url = https://github.com/mongodb/specifications + url = git@github.com:matthewdale/specifications.git diff --git a/internal/integration/unified/unified_spec_test.go b/internal/integration/unified/unified_spec_test.go index 8775b93bb..293fb4a5f 100644 --- a/internal/integration/unified/unified_spec_test.go +++ b/internal/integration/unified/unified_spec_test.go @@ -39,7 +39,7 @@ var ( "mongodb-handshake/tests/unified", "client-backpressure/tests", "transactions/tests/unified", - "../../causal-consistency/tests", + "causal-consistency/tests", } failDirectories = []string{ "unified-test-format/tests/valid-fail", diff --git a/testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.json b/testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.json deleted file mode 100644 index e83f53775..000000000 --- a/testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.json +++ /dev/null @@ -1,146 +0,0 @@ -{ - "description": "causal consistency write commands include afterClusterTime", - "schemaVersion": "1.3", - "runOnRequirements": [ - { - "minServerVersion": "8.0", - "topologies": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false, - "uriOptions": { - "retryWrites": false - }, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "causal-consistency-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - }, - { - "session": { - "id": "session0", - "client": "client0", - "sessionOptions": { - "causalConsistency": true - } - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "causal-consistency-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "clientBulkWrite includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "clientBulkWrite", - "object": "client0", - "arguments": { - "session": "session0", - "models": [ - { - "insertOne": { - "namespace": "causal-consistency-tests.test", - "document": { - "_id": 4 - } - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "bulkWrite", - "command": { - "bulkWrite": 1, - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - } - ] -} diff --git a/testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.yml b/testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.yml deleted file mode 100644 index e908aa872..000000000 --- a/testdata/causal-consistency/tests/causal-consistency-clientBulkWrite.yml +++ /dev/null @@ -1,74 +0,0 @@ -description: "causal consistency write commands include afterClusterTime" - -schemaVersion: "1.3" - -runOnRequirements: - - minServerVersion: "8.0" - topologies: [replicaset, sharded, load-balanced] - -createEntities: - - client: - id: &client0 client0 - useMultipleMongoses: false - uriOptions: - retryWrites: false - observeEvents: [commandStartedEvent] - - database: - id: &database0 database0 - client: *client0 - databaseName: &databaseName causal-consistency-tests - - collection: - id: &collection0 collection0 - database: *database0 - collectionName: &collectionName test - - session: - id: &session0 session0 - client: *client0 - sessionOptions: - causalConsistency: true - -initialData: - - collectionName: *collectionName - databaseName: *databaseName - documents: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - -# In a causally consistent session, once an operationTime has been established by a prior -# operation, subsequent write commands MUST include readConcern.afterClusterTime so the -# server can apply the write causally after the previously-observed data. - -tests: - - description: "clientBulkWrite includes afterClusterTime in causally consistent session" - operations: - - name: findOne - object: *collection0 - arguments: - session: *session0 - filter: { _id: 1 } - expectResult: { _id: 1, x: 11 } - - name: clientBulkWrite - object: *client0 - arguments: - session: *session0 - models: - - insertOne: - namespace: causal-consistency-tests.test - document: { _id: 4 } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - commandName: find - command: - find: *collectionName - readConcern: { $$exists: false } - lsid: { $$sessionLsid: *session0 } - - commandStartedEvent: - commandName: bulkWrite - command: - bulkWrite: 1 - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } diff --git a/testdata/causal-consistency/tests/causal-consistency-write-commands.json b/testdata/causal-consistency/tests/causal-consistency-write-commands.json deleted file mode 100644 index f99582a3d..000000000 --- a/testdata/causal-consistency/tests/causal-consistency-write-commands.json +++ /dev/null @@ -1,1183 +0,0 @@ -{ - "description": "causal consistency write commands include afterClusterTime", - "schemaVersion": "1.3", - "runOnRequirements": [ - { - "topologies": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false, - "uriOptions": { - "retryWrites": false - }, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "causal-consistency-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - }, - { - "session": { - "id": "session0", - "client": "client0", - "sessionOptions": { - "causalConsistency": true - } - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "causal-consistency-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "insertOne includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 4 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 4 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "insert", - "command": { - "insert": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "insertMany includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "insertMany", - "object": "collection0", - "arguments": { - "session": "session0", - "documents": [ - { - "_id": 4 - }, - { - "_id": 5 - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "insert", - "command": { - "insert": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "updateOne includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 100 - } - } - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "update", - "command": { - "update": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "updateMany includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "updateMany", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gt": 0 - } - }, - "update": { - "$set": { - "updated": true - } - } - }, - "expectResult": { - "matchedCount": 3, - "modifiedCount": 3, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "update", - "command": { - "update": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "replaceOne includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "replaceOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "replacement": { - "x": 100 - } - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "update", - "command": { - "update": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "deleteOne includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "deleteOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "deletedCount": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "delete", - "command": { - "delete": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "deleteMany includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "deleteMany", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gt": 0 - } - } - }, - "expectResult": { - "deletedCount": 3 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "delete", - "command": { - "delete": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "findOneAndUpdate includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 100 - } - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "findAndModify", - "command": { - "findAndModify": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "findOneAndDelete includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "findOneAndDelete", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "findAndModify", - "command": { - "findAndModify": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "findOneAndReplace includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "findOneAndReplace", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "replacement": { - "x": 100 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "findAndModify", - "command": { - "findAndModify": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "bulkWrite includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "session": "session0", - "requests": [ - { - "insertOne": { - "document": { - "_id": 4 - } - } - }, - { - "updateOne": { - "filter": { - "_id": 2 - }, - "update": { - "$set": { - "x": 100 - } - } - } - }, - { - "deleteOne": { - "filter": { - "_id": 3 - } - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "insert", - "command": { - "insert": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "update", - "command": { - "update": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "delete", - "command": { - "delete": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "create includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "createCollection", - "object": "database0", - "arguments": { - "session": "session0", - "collection": "newcoll" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "create", - "command": { - "create": "newcoll", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "createIndexes includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "createIndex", - "object": "collection0", - "arguments": { - "session": "session0", - "keys": { - "x": 1 - }, - "name": "x_1" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "createIndexes", - "command": { - "createIndexes": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "drop includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "session": "session0", - "collection": "test" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "drop", - "command": { - "drop": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "dropIndexes includes afterClusterTime in causally consistent session", - "operations": [ - { - "name": "findOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - }, - { - "name": "dropIndexes", - "object": "collection0", - "arguments": { - "session": "session0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "command": { - "find": "test", - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "dropIndexes", - "command": { - "dropIndexes": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "readConcern": { - "afterClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - } - ] -} diff --git a/testdata/causal-consistency/tests/causal-consistency-write-commands.yml b/testdata/causal-consistency/tests/causal-consistency-write-commands.yml deleted file mode 100644 index 7cbc19c70..000000000 --- a/testdata/causal-consistency/tests/causal-consistency-write-commands.yml +++ /dev/null @@ -1,401 +0,0 @@ -description: "causal consistency write commands include afterClusterTime" - -schemaVersion: "1.3" - -runOnRequirements: - - topologies: [replicaset, sharded, load-balanced] - -createEntities: - - client: - id: &client0 client0 - useMultipleMongoses: false - uriOptions: - retryWrites: false - observeEvents: [commandStartedEvent] - - database: - id: &database0 database0 - client: *client0 - databaseName: &databaseName causal-consistency-tests - - collection: - id: &collection0 collection0 - database: *database0 - collectionName: &collectionName test - - session: - id: &session0 session0 - client: *client0 - sessionOptions: - causalConsistency: true - -initialData: - - collectionName: *collectionName - databaseName: *databaseName - documents: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - -# In a causally consistent session, once an operationTime has been established by a prior -# operation, subsequent write commands MUST include readConcern.afterClusterTime so the -# server can apply the write causally after the previously-observed data. - -tests: - - description: "insertOne includes afterClusterTime in causally consistent session" - operations: - - &findOne - name: findOne - object: *collection0 - arguments: - session: *session0 - filter: { _id: 1 } - expectResult: { _id: 1, x: 11 } - - name: insertOne - object: *collection0 - arguments: - session: *session0 - document: { _id: 4 } - expectResult: - $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 4 } } - expectEvents: - - client: *client0 - events: - - &findEvent - commandStartedEvent: - commandName: find - command: - find: *collectionName - readConcern: { $$exists: false } - lsid: { $$sessionLsid: *session0 } - - commandStartedEvent: - commandName: insert - command: - insert: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - - description: "insertMany includes afterClusterTime in causally consistent session" - operations: - - *findOne - - name: insertMany - object: *collection0 - arguments: - session: *session0 - documents: - - { _id: 4 } - - { _id: 5 } - expectEvents: - - client: *client0 - events: - - *findEvent - - commandStartedEvent: - commandName: insert - command: - insert: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - - description: "updateOne includes afterClusterTime in causally consistent session" - operations: - - *findOne - - name: updateOne - object: *collection0 - arguments: - session: *session0 - filter: { _id: 1 } - update: { $set: { x: 100 } } - expectResult: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - expectEvents: - - client: *client0 - events: - - *findEvent - - commandStartedEvent: - commandName: update - command: - update: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - - description: "updateMany includes afterClusterTime in causally consistent session" - operations: - - *findOne - - name: updateMany - object: *collection0 - arguments: - session: *session0 - filter: { _id: { $gt: 0 } } - update: { $set: { updated: true } } - expectResult: - matchedCount: 3 - modifiedCount: 3 - upsertedCount: 0 - expectEvents: - - client: *client0 - events: - - *findEvent - - commandStartedEvent: - commandName: update - command: - update: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - - description: "replaceOne includes afterClusterTime in causally consistent session" - operations: - - *findOne - - name: replaceOne - object: *collection0 - arguments: - session: *session0 - filter: { _id: 1 } - replacement: { x: 100 } - expectResult: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - expectEvents: - - client: *client0 - events: - - *findEvent - - commandStartedEvent: - commandName: update - command: - update: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - - description: "deleteOne includes afterClusterTime in causally consistent session" - operations: - - *findOne - - name: deleteOne - object: *collection0 - arguments: - session: *session0 - filter: { _id: 1 } - expectResult: - deletedCount: 1 - expectEvents: - - client: *client0 - events: - - *findEvent - - commandStartedEvent: - commandName: delete - command: - delete: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - - description: "deleteMany includes afterClusterTime in causally consistent session" - operations: - - *findOne - - name: deleteMany - object: *collection0 - arguments: - session: *session0 - filter: { _id: { $gt: 0 } } - expectResult: - deletedCount: 3 - expectEvents: - - client: *client0 - events: - - *findEvent - - commandStartedEvent: - commandName: delete - command: - delete: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - - description: "findOneAndUpdate includes afterClusterTime in causally consistent session" - operations: - - *findOne - - name: findOneAndUpdate - object: *collection0 - arguments: - session: *session0 - filter: { _id: 1 } - update: { $set: { x: 100 } } - expectResult: { _id: 1, x: 11 } - expectEvents: - - client: *client0 - events: - - *findEvent - - commandStartedEvent: - commandName: findAndModify - command: - findAndModify: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - - description: "findOneAndDelete includes afterClusterTime in causally consistent session" - operations: - - *findOne - - name: findOneAndDelete - object: *collection0 - arguments: - session: *session0 - filter: { _id: 1 } - expectResult: { _id: 1, x: 11 } - expectEvents: - - client: *client0 - events: - - *findEvent - - commandStartedEvent: - commandName: findAndModify - command: - findAndModify: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - - description: "findOneAndReplace includes afterClusterTime in causally consistent session" - operations: - - *findOne - - name: findOneAndReplace - object: *collection0 - arguments: - session: *session0 - filter: { _id: 1 } - replacement: { x: 100 } - expectResult: { _id: 1, x: 11 } - expectEvents: - - client: *client0 - events: - - *findEvent - - commandStartedEvent: - commandName: findAndModify - command: - findAndModify: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - - description: "bulkWrite includes afterClusterTime in causally consistent session" - operations: - - *findOne - - name: bulkWrite - object: *collection0 - arguments: - session: *session0 - requests: - - insertOne: - document: { _id: 4 } - - updateOne: - filter: { _id: 2 } - update: { $set: { x: 100 } } - - deleteOne: - filter: { _id: 3 } - expectEvents: - - client: *client0 - events: - - *findEvent - - commandStartedEvent: - commandName: insert - command: - insert: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - commandStartedEvent: - commandName: update - command: - update: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - commandStartedEvent: - commandName: delete - command: - delete: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - - description: "create includes afterClusterTime in causally consistent session" - operations: - - *findOne - - name: createCollection - object: *database0 - arguments: - session: *session0 - collection: &newCollectionName newcoll - expectEvents: - - client: *client0 - events: - - *findEvent - - commandStartedEvent: - commandName: create - command: - create: *newCollectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - - description: "createIndexes includes afterClusterTime in causally consistent session" - operations: - - *findOne - - name: createIndex - object: *collection0 - arguments: - session: *session0 - keys: { x: 1 } - name: x_1 - expectEvents: - - client: *client0 - events: - - *findEvent - - commandStartedEvent: - commandName: createIndexes - command: - createIndexes: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - - description: "drop includes afterClusterTime in causally consistent session" - operations: - - *findOne - - name: dropCollection - object: *database0 - arguments: - session: *session0 - collection: *collectionName - expectEvents: - - client: *client0 - events: - - *findEvent - - commandStartedEvent: - commandName: drop - command: - drop: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } - - - description: "dropIndexes includes afterClusterTime in causally consistent session" - operations: - - *findOne - - name: dropIndexes - object: *collection0 - arguments: - session: *session0 - expectEvents: - - client: *client0 - events: - - *findEvent - - commandStartedEvent: - commandName: dropIndexes - command: - dropIndexes: *collectionName - lsid: { $$sessionLsid: *session0 } - readConcern: - afterClusterTime: { $$exists: true } diff --git a/testdata/specifications b/testdata/specifications index 0f48bb56c..c4c9860d3 160000 --- a/testdata/specifications +++ b/testdata/specifications @@ -1 +1 @@ -Subproject commit 0f48bb56c2046528554aa0899b2bf661bbd4a7bb +Subproject commit c4c9860d31dae1608cb3dd2dad48e20f71f6d73f diff --git a/x/mongo/driver/operation.go b/x/mongo/driver/operation.go index d4413b5e4..50e97609e 100644 --- a/x/mongo/driver/operation.go +++ b/x/mongo/driver/operation.go @@ -1583,18 +1583,25 @@ func (op Operation) addReadConcern(dst []byte, desc description.SelectedServer) rc = &readconcern.ReadConcern{} } - // If this is a write operation (which in this case, includes database, - // collection, and index modification operations), then we add an empty read - // concern so the following code can set "afterClusterTime". That avoids a - // data correctness problem that can happen when there is a network - // partition in a sharded cluster. See DRIVERS-3274 for more details. - isWrite := op.Type == Write || + // If this is a write operation, then we add an empty read concern so the + // following code can set "afterClusterTime". That avoids a data correctness + // problem that can happen when there is a network partition in a sharded + // cluster. See DRIVERS-3274 for more details. + isWrite := op.Name == driverutil.InsertOp || + op.Name == driverutil.UpdateOp || + op.Name == driverutil.FindAndModifyOp || + op.Name == driverutil.DeleteOp || + op.Name == driverutil.BulkWriteOp || op.Name == driverutil.CreateOp || op.Name == driverutil.CreateIndexesOp || op.Name == driverutil.DropOp || op.Name == driverutil.DropDatabaseOp || op.Name == driverutil.DropIndexesOp - if rc == nil && client != nil && !client.TransactionRunning() && client.Consistent && client.OperationTime != nil && isWrite { + isNotInTxn := client != nil && (client.TransactionState == session.None || + client.TransactionState == session.Committed || + client.TransactionState == session.Aborted || + client.TransactionState == session.Starting) + if rc == nil && client != nil && client.Consistent && client.OperationTime != nil && isWrite && isNotInTxn { rc = &readconcern.ReadConcern{} } From 84a44786054ad8e00fe7b13624924486544230a3 Mon Sep 17 00:00:00 2001 From: Matt Dale <9760375+matthewdale@users.noreply.github.com> Date: Thu, 7 May 2026 08:46:46 -0700 Subject: [PATCH 4/8] Use public URL for submodule. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 0aa121ccb..db52c2b66 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "testdata/specifications"] path = testdata/specifications - url = git@github.com:matthewdale/specifications.git + url = https://github.com/matthewdale/specifications.git From cb29895aa75eb0bb2f85e34cb0d4a07a6b74fc05 Mon Sep 17 00:00:00 2001 From: Matt Dale <9760375+matthewdale@users.noreply.github.com> Date: Thu, 7 May 2026 12:20:12 -0700 Subject: [PATCH 5/8] Update spec submodule. --- testdata/specifications | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/specifications b/testdata/specifications index c4c9860d3..51c492009 160000 --- a/testdata/specifications +++ b/testdata/specifications @@ -1 +1 @@ -Subproject commit c4c9860d31dae1608cb3dd2dad48e20f71f6d73f +Subproject commit 51c492009a87b8162b31da1fd75af4b2c11cdb54 From d3fd64028610e2c3bc9bc74fa9c85b89bce7cc29 Mon Sep 17 00:00:00 2001 From: Matt Dale <9760375+matthewdale@users.noreply.github.com> Date: Thu, 7 May 2026 16:50:37 -0700 Subject: [PATCH 6/8] Don't send afterClusterTime on ops in a transaction. --- testdata/specifications | 2 +- x/mongo/driver/operation.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/testdata/specifications b/testdata/specifications index 51c492009..c5b9d0475 160000 --- a/testdata/specifications +++ b/testdata/specifications @@ -1 +1 @@ -Subproject commit 51c492009a87b8162b31da1fd75af4b2c11cdb54 +Subproject commit c5b9d047595a5e5bc628eb44ef225398c4a9cd67 diff --git a/x/mongo/driver/operation.go b/x/mongo/driver/operation.go index 50e97609e..a0014befc 100644 --- a/x/mongo/driver/operation.go +++ b/x/mongo/driver/operation.go @@ -1599,8 +1599,7 @@ func (op Operation) addReadConcern(dst []byte, desc description.SelectedServer) op.Name == driverutil.DropIndexesOp isNotInTxn := client != nil && (client.TransactionState == session.None || client.TransactionState == session.Committed || - client.TransactionState == session.Aborted || - client.TransactionState == session.Starting) + client.TransactionState == session.Aborted) if rc == nil && client != nil && client.Consistent && client.OperationTime != nil && isWrite && isNotInTxn { rc = &readconcern.ReadConcern{} } From f2f6c9eaebd243f63fdc3fbae795e086a0809b30 Mon Sep 17 00:00:00 2001 From: Matt Dale <9760375+matthewdale@users.noreply.github.com> Date: Fri, 8 May 2026 17:02:50 -0700 Subject: [PATCH 7/8] Update spec submodule. --- testdata/specifications | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/specifications b/testdata/specifications index c5b9d0475..fa869a33d 160000 --- a/testdata/specifications +++ b/testdata/specifications @@ -1 +1 @@ -Subproject commit c5b9d047595a5e5bc628eb44ef225398c4a9cd67 +Subproject commit fa869a33ddb716f63dc9195f5f3e201ca98064ce From 7466f2e711f42bf14e0d6ba58e219ee4603e0c1b Mon Sep 17 00:00:00 2001 From: Matt Dale <9760375+matthewdale@users.noreply.github.com> Date: Fri, 29 May 2026 09:22:45 -0700 Subject: [PATCH 8/8] Add dropDatabase unified spec runner command and update submodule. --- .../unified/database_operation_execution.go | 27 +++++++++++++++++++ internal/integration/unified/operation.go | 2 ++ testdata/specifications | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/internal/integration/unified/database_operation_execution.go b/internal/integration/unified/database_operation_execution.go index 940bf3733..2bf8d391f 100644 --- a/internal/integration/unified/database_operation_execution.go +++ b/internal/integration/unified/database_operation_execution.go @@ -185,6 +185,33 @@ func executeDropCollection(ctx context.Context, operation *operation) (*operatio return newErrorResult(err), nil } +func executeDropDatabase(ctx context.Context, operation *operation) (*operationResult, error) { + client, err := entities(ctx).client(operation.Object) + if err != nil { + return nil, err + } + + var dbName string + elems, _ := operation.Arguments.Elements() + for _, elem := range elems { + key := elem.Key() + val := elem.Value() + + switch key { + case "database": + dbName = val.StringValue() + default: + return nil, fmt.Errorf("unrecognized dropDatabase option %q", key) + } + } + if dbName == "" { + return nil, newMissingArgumentError("database") + } + + err = client.Database(dbName).Drop(ctx) + return newErrorResult(err), nil +} + func executeListCollections(ctx context.Context, operation *operation) (*operationResult, error) { db, err := entities(ctx).database(operation.Object) if err != nil { diff --git a/internal/integration/unified/operation.go b/internal/integration/unified/operation.go index 41df0430c..d90ec4aad 100644 --- a/internal/integration/unified/operation.go +++ b/internal/integration/unified/operation.go @@ -150,6 +150,8 @@ func (op *operation) run(ctx context.Context, loopDone <-chan struct{}) (*operat return executeCreateCollection(ctx, op) case "dropCollection": return executeDropCollection(ctx, op) + case "dropDatabase": + return executeDropDatabase(ctx, op) case "listCollections": return executeListCollections(ctx, op) case "listCollectionNames": diff --git a/testdata/specifications b/testdata/specifications index fa869a33d..8ab62d639 160000 --- a/testdata/specifications +++ b/testdata/specifications @@ -1 +1 @@ -Subproject commit fa869a33ddb716f63dc9195f5f3e201ca98064ce +Subproject commit 8ab62d639054396bd0d784791bd3683f16af0747