From 119ebac751f8e06c3fe0a868122d4fc2668ce3c3 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Tue, 9 Dec 2025 16:33:58 -0500 Subject: [PATCH 01/11] Add OpenTelemetry specification tests --- .../open-telemetry/operation/aggregate.json | 127 ++++ .../open-telemetry/operation/aggregate.yml | 62 ++ .../operation/atlas_search.json | 317 ++++++++++ .../open-telemetry/operation/atlas_search.yml | 170 ++++++ .../open-telemetry/operation/bulk_write.json | 333 +++++++++++ .../open-telemetry/operation/bulk_write.yml | 198 +++++++ .../open-telemetry/operation/count.json | 123 ++++ .../open-telemetry/operation/count.yml | 63 ++ .../operation/create_collection.json | 110 ++++ .../operation/create_collection.yml | 55 ++ .../operation/create_indexes.json | 127 ++++ .../operation/create_indexes.yml | 60 ++ .../open-telemetry/operation/delete.json | 132 +++++ .../open-telemetry/operation/delete.yml | 62 ++ .../open-telemetry/operation/distinct.json | 127 ++++ .../open-telemetry/operation/distinct.yml | 66 +++ .../operation/drop_collection.json | 122 ++++ .../operation/drop_collection.yml | 65 +++ .../operation/drop_indexes.json | 145 +++++ .../open-telemetry/operation/drop_indexes.yml | 78 +++ .../open-telemetry/operation/find.json | 291 ++++++++++ .../open-telemetry/operation/find.yml | 143 +++++ .../operation/find_and_modify.json | 143 +++++ .../operation/find_and_modify.yml | 70 +++ .../operation/find_without_query_text.json | 119 ++++ .../operation/find_without_query_text.yml | 58 ++ .../open-telemetry/operation/insert.json | 146 +++++ .../open-telemetry/operation/insert.yml | 72 +++ .../operation/list_collections.json | 108 ++++ .../operation/list_collections.yml | 53 ++ .../operation/list_databases.json | 104 ++++ .../operation/list_databases.yml | 50 ++ .../operation/list_indexes.json | 118 ++++ .../open-telemetry/operation/list_indexes.yml | 61 ++ .../open-telemetry/operation/map_reduce.json | 177 ++++++ .../open-telemetry/operation/map_reduce.yml | 99 ++++ .../open-telemetry/operation/retries.json | 209 +++++++ .../open-telemetry/operation/retries.yml | 105 ++++ .../open-telemetry/operation/update.json | 140 +++++ .../open-telemetry/operation/update.yml | 66 +++ .../transaction/convenient.json | 326 +++++++++++ .../open-telemetry/transaction/convenient.yml | 167 ++++++ .../open-telemetry/transaction/core_api.json | 545 ++++++++++++++++++ .../open-telemetry/transaction/core_api.yml | 269 +++++++++ 44 files changed, 6181 insertions(+) create mode 100644 specifications/open-telemetry/operation/aggregate.json create mode 100644 specifications/open-telemetry/operation/aggregate.yml create mode 100644 specifications/open-telemetry/operation/atlas_search.json create mode 100644 specifications/open-telemetry/operation/atlas_search.yml create mode 100644 specifications/open-telemetry/operation/bulk_write.json create mode 100644 specifications/open-telemetry/operation/bulk_write.yml create mode 100644 specifications/open-telemetry/operation/count.json create mode 100644 specifications/open-telemetry/operation/count.yml create mode 100644 specifications/open-telemetry/operation/create_collection.json create mode 100644 specifications/open-telemetry/operation/create_collection.yml create mode 100644 specifications/open-telemetry/operation/create_indexes.json create mode 100644 specifications/open-telemetry/operation/create_indexes.yml create mode 100644 specifications/open-telemetry/operation/delete.json create mode 100644 specifications/open-telemetry/operation/delete.yml create mode 100644 specifications/open-telemetry/operation/distinct.json create mode 100644 specifications/open-telemetry/operation/distinct.yml create mode 100644 specifications/open-telemetry/operation/drop_collection.json create mode 100644 specifications/open-telemetry/operation/drop_collection.yml create mode 100644 specifications/open-telemetry/operation/drop_indexes.json create mode 100644 specifications/open-telemetry/operation/drop_indexes.yml create mode 100644 specifications/open-telemetry/operation/find.json create mode 100644 specifications/open-telemetry/operation/find.yml create mode 100644 specifications/open-telemetry/operation/find_and_modify.json create mode 100644 specifications/open-telemetry/operation/find_and_modify.yml create mode 100644 specifications/open-telemetry/operation/find_without_query_text.json create mode 100644 specifications/open-telemetry/operation/find_without_query_text.yml create mode 100644 specifications/open-telemetry/operation/insert.json create mode 100644 specifications/open-telemetry/operation/insert.yml create mode 100644 specifications/open-telemetry/operation/list_collections.json create mode 100644 specifications/open-telemetry/operation/list_collections.yml create mode 100644 specifications/open-telemetry/operation/list_databases.json create mode 100644 specifications/open-telemetry/operation/list_databases.yml create mode 100644 specifications/open-telemetry/operation/list_indexes.json create mode 100644 specifications/open-telemetry/operation/list_indexes.yml create mode 100644 specifications/open-telemetry/operation/map_reduce.json create mode 100644 specifications/open-telemetry/operation/map_reduce.yml create mode 100644 specifications/open-telemetry/operation/retries.json create mode 100644 specifications/open-telemetry/operation/retries.yml create mode 100644 specifications/open-telemetry/operation/update.json create mode 100644 specifications/open-telemetry/operation/update.yml create mode 100644 specifications/open-telemetry/transaction/convenient.json create mode 100644 specifications/open-telemetry/transaction/convenient.yml create mode 100644 specifications/open-telemetry/transaction/core_api.json create mode 100644 specifications/open-telemetry/transaction/core_api.yml diff --git a/specifications/open-telemetry/operation/aggregate.json b/specifications/open-telemetry/operation/aggregate.json new file mode 100644 index 00000000000..ac42cc8be65 --- /dev/null +++ b/specifications/open-telemetry/operation/aggregate.json @@ -0,0 +1,127 @@ +{ + "description": "operation aggregate", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-aggregate" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "aggregation", + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ] + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "aggregate operation-aggregate.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-aggregate", + "db.collection.name": "test", + "db.operation.name": "aggregate", + "db.operation.summary": "aggregate operation-aggregate.test" + }, + "nested": [ + { + "name": "aggregate", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-aggregate", + "db.collection.name": "test", + "db.command.name": "aggregate", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "aggregate operation-aggregate.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "aggregate": "test", + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ] + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/aggregate.yml b/specifications/open-telemetry/operation/aggregate.yml new file mode 100644 index 00000000000..630000f4414 --- /dev/null +++ b/specifications/open-telemetry/operation/aggregate.yml @@ -0,0 +1,62 @@ +description: operation aggregate +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-aggregate + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName0 test + +tests: + - description: aggregation + operations: + - name: aggregate + object: *collection0 + arguments: + pipeline: &pipeline0 + - $match: { _id: 1 } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: aggregate operation-aggregate.test + attributes: + db.system: mongodb + db.namespace: operation-aggregate + db.collection.name: test + db.operation.name: aggregate + db.operation.summary: aggregate operation-aggregate.test + + nested: + - name: aggregate + attributes: + db.system: mongodb + db.namespace: operation-aggregate + db.collection.name: *collectionName0 + db.command.name: aggregate + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: aggregate operation-aggregate.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + aggregate: test + pipeline: *pipeline0 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/atlas_search.json b/specifications/open-telemetry/operation/atlas_search.json new file mode 100644 index 00000000000..0f236c2bed1 --- /dev/null +++ b/specifications/open-telemetry/operation/atlas_search.json @@ -0,0 +1,317 @@ +{ + "description": "operation atlas_search", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-atlas-search" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "7.0.5", + "maxServerVersion": "7.0.99", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + }, + { + "minServerVersion": "7.2.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "atlas search indexes", + "operations": [ + { + "name": "createSearchIndex", + "object": "collection0", + "arguments": { + "model": { + "definition": { + "mappings": { + "dynamic": true + } + }, + "type": "search" + } + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + }, + { + "name": "updateSearchIndex", + "object": "collection0", + "arguments": { + "name": "test index", + "definition": {} + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + }, + { + "name": "dropSearchIndex", + "object": "collection0", + "arguments": { + "name": "test index" + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "createSearchIndexes operation-atlas-search.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-atlas-search", + "db.collection.name": "test", + "db.operation.name": "createSearchIndexes", + "db.operation.summary": "createSearchIndexes operation-atlas-search.test" + }, + "nested": [ + { + "name": "createSearchIndexes", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-atlas-search", + "db.command.name": "createSearchIndexes", + "db.collection.name": "test", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": true + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "exception.message": { + "$$exists": true + }, + "exception.type": { + "$$exists": true + }, + "exception.stacktrace": { + "$$exists": true + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.query.summary": "createSearchIndexes operation-atlas-search.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "createSearchIndexes": "test", + "indexes": [ + { + "type": "search", + "definition": { + "mappings": { + "dynamic": true + } + } + } + ] + } + } + } + } + } + ] + }, + { + "name": "updateSearchIndex operation-atlas-search.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-atlas-search", + "db.collection.name": "test", + "db.operation.name": "updateSearchIndex", + "db.operation.summary": "updateSearchIndex operation-atlas-search.test" + }, + "nested": [ + { + "name": "updateSearchIndex", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-atlas-search", + "db.command.name": "updateSearchIndex", + "db.collection.name": "test", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": true + }, + "exception.message": { + "$$exists": true + }, + "exception.type": { + "$$exists": true + }, + "exception.stacktrace": { + "$$exists": true + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "updateSearchIndex operation-atlas-search.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "updateSearchIndex": "test", + "name": "test index", + "definition": {} + } + } + } + } + } + ] + }, + { + "name": "dropSearchIndex operation-atlas-search.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-atlas-search", + "db.collection.name": "test", + "db.operation.name": "dropSearchIndex", + "db.operation.summary": "dropSearchIndex operation-atlas-search.test" + }, + "nested": [ + { + "name": "dropSearchIndex", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-atlas-search", + "db.command.name": "dropSearchIndex", + "db.collection.name": "test", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": true + }, + "exception.message": { + "$$exists": true + }, + "exception.type": { + "$$exists": true + }, + "exception.stacktrace": { + "$$exists": true + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "dropSearchIndex operation-atlas-search.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "dropSearchIndex": "test", + "name": "test index" + } + } + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/atlas_search.yml b/specifications/open-telemetry/operation/atlas_search.yml new file mode 100644 index 00000000000..d5029f994f2 --- /dev/null +++ b/specifications/open-telemetry/operation/atlas_search.yml @@ -0,0 +1,170 @@ +description: operation atlas_search +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-atlas-search + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: test + +runOnRequirements: + # Skip server versions without fix of SERVER-83107 to avoid error message "BSON field 'createSearchIndexes.indexes.type' is an unknown field." + # SERVER-83107 was not backported to 7.1. + - minServerVersion: "7.0.5" + maxServerVersion: "7.0.99" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + - minServerVersion: "7.2.0" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + + +tests: + - description: atlas search indexes + operations: + - name: createSearchIndex + object: *collection0 + arguments: + model: { definition: { mappings: { dynamic: true } } , type: 'search' } + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + + - name: updateSearchIndex + object: *collection0 + arguments: + name: 'test index' + definition: {} + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + + - name: dropSearchIndex + object: *collection0 + arguments: + name: 'test index' + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: createSearchIndexes operation-atlas-search.test + attributes: + db.system: mongodb + db.namespace: operation-atlas-search + db.collection.name: test + db.operation.name: createSearchIndexes + db.operation.summary: createSearchIndexes operation-atlas-search.test + nested: + - name: createSearchIndexes + attributes: + db.system: mongodb + db.namespace: operation-atlas-search + db.command.name: createSearchIndexes + db.collection.name: test + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: true } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + exception.message: { $$exists: true } + exception.type: { $$exists: true } + exception.stacktrace: { $$exists: true } + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.query.summary: createSearchIndexes operation-atlas-search.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + createSearchIndexes: test + indexes: [ { "type": "search", "definition": { "mappings": { "dynamic": true } } } ] + + - name: updateSearchIndex operation-atlas-search.test + attributes: + db.system: mongodb + db.namespace: operation-atlas-search + db.collection.name: test + db.operation.name: updateSearchIndex + db.operation.summary: updateSearchIndex operation-atlas-search.test + + nested: + - name: updateSearchIndex + attributes: + db.system: mongodb + db.namespace: operation-atlas-search + db.command.name: updateSearchIndex + db.collection.name: test + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: true } + exception.message: { $$exists: true } + exception.type: { $$exists: true } + exception.stacktrace: { $$exists: true } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.summary: updateSearchIndex operation-atlas-search.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + updateSearchIndex: test + name: test index + definition: {} + + - name: dropSearchIndex operation-atlas-search.test + attributes: + db.system: mongodb + db.namespace: operation-atlas-search + db.collection.name: test + db.operation.name: dropSearchIndex + db.operation.summary: dropSearchIndex operation-atlas-search.test + nested: + - name: dropSearchIndex + attributes: + db.system: mongodb + db.namespace: operation-atlas-search + db.command.name: dropSearchIndex + db.collection.name: test + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: true } + exception.message: { $$exists: true } + exception.type: { $$exists: true } + exception.stacktrace: { $$exists: true } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.summary: dropSearchIndex operation-atlas-search.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + dropSearchIndex: test + name: test index diff --git a/specifications/open-telemetry/operation/bulk_write.json b/specifications/open-telemetry/operation/bulk_write.json new file mode 100644 index 00000000000..6f1e62004ac --- /dev/null +++ b/specifications/open-telemetry/operation/bulk_write.json @@ -0,0 +1,333 @@ +{ + "description": "operation bulk_write", + "schemaVersion": "1.27", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-bulk-write-0" + } + }, + { + "database": { + "id": "database1", + "client": "client0", + "databaseName": "operation-bulk-write-1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test0" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test1" + } + } + ], + "initialData": [ + { + "collectionName": "test0", + "databaseName": "operation-bulk-write-0", + "documents": [] + }, + { + "collectionName": "test1", + "databaseName": "operation-bulk-write-1", + "documents": [] + } + ], + "_yamlAnchors": { + "namespace0": "operation-bulk-write-0.test0", + "namespace1": "operation-bulk-write-1.test1" + }, + "tests": [ + { + "description": "bulkWrite", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "operation-bulk-write-0.test0", + "document": { + "_id": 8, + "x": 88 + } + } + }, + { + "updateOne": { + "namespace": "operation-bulk-write-0.test0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "updateMany": { + "namespace": "operation-bulk-write-1.test1", + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "update": { + "$inc": { + "x": 2 + } + } + } + }, + { + "replaceOne": { + "namespace": "operation-bulk-write-1.test1", + "filter": { + "_id": 4 + }, + "replacement": { + "x": 44 + }, + "upsert": true + } + }, + { + "deleteOne": { + "namespace": "operation-bulk-write-0.test0", + "filter": { + "_id": 5 + } + } + }, + { + "deleteMany": { + "namespace": "operation-bulk-write-1.test1", + "filter": { + "$and": [ + { + "_id": { + "$gt": 5 + } + }, + { + "_id": { + "$lte": 7 + } + } + ] + } + } + } + ] + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "bulkWrite admin", + "attributes": { + "db.system": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.operation.name": "bulkWrite", + "db.operation.summary": "bulkWrite admin" + }, + "nested": [ + { + "name": "bulkWrite", + "attributes": { + "db.system": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.command.name": "bulkWrite", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "bulkWrite admin", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "bulkWrite": 1, + "errorsOnly": true, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 8, + "x": 88 + } + }, + { + "update": 0, + "multi": false, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + } + }, + { + "update": 1, + "multi": true, + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "updateMods": { + "$inc": { + "x": 2 + } + } + }, + { + "update": 1, + "multi": false, + "filter": { + "_id": 4 + }, + "updateMods": { + "x": 44 + }, + "upsert": true + }, + { + "delete": 0, + "multi": false, + "filter": { + "_id": 5 + } + }, + { + "delete": 1, + "multi": true, + "filter": { + "$and": [ + { + "_id": { + "$gt": 5 + } + }, + { + "_id": { + "$lte": 7 + } + } + ] + } + } + ], + "nsInfo": [ + { + "ns": "operation-bulk-write-0.test0" + }, + { + "ns": "operation-bulk-write-1.test1" + } + ] + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/bulk_write.yml b/specifications/open-telemetry/operation/bulk_write.yml new file mode 100644 index 00000000000..6f84e50fb15 --- /dev/null +++ b/specifications/open-telemetry/operation/bulk_write.yml @@ -0,0 +1,198 @@ +description: operation bulk_write +schemaVersion: '1.27' +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName0 operation-bulk-write-0 + - database: + id: &database1 database1 + client: *client0 + databaseName: &databaseName1 operation-bulk-write-1 + - collection: + id: collection0 + database: *database0 + collectionName: &collectionName0 test0 + - collection: + id: collection1 + database: *database1 + collectionName: &collectionName1 test1 + +initialData: + - collectionName: *collectionName0 + databaseName: *databaseName0 + documents: [ ] + - collectionName: *collectionName1 + databaseName: *databaseName1 + documents: [ ] + +_yamlAnchors: + namespace0: &namespace0 "operation-bulk-write-0.test0" + namespace1: &namespace1 "operation-bulk-write-1.test1" + +tests: + - description: bulkWrite + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace0 + document: { _id: 8, x: 88 } + - updateOne: + namespace: *namespace0 + filter: { _id: 1 } + update: { $inc: { x: 1 } } + - updateMany: + namespace: *namespace1 + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + update: { $inc: { x: 2 } } + - replaceOne: + namespace: *namespace1 + filter: { _id: 4 } + replacement: { x: 44 } + upsert: true + - deleteOne: + namespace: *namespace0 + filter: { _id: 5 } + - deleteMany: + namespace: *namespace1 + filter: + $and: [ { _id: { $gt: 5 } }, { _id: { $lte: 7 } } ] + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: bulkWrite admin + attributes: + db.system: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.operation.name: bulkWrite + db.operation.summary: bulkWrite admin + nested: + - name: bulkWrite + attributes: + db.system: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.command.name: bulkWrite + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: bulkWrite admin + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + bulkWrite: 1 + errorsOnly: true + ordered: true + ops: [ + { + "insert": 0, + "document": { + "_id": 8, + "x": 88 + } + }, + { + "update": 0, + "multi": false, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + } + }, + { + "update": 1, + "multi": true, + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "updateMods": { + "$inc": { + "x": 2 + } + } + }, + { + "update": 1, + "multi": false, + "filter": { + "_id": 4 + }, + "updateMods": { + "x": 44 + }, + "upsert": true + }, + { + "delete": 0, + "multi": false, + "filter": { + "_id": 5 + } + }, + { + "delete": 1, + "multi": true, + "filter": { + "$and": [ + { + "_id": { + "$gt": 5 + } + }, + { + "_id": { + "$lte": 7 + } + } + ] + } + } + ] + nsInfo: [ + { + "ns": *namespace0 + }, + { + "ns": *namespace1 + } + ] + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/count.json b/specifications/open-telemetry/operation/count.json new file mode 100644 index 00000000000..6b4304dfd85 --- /dev/null +++ b/specifications/open-telemetry/operation/count.json @@ -0,0 +1,123 @@ +{ + "description": "operation count", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-count" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-count", + "documents": [] + } + ], + "tests": [ + { + "description": "estimated document count", + "operations": [ + { + "object": "collection0", + "name": "estimatedDocumentCount", + "arguments": {}, + "expectResult": 0 + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "count operation-count.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-count", + "db.collection.name": "test", + "db.operation.name": "count", + "db.operation.summary": "count operation-count.test" + }, + "nested": [ + { + "name": "count", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-count", + "db.collection.name": "test", + "db.command.name": "count", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "count operation-count.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "count": "test" + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/count.yml b/specifications/open-telemetry/operation/count.yml new file mode 100644 index 00000000000..3d2abcb04c8 --- /dev/null +++ b/specifications/open-telemetry/operation/count.yml @@ -0,0 +1,63 @@ +description: operation count +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: database0 + client: *client0 + databaseName: &database0Name operation-count + - collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection0Name test +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] +tests: + - description: estimated document count + operations: + - object: *collection0 + name: estimatedDocumentCount + arguments: { } + expectResult: 0 + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: count operation-count.test + attributes: + db.system: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.operation.name: count + db.operation.summary: count operation-count.test + nested: + - name: count + attributes: + db.system: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.command.name: count + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: count operation-count.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + count: test + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/create_collection.json b/specifications/open-telemetry/operation/create_collection.json new file mode 100644 index 00000000000..fc296b4fd22 --- /dev/null +++ b/specifications/open-telemetry/operation/create_collection.json @@ -0,0 +1,110 @@ +{ + "description": "operation create collection", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-create-collection" + } + } + ], + "tests": [ + { + "description": "create collection", + "operations": [ + { + "object": "database0", + "name": "createCollection", + "arguments": { + "collection": "newlyCreatedCollection" + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "createCollection operation-create-collection.newlyCreatedCollection", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-create-collection", + "db.collection.name": "newlyCreatedCollection", + "db.operation.name": "createCollection", + "db.operation.summary": "createCollection operation-create-collection.newlyCreatedCollection" + }, + "nested": [ + { + "name": "create", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-create-collection", + "db.collection.name": "newlyCreatedCollection", + "db.command.name": "create", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "create operation-create-collection.newlyCreatedCollection", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "create": "newlyCreatedCollection" + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/create_collection.yml b/specifications/open-telemetry/operation/create_collection.yml new file mode 100644 index 00000000000..d0dee36051a --- /dev/null +++ b/specifications/open-telemetry/operation/create_collection.yml @@ -0,0 +1,55 @@ +description: operation create collection +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name operation-create-collection +tests: + - description: create collection + operations: + - object: *database0 + name: createCollection + arguments: + collection: &collectionName newlyCreatedCollection + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: createCollection operation-create-collection.newlyCreatedCollection + attributes: + db.system: mongodb + db.namespace: *database0Name + db.collection.name: *collectionName + db.operation.name: createCollection + db.operation.summary: createCollection operation-create-collection.newlyCreatedCollection + nested: + - name: create + attributes: + db.system: mongodb + db.namespace: *database0Name + db.collection.name: *collectionName + db.command.name: create + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: create operation-create-collection.newlyCreatedCollection + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + create: newlyCreatedCollection + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/create_indexes.json b/specifications/open-telemetry/operation/create_indexes.json new file mode 100644 index 00000000000..40afac51479 --- /dev/null +++ b/specifications/open-telemetry/operation/create_indexes.json @@ -0,0 +1,127 @@ +{ + "description": "operation create_indexes", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-create-indexes" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "create indexes", + "operations": [ + { + "object": "collection0", + "name": "createIndex", + "arguments": { + "keys": { + "x": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "createIndexes operation-create-indexes.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-create-indexes", + "db.collection.name": "test", + "db.operation.name": "createIndexes", + "db.operation.summary": "createIndexes operation-create-indexes.test" + }, + "nested": [ + { + "name": "createIndexes", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-create-indexes", + "db.collection.name": "test", + "db.command.name": "createIndexes", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "createIndexes operation-create-indexes.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "createIndexes": "test", + "indexes": [ + { + "key": { + "x": 1 + }, + "name": "x_1" + } + ] + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/create_indexes.yml b/specifications/open-telemetry/operation/create_indexes.yml new file mode 100644 index 00000000000..01e23e242cd --- /dev/null +++ b/specifications/open-telemetry/operation/create_indexes.yml @@ -0,0 +1,60 @@ +description: operation create_indexes +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name operation-create-indexes + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test +tests: + - description: create indexes + operations: + - object: *collection0 + name: createIndex + arguments: + keys: { x: 1 } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: createIndexes operation-create-indexes.test + attributes: + db.system: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.operation.name: createIndexes + db.operation.summary: createIndexes operation-create-indexes.test + nested: + - name: createIndexes + attributes: + db.system: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.command.name: createIndexes + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: createIndexes operation-create-indexes.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + createIndexes: test + indexes: [ { key: { x: 1 }, name: "x_1" } ] + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/delete.json b/specifications/open-telemetry/operation/delete.json new file mode 100644 index 00000000000..203e392ce88 --- /dev/null +++ b/specifications/open-telemetry/operation/delete.json @@ -0,0 +1,132 @@ +{ + "description": "operation delete", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-delete" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "delete elements", + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "delete operation-delete.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-delete", + "db.collection.name": "test", + "db.operation.name": "delete", + "db.operation.summary": "delete operation-delete.test" + }, + "nested": [ + { + "name": "delete", + "attributes": { + "db.system": "mongodb", + "db.command.name": "delete", + "db.namespace": "operation-delete", + "db.collection.name": "test", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "delete operation-delete.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "delete": "test", + "ordered": true, + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "limit": 0 + } + ] + } + } + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/delete.yml b/specifications/open-telemetry/operation/delete.yml new file mode 100644 index 00000000000..94410683f96 --- /dev/null +++ b/specifications/open-telemetry/operation/delete.yml @@ -0,0 +1,62 @@ +description: operation delete +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName0 operation-delete + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName0 test + +tests: + - description: delete elements + operations: + - object: *collection0 + name: deleteMany + arguments: + filter: { _id: { $gt: 1 } } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: delete operation-delete.test + attributes: + db.system: mongodb + db.namespace: *databaseName0 + db.collection.name: *collectionName0 + db.operation.name: delete + db.operation.summary: delete operation-delete.test + nested: + - name: delete + attributes: + db.system: mongodb + db.command.name: delete + db.namespace: operation-delete + db.collection.name: test + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.summary: delete operation-delete.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + delete: test + ordered: true + deletes: [ { q: { _id: { $gt: 1 } }, limit: 0 } ] diff --git a/specifications/open-telemetry/operation/distinct.json b/specifications/open-telemetry/operation/distinct.json new file mode 100644 index 00000000000..8478d569fee --- /dev/null +++ b/specifications/open-telemetry/operation/distinct.json @@ -0,0 +1,127 @@ +{ + "description": "operation distinct", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-distinct" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-distinct", + "documents": [] + } + ], + "tests": [ + { + "description": "distinct on a field", + "operations": [ + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": {} + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "distinct operation-distinct.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-distinct", + "db.collection.name": "test", + "db.operation.name": "distinct", + "db.operation.summary": "distinct operation-distinct.test" + }, + "nested": [ + { + "name": "distinct", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-distinct", + "db.collection.name": "test", + "db.command.name": "distinct", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "distinct operation-distinct.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "distinct": "test", + "key": "x", + "query": {} + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/distinct.yml b/specifications/open-telemetry/operation/distinct.yml new file mode 100644 index 00000000000..0b78cecca9d --- /dev/null +++ b/specifications/open-telemetry/operation/distinct.yml @@ -0,0 +1,66 @@ +description: operation distinct +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: database0 + client: *client0 + databaseName: operation-distinct + - collection: + id: &collection0 collection0 + database: database0 + collectionName: test +initialData: + - collectionName: test + databaseName: operation-distinct + documents: [] +tests: + - description: distinct on a field + operations: + - object: *collection0 + name: distinct + arguments: + fieldName: x + filter: {} + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: distinct operation-distinct.test + attributes: + db.system: mongodb + db.namespace: operation-distinct + db.collection.name: test + db.operation.name: distinct + db.operation.summary: distinct operation-distinct.test + nested: + - name: distinct + attributes: + db.system: mongodb + db.namespace: operation-distinct + db.collection.name: test + db.command.name: distinct + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: distinct operation-distinct.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + distinct: test + key: x + query: { } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/drop_collection.json b/specifications/open-telemetry/operation/drop_collection.json new file mode 100644 index 00000000000..fbffed165bd --- /dev/null +++ b/specifications/open-telemetry/operation/drop_collection.json @@ -0,0 +1,122 @@ +{ + "description": "operation drop collection", + "schemaVersion": "1.27", + "runOnRequirements": [ + { + "minServerVersion": "7.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-drop-collection" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "drop collection", + "operations": [ + { + "object": "database0", + "name": "dropCollection", + "arguments": { + "collection": "test" + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "dropCollection operation-drop-collection.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-drop-collection", + "db.collection.name": "test", + "db.operation.name": "dropCollection", + "db.operation.summary": "dropCollection operation-drop-collection.test" + }, + "nested": [ + { + "name": "drop", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-drop-collection", + "db.collection.name": "test", + "db.command.name": "drop", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "drop operation-drop-collection.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "drop": "test" + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/drop_collection.yml b/specifications/open-telemetry/operation/drop_collection.yml new file mode 100644 index 00000000000..7308c83b5ec --- /dev/null +++ b/specifications/open-telemetry/operation/drop_collection.yml @@ -0,0 +1,65 @@ +description: operation drop collection +schemaVersion: '1.27' + +runOnRequirements: + - minServerVersion: "7.0" + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-drop-collection + + - collection: + id: collection0 + database: *database0 + collectionName: &collection_name test +tests: + - description: drop collection + operations: + - object: *database0 + name: dropCollection + arguments: + collection: *collection_name + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: dropCollection operation-drop-collection.test + attributes: + db.system: mongodb + db.namespace: operation-drop-collection + db.collection.name: test + db.operation.name: dropCollection + db.operation.summary: dropCollection operation-drop-collection.test + + nested: + - name: drop + attributes: + db.system: mongodb + db.namespace: operation-drop-collection + db.collection.name: test + db.command.name: drop + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: drop operation-drop-collection.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + drop: test + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/drop_indexes.json b/specifications/open-telemetry/operation/drop_indexes.json new file mode 100644 index 00000000000..efc865b3226 --- /dev/null +++ b/specifications/open-telemetry/operation/drop_indexes.json @@ -0,0 +1,145 @@ +{ + "description": "operation drop indexes", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-drop-indexes" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "client": { + "id": "clientWithoutTracing", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "databaseWithoutTracing", + "client": "clientWithoutTracing", + "databaseName": "operation-drop-indexes" + } + }, + { + "collection": { + "id": "collectionWithoutTracing", + "database": "databaseWithoutTracing", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "drop indexes", + "operations": [ + { + "name": "createIndex", + "object": "collectionWithoutTracing", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + }, + { + "name": "dropIndexes", + "object": "collection0" + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": true, + "spans": [ + { + "name": "dropIndexes operation-drop-indexes.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-drop-indexes", + "db.collection.name": "test", + "db.operation.name": "dropIndexes", + "db.operation.summary": "dropIndexes operation-drop-indexes.test" + }, + "nested": [ + { + "name": "dropIndexes", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-drop-indexes", + "db.collection.name": "test", + "db.command.name": "dropIndexes", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "dropIndexes operation-drop-indexes.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "dropIndexes": "test", + "index": "*" + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/drop_indexes.yml b/specifications/open-telemetry/operation/drop_indexes.yml new file mode 100644 index 00000000000..519995b7436 --- /dev/null +++ b/specifications/open-telemetry/operation/drop_indexes.yml @@ -0,0 +1,78 @@ +description: operation drop indexes +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-drop-indexes + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: test + - client: + id: &clientWithoutTracing clientWithoutTracing + useMultipleMongoses: false + - database: + id: &databaseWithoutTracing databaseWithoutTracing + client: *clientWithoutTracing + databaseName: operation-drop-indexes + - collection: + id: &collectionWithoutTracing collectionWithoutTracing + database: *databaseWithoutTracing + collectionName: test + +tests: + - description: drop indexes + operations: + - name: createIndex + object: *collectionWithoutTracing + arguments: + keys: + x: 1 + name: x_1 + + - name: dropIndexes + object: *collection0 + + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: true + spans: + - name: dropIndexes operation-drop-indexes.test + attributes: + db.system: mongodb + db.namespace: operation-drop-indexes + db.collection.name: test + db.operation.name: dropIndexes + db.operation.summary: dropIndexes operation-drop-indexes.test + nested: + - name: dropIndexes + attributes: + db.system: mongodb + db.namespace: operation-drop-indexes + db.collection.name: test + db.command.name: dropIndexes + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: dropIndexes operation-drop-indexes.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + dropIndexes: test + index: '*' + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/find.json b/specifications/open-telemetry/operation/find.json new file mode 100644 index 00000000000..573fd75411b --- /dev/null +++ b/specifications/open-telemetry/operation/find.json @@ -0,0 +1,291 @@ +{ + "description": "operation find", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-find" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-find", + "documents": [] + } + ], + "tests": [ + { + "description": "find an element", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "x": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "find operation-find.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-find", + "db.collection.name": "test", + "db.operation.name": "find", + "db.operation.summary": "find operation-find.test" + }, + "nested": [ + { + "name": "find", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-find", + "db.collection.name": "test", + "db.command.name": "find", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "find operation-find.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "find": "test", + "filter": { + "x": 1 + } + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + }, + { + "description": "find an element retrying failed command", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 89, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "x": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": true, + "spans": [ + { + "name": "find operation-find.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-find", + "db.collection.name": "test", + "db.operation.name": "find", + "db.operation.summary": "find operation-find.test" + }, + "nested": [ + { + "name": "find", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-find", + "db.collection.name": "test", + "db.command.name": "find", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": "89", + "exception.message": { + "$$type": "string" + }, + "exception.type": { + "$$type": "string" + }, + "exception.stacktrace": { + "$$type": "string" + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.query.summary": "find operation-find.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "find": "test", + "filter": { + "x": 1 + } + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "name": "find", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-find", + "db.collection.name": "test", + "db.command.name": "find", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "find operation-find.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "find": "test", + "filter": { + "x": 1 + } + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/find.yml b/specifications/open-telemetry/operation/find.yml new file mode 100644 index 00000000000..c29bc0123a1 --- /dev/null +++ b/specifications/open-telemetry/operation/find.yml @@ -0,0 +1,143 @@ +description: operation find +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-find + - collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection0Name test +initialData: + - collectionName: test + databaseName: operation-find + documents: [] +tests: + - description: find an element + operations: + - name: find + object: *collection0 + arguments: { filter: { x: 1 } } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: find operation-find.test + attributes: + db.system: mongodb + db.namespace: operation-find + db.collection.name: test + db.operation.name: find + db.operation.summary: find operation-find.test + nested: + - name: find + attributes: + db.system: mongodb + db.namespace: operation-find + db.collection.name: test + db.command.name: find + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: find operation-find.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + find: test + filter: + x: 1 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + + - description: find an element retrying failed command + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ find ] + errorCode: 89 + errorLabels: [ RetryableWriteError ] + + - name: find + object: *collection0 + arguments: + filter: { x: 1 } + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: true + spans: + - name: find operation-find.test + attributes: + db.system: mongodb + db.namespace: operation-find + db.collection.name: test + db.operation.name: find + db.operation.summary: find operation-find.test + nested: + - name: find + attributes: + db.system: mongodb + db.namespace: operation-find + db.collection.name: test + db.command.name: find + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: '89' + exception.message: { $$type: string } + exception.type: { $$type: string } + exception.stacktrace: { $$type: string } + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.query.summary: find operation-find.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + find: test + filter: + x: 1 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + - name: find + attributes: + db.system: mongodb + db.namespace: operation-find + db.collection.name: test + db.command.name: find + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: find operation-find.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + find: test + filter: + x: 1 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/find_and_modify.json b/specifications/open-telemetry/operation/find_and_modify.json new file mode 100644 index 00000000000..26e45b1deda --- /dev/null +++ b/specifications/open-telemetry/operation/find_and_modify.json @@ -0,0 +1,143 @@ +{ + "description": "operation find_one_and_update", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-find-one-and-update" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "findOneAndUpdate", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": "comment" + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "findAndModify operation-find-one-and-update.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-find-one-and-update", + "db.collection.name": "test", + "db.operation.name": "findAndModify", + "db.operation.summary": "findAndModify operation-find-one-and-update.test" + }, + "nested": [ + { + "name": "findAndModify", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-find-one-and-update", + "db.collection.name": "test", + "db.command.name": "findAndModify", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "findAndModify operation-find-one-and-update.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "findAndModify": "test", + "query": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": "comment" + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/find_and_modify.yml b/specifications/open-telemetry/operation/find_and_modify.yml new file mode 100644 index 00000000000..7959d4880ec --- /dev/null +++ b/specifications/open-telemetry/operation/find_and_modify.yml @@ -0,0 +1,70 @@ +description: operation find_one_and_update +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-find-one-and-update + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: test + +tests: + - description: findOneAndUpdate + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: &filter + _id: 1 + update: &update + - $set: { x: 5 } + comment: "comment" + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: findAndModify operation-find-one-and-update.test + attributes: + db.system: mongodb + db.namespace: operation-find-one-and-update + db.collection.name: test + db.operation.name: findAndModify + db.operation.summary: findAndModify operation-find-one-and-update.test + + nested: + - name: findAndModify + attributes: + db.system: mongodb + db.namespace: operation-find-one-and-update + db.collection.name: test + db.command.name: findAndModify + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: findAndModify operation-find-one-and-update.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + findAndModify: test + query: *filter + update: *update + comment: "comment" + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/find_without_query_text.json b/specifications/open-telemetry/operation/find_without_query_text.json new file mode 100644 index 00000000000..74e1614bf4f --- /dev/null +++ b/specifications/open-telemetry/operation/find_without_query_text.json @@ -0,0 +1,119 @@ +{ + "description": "operation find without db.query.text", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": false + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-find" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-find", + "documents": [] + } + ], + "tests": [ + { + "description": "find an element", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "x": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "find operation-find.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-find", + "db.collection.name": "test", + "db.operation.name": "find", + "db.operation.summary": "find operation-find.test" + }, + "nested": [ + { + "name": "find", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-find", + "db.collection.name": "test", + "db.command.name": "find", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "find operation-find.test", + "db.query.text": { + "$$exists": false + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/find_without_query_text.yml b/specifications/open-telemetry/operation/find_without_query_text.yml new file mode 100644 index 00000000000..428537de5d7 --- /dev/null +++ b/specifications/open-telemetry/operation/find_without_query_text.yml @@ -0,0 +1,58 @@ +description: operation find without db.query.text +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: false + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-find + - collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection0Name test +initialData: + - collectionName: test + databaseName: operation-find + documents: [] +tests: + - description: find an element + operations: + - name: find + object: *collection0 + arguments: { filter: { x: 1 } } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: find operation-find.test + attributes: + db.system: mongodb + db.namespace: operation-find + db.collection.name: test + db.operation.name: find + db.operation.summary: find operation-find.test + nested: + - name: find + attributes: + db.system: mongodb + db.namespace: operation-find + db.collection.name: test + db.command.name: find + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.summary: find operation-find.test + db.query.text: { $$exists: false } diff --git a/specifications/open-telemetry/operation/insert.json b/specifications/open-telemetry/operation/insert.json new file mode 100644 index 00000000000..5ab17cf040b --- /dev/null +++ b/specifications/open-telemetry/operation/insert.json @@ -0,0 +1,146 @@ +{ + "description": "operation insert", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-insert" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-insert", + "documents": [] + } + ], + "tests": [ + { + "description": "insert one element", + "operations": [ + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "insert operation-insert.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-insert", + "db.collection.name": "test", + "db.operation.name": "insert", + "db.operation.summary": "insert operation-insert.test" + }, + "nested": [ + { + "name": "insert", + "attributes": { + "db.system": "mongodb", + "db.command.name": "insert", + "db.namespace": "operation-insert", + "db.collection.name": "test", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "insert operation-insert.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "insert": "test", + "ordered": true, + "txnNumber": { + "$$unsetOrMatches": 1 + }, + "documents": [ + { + "_id": 1 + } + ] + } + } + } + } + } + ] + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "operation-insert", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/insert.yml b/specifications/open-telemetry/operation/insert.yml new file mode 100644 index 00000000000..dbb11fafa26 --- /dev/null +++ b/specifications/open-telemetry/operation/insert.yml @@ -0,0 +1,72 @@ +description: operation insert +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-insert + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: test +initialData: + - collectionName: test + databaseName: operation-insert + documents: [ ] +tests: + - description: insert one element + operations: + - object: *collection0 + name: insertOne + arguments: { document: { _id: 1 } } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: insert operation-insert.test + attributes: + db.system: mongodb + db.namespace: operation-insert + db.collection.name: test + db.operation.name: insert + db.operation.summary: insert operation-insert.test + nested: + - name: insert + attributes: + db.system: mongodb + db.command.name: insert + db.namespace: operation-insert + db.collection.name: test + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.summary: insert operation-insert.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + insert: test + ordered: true + txnNumber: { $$unsetOrMatches: 1 } + documents: + - _id: 1 + + outcome: + - collectionName: test + databaseName: operation-insert + documents: + - _id: 1 diff --git a/specifications/open-telemetry/operation/list_collections.json b/specifications/open-telemetry/operation/list_collections.json new file mode 100644 index 00000000000..d9b1937eb50 --- /dev/null +++ b/specifications/open-telemetry/operation/list_collections.json @@ -0,0 +1,108 @@ +{ + "description": "operation list_collections", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-list-collections" + } + } + ], + "tests": [ + { + "description": "List collections", + "operations": [ + { + "object": "database0", + "name": "listCollections" + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "listCollections operation-list-collections", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-list-collections", + "db.operation.name": "listCollections", + "db.operation.summary": "listCollections operation-list-collections", + "db.collection.name": { + "$$exists": false + } + }, + "nested": [ + { + "name": "listCollections", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-list-collections", + "db.collection.name": { + "$$exists": false + }, + "db.command.name": "listCollections", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "listCollections operation-list-collections", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "listCollections": 1 + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/list_collections.yml b/specifications/open-telemetry/operation/list_collections.yml new file mode 100644 index 00000000000..da17fbb2e48 --- /dev/null +++ b/specifications/open-telemetry/operation/list_collections.yml @@ -0,0 +1,53 @@ +description: operation list_collections +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-list-collections +tests: + - description: List collections + operations: + - object: *database0 + name: listCollections + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: listCollections operation-list-collections + attributes: + db.system: mongodb + db.namespace: operation-list-collections + db.operation.name: listCollections + db.operation.summary: listCollections operation-list-collections + db.collection.name: { $$exists: false } + + nested: + - name: listCollections + attributes: + db.system: mongodb + db.namespace: operation-list-collections + db.collection.name: { $$exists: false } + db.command.name: listCollections + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: listCollections operation-list-collections + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + listCollections: 1 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/list_databases.json b/specifications/open-telemetry/operation/list_databases.json new file mode 100644 index 00000000000..8fbd8b1014c --- /dev/null +++ b/specifications/open-telemetry/operation/list_databases.json @@ -0,0 +1,104 @@ +{ + "description": "operation list_databases", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + } + ], + "tests": [ + { + "description": "list databases", + "operations": [ + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "listDatabases admin", + "attributes": { + "db.system": "mongodb", + "db.namespace": "admin", + "db.operation.name": "listDatabases", + "db.operation.summary": "listDatabases admin", + "db.collection.name": { + "$$exists": false + } + }, + "nested": [ + { + "name": "listDatabases", + "attributes": { + "db.system": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.command.name": "listDatabases", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "listDatabases admin", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "listDatabases": 1 + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/list_databases.yml b/specifications/open-telemetry/operation/list_databases.yml new file mode 100644 index 00000000000..4adc361af6b --- /dev/null +++ b/specifications/open-telemetry/operation/list_databases.yml @@ -0,0 +1,50 @@ +description: operation list_databases +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true +tests: + - description: list databases + operations: + - object: *client0 + name: listDatabases + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: listDatabases admin + attributes: + db.system: mongodb + db.namespace: admin + db.operation.name: listDatabases + db.operation.summary: listDatabases admin + db.collection.name: { $$exists: false } + + nested: + - name: listDatabases + attributes: + db.system: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.command.name: listDatabases + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: listDatabases admin + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + listDatabases: 1 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/list_indexes.json b/specifications/open-telemetry/operation/list_indexes.json new file mode 100644 index 00000000000..2674dec2259 --- /dev/null +++ b/specifications/open-telemetry/operation/list_indexes.json @@ -0,0 +1,118 @@ +{ + "description": "operation list_indexes", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-list-indexes" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-list-indexes", + "documents": [] + } + ], + "tests": [ + { + "description": "List indexes", + "operations": [ + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "listIndexes operation-list-indexes.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-list-indexes", + "db.collection.name": "test", + "db.operation.name": "listIndexes", + "db.operation.summary": "listIndexes operation-list-indexes.test" + }, + "nested": [ + { + "name": "listIndexes", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-list-indexes", + "db.collection.name": "test", + "db.command.name": "listIndexes", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "listIndexes operation-list-indexes.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "listIndexes": "test" + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/list_indexes.yml b/specifications/open-telemetry/operation/list_indexes.yml new file mode 100644 index 00000000000..28c48e20dcb --- /dev/null +++ b/specifications/open-telemetry/operation/list_indexes.yml @@ -0,0 +1,61 @@ +description: operation list_indexes +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-list-indexes + - collection: + id: &collection0 collection0 + database: database0 + collectionName: test +initialData: + - collectionName: test + databaseName: operation-list-indexes + documents: [ ] +tests: + - description: List indexes + operations: + - object: *collection0 + name: listIndexes + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: listIndexes operation-list-indexes.test + attributes: + db.system: mongodb + db.namespace: operation-list-indexes + db.collection.name: test + db.operation.name: listIndexes + db.operation.summary: listIndexes operation-list-indexes.test + + nested: + - name: listIndexes + attributes: + db.system: mongodb + db.namespace: operation-list-indexes + db.collection.name: test + db.command.name: listIndexes + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: listIndexes operation-list-indexes.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + listIndexes: test + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/map_reduce.json b/specifications/open-telemetry/operation/map_reduce.json new file mode 100644 index 00000000000..fbe0972af7f --- /dev/null +++ b/specifications/open-telemetry/operation/map_reduce.json @@ -0,0 +1,177 @@ +{ + "description": "operation map_reduce", + "schemaVersion": "1.27", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "serverless": "forbid", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-map-reduce" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-map-reduce", + "documents": [ + { + "_id": 1, + "x": 0 + }, + { + "_id": 2, + "x": 1 + }, + { + "_id": 3, + "x": 2 + } + ] + } + ], + "tests": [ + { + "description": "mapReduce", + "operations": [ + { + "object": "collection0", + "name": "mapReduce", + "arguments": { + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "expectResult": [ + { + "_id": 0, + "value": 6 + } + ] + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "mapReduce operation-map-reduce.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-map-reduce", + "db.collection.name": "test", + "db.operation.name": "mapReduce", + "db.operation.summary": "mapReduce operation-map-reduce.test" + }, + "nested": [ + { + "name": "mapReduce", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-map-reduce", + "db.collection.name": "test", + "db.command.name": "mapReduce", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "mapReduce operation-map-reduce.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "mapReduce": "test", + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/map_reduce.yml b/specifications/open-telemetry/operation/map_reduce.yml new file mode 100644 index 00000000000..7fd74bc217a --- /dev/null +++ b/specifications/open-telemetry/operation/map_reduce.yml @@ -0,0 +1,99 @@ +description: operation map_reduce +schemaVersion: '1.27' +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + # serverless proxy does not support mapReduce operation + serverless: forbid + topologies: + - sharded + - load-balanced + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-map-reduce + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: test +initialData: + - + collectionName: test + databaseName: operation-map-reduce + documents: + - + _id: 1 + x: 0 + - + _id: 2 + x: 1 + - + _id: 3 + x: 2 +tests: + - description: mapReduce + operations: + - object: *collection0 + name: mapReduce + arguments: + map: + $code: 'function inc() { return emit(0, this.x + 1) }' + reduce: + $code: 'function sum(key, values) { return values.reduce((acc, x) => acc + x); }' + out: + inline: 1 + expectResult: + - + _id: 0 + value: 6 + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: mapReduce operation-map-reduce.test + attributes: + db.system: mongodb + db.namespace: operation-map-reduce + db.collection.name: test + db.operation.name: mapReduce + db.operation.summary: mapReduce operation-map-reduce.test + + nested: + - name: mapReduce + attributes: + db.system: mongodb + db.namespace: operation-map-reduce + db.collection.name: test + db.command.name: mapReduce + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: mapReduce operation-map-reduce.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + mapReduce: test + map: { $code: 'function inc() { return emit(0, this.x + 1) }' } + reduce: { $code: 'function sum(key, values) { return values.reduce((acc, x) => acc + x); }' } + out: { inline: 1 } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/retries.json b/specifications/open-telemetry/operation/retries.json new file mode 100644 index 00000000000..b8a94547c10 --- /dev/null +++ b/specifications/open-telemetry/operation/retries.json @@ -0,0 +1,209 @@ +{ + "description": "retries", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-retries" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-retries", + "documents": [] + } + ], + "tests": [ + { + "description": "find an element with retries", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 89, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "x": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": true, + "spans": [ + { + "name": "find operation-retries.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-retries", + "db.collection.name": "test", + "db.operation.name": "find", + "db.operation.summary": "find operation-retries.test" + }, + "nested": [ + { + "name": "find", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-retries", + "db.collection.name": "test", + "db.command.name": "find", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": "89", + "exception.message": { + "$$type": "string" + }, + "exception.type": { + "$$type": "string" + }, + "exception.stacktrace": { + "$$type": "string" + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.query.summary": "find operation-retries.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "find": "test", + "filter": { + "x": 1 + } + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "name": "find", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-retries", + "db.collection.name": "test", + "db.command.name": "find", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "find operation-retries.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "find": "test", + "filter": { + "x": 1 + } + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/retries.yml b/specifications/open-telemetry/operation/retries.yml new file mode 100644 index 00000000000..8375ffb1d64 --- /dev/null +++ b/specifications/open-telemetry/operation/retries.yml @@ -0,0 +1,105 @@ +description: retries +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name operation-retries + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test +initialData: + - collectionName: test + databaseName: operation-retries + documents: [ ] +tests: + - description: find an element with retries + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ find ] + errorCode: 89 + errorLabels: [ RetryableWriteError ] + + - name: find + object: *collection0 + arguments: + filter: { x: 1 } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: true + spans: + - name: find operation-retries.test + attributes: + db.system: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.operation.name: find + db.operation.summary: find operation-retries.test + + nested: + - name: find + attributes: + db.system: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.command.name: find + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: '89' + exception.message: { $$type: string } + exception.type: { $$type: string } + exception.stacktrace: { $$type: string } + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.query.summary: find operation-retries.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + find: test + filter: + x: 1 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + - name: find + attributes: + db.system: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.command.name: find + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: find operation-retries.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + find: test + filter: + x: 1 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/specifications/open-telemetry/operation/update.json b/specifications/open-telemetry/operation/update.json new file mode 100644 index 00000000000..06a6e3fe412 --- /dev/null +++ b/specifications/open-telemetry/operation/update.json @@ -0,0 +1,140 @@ +{ + "description": "operation update", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-update" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "update one element", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "update operation-update.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-update", + "db.collection.name": "test", + "db.operation.name": "update", + "db.operation.summary": "update operation-update.test" + }, + "nested": [ + { + "name": "update", + "attributes": { + "db.system": "mongodb", + "db.namespace": "operation-update", + "db.collection.name": "test", + "db.command.name": "update", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "update operation-update.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "update": "test", + "ordered": true, + "txnNumber": { + "$$unsetOrMatches": 1 + }, + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$inc": { + "x": 1 + } + } + } + ] + } + } + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/operation/update.yml b/specifications/open-telemetry/operation/update.yml new file mode 100644 index 00000000000..c3c816ec17e --- /dev/null +++ b/specifications/open-telemetry/operation/update.yml @@ -0,0 +1,66 @@ +description: operation update +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName0 operation-update + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName0 test + +tests: + - description: update one element + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: update operation-update.test + attributes: + db.system: mongodb + db.namespace: *databaseName0 + db.collection.name: *collectionName0 + db.operation.name: update + db.operation.summary: update operation-update.test + + nested: + - name: update + attributes: + db.system: mongodb + db.namespace: operation-update + db.collection.name: *collectionName0 + db.command.name: update + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.summary: update operation-update.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + update: test + ordered: true + txnNumber: { $$unsetOrMatches: 1 } + updates: [ { "q": { "_id": 1 }, "u": { "$inc": { "x": 1 } } } ] diff --git a/specifications/open-telemetry/transaction/convenient.json b/specifications/open-telemetry/transaction/convenient.json new file mode 100644 index 00000000000..b0245151d14 --- /dev/null +++ b/specifications/open-telemetry/transaction/convenient.json @@ -0,0 +1,326 @@ +{ + "description": "convenient transactions", + "schemaVersion": "1.27", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "convenient-transaction-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "convenient-transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "withTransaction", + "operations": [ + { + "name": "withTransaction", + "object": "session", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + }, + "session": "session" + } + } + ] + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "x": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "transaction", + "attributes": { + "db.system": "mongodb" + }, + "nested": [ + { + "name": "insert convenient-transaction-tests.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "convenient-transaction-tests", + "db.collection.name": "test", + "db.operation.name": "insert", + "db.operation.summary": "insert convenient-transaction-tests.test" + }, + "nested": [ + { + "name": "insert", + "attributes": { + "db.system": "mongodb", + "db.namespace": "convenient-transaction-tests", + "db.collection.name": "test", + "db.command.name": "insert", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.query.summary": "insert convenient-transaction-tests.test", + "db.mongodb.lsid": { + "$$sessionLsid": "session" + }, + "db.mongodb.txn_number": 1, + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "insert": "test", + "ordered": true, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "documents": [ + { + "_id": 1 + } + ] + } + } + } + } + } + ] + }, + { + "name": "commitTransaction admin", + "attributes": { + "db.system": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.operation.name": "commitTransaction", + "db.operation.summary": "commitTransaction admin" + }, + "nested": [ + { + "name": "commitTransaction", + "attributes": { + "db.system": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.query.summary": "commitTransaction admin", + "db.command.name": "commitTransaction", + "db.mongodb.lsid": { + "$$sessionLsid": "session" + }, + "db.mongodb.txn_number": 1, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "commitTransaction": 1, + "txnNumber": 1, + "autocommit": false + } + } + } + } + } + ] + } + ] + }, + { + "name": "find convenient-transaction-tests.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "convenient-transaction-tests", + "db.collection.name": "test", + "db.operation.summary": "find convenient-transaction-tests.test", + "db.operation.name": "find" + }, + "nested": [ + { + "name": "find", + "attributes": { + "db.system": "mongodb", + "db.namespace": "convenient-transaction-tests", + "db.collection.name": "test", + "db.command.name": "find", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "find convenient-transaction-tests.test", + "db.query.text": { + "$$exists": true + } + } + } + ] + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "convenient-transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/transaction/convenient.yml b/specifications/open-telemetry/transaction/convenient.yml new file mode 100644 index 00000000000..a606b82957c --- /dev/null +++ b/specifications/open-telemetry/transaction/convenient.yml @@ -0,0 +1,167 @@ +description: convenient transactions + +schemaVersion: "1.27" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database database + client: *client + databaseName: &databaseName convenient-transaction-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + - session: + id: &session session + client: *client + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - description: "withTransaction" + operations: + - name: withTransaction + object: *session + arguments: + callback: + - name: insertOne + object: *collection + arguments: + document: + _id: 1 + session: *session + - name: find + object: *collection + arguments: { filter: { x: 1 } } + + expectTracingMessages: + - client: *client + ignoreExtraSpans: false + spans: + - name: transaction + attributes: + db.system: mongodb + nested: + - name: insert convenient-transaction-tests.test + attributes: + db.system: mongodb + db.namespace: *databaseName + db.collection.name: *collectionName + db.operation.name: insert + db.operation.summary: insert convenient-transaction-tests.test + + nested: + - name: insert + attributes: + db.system: mongodb + db.namespace: *databaseName + db.collection.name: *collectionName + db.command.name: insert + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.query.summary: insert convenient-transaction-tests.test + db.mongodb.lsid: { $$sessionLsid: *session } + db.mongodb.txn_number: 1 + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + insert: test + ordered: true + txnNumber: 1 + startTransaction: true + autocommit: false + documents: + - _id: 1 + - name: commitTransaction admin + attributes: + db.system: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.operation.name: commitTransaction + db.operation.summary: commitTransaction admin + + nested: + - name: commitTransaction + attributes: + db.system: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.query.summary: commitTransaction admin + db.command.name: commitTransaction + db.mongodb.lsid: { $$sessionLsid: *session } + db.mongodb.txn_number: 1 + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + commitTransaction: 1 + txnNumber: 1 + autocommit: false + + - name: find convenient-transaction-tests.test + attributes: + db.system: mongodb + db.namespace: *databaseName + db.collection.name: *collectionName + db.operation.summary: find convenient-transaction-tests.test + db.operation.name: find + + nested: + - name: find + attributes: + db.system: mongodb + db.namespace: *databaseName + db.collection.name: *collectionName + db.command.name: find + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.summary: find convenient-transaction-tests.test + db.query.text: { $$exists: true } + + outcome: + - collectionName: test + databaseName: convenient-transaction-tests + documents: + - _id: 1 diff --git a/specifications/open-telemetry/transaction/core_api.json b/specifications/open-telemetry/transaction/core_api.json new file mode 100644 index 00000000000..93e3a9933d1 --- /dev/null +++ b/specifications/open-telemetry/transaction/core_api.json @@ -0,0 +1,545 @@ +{ + "description": "transaction spans", + "schemaVersion": "1.27", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "commit transaction", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "x": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "transaction", + "attributes": { + "db.system": "mongodb" + }, + "nested": [ + { + "name": "insert transaction-tests.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "transaction-tests", + "db.collection.name": "test", + "db.operation.name": "insert", + "db.operation.summary": "insert transaction-tests.test" + }, + "nested": [ + { + "name": "insert", + "attributes": { + "db.system": "mongodb", + "db.namespace": "transaction-tests", + "db.collection.name": "test", + "db.command.name": "insert", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.query.summary": "insert transaction-tests.test", + "db.mongodb.lsid": { + "$$sessionLsid": "session0" + }, + "db.mongodb.txn_number": 1, + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "insert": "test", + "ordered": true, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "documents": [ + { + "_id": 1 + } + ] + } + } + } + } + } + ] + }, + { + "name": "commitTransaction admin", + "attributes": { + "db.system": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.operation.name": "commitTransaction", + "db.operation.summary": "commitTransaction admin" + }, + "nested": [ + { + "name": "commitTransaction", + "attributes": { + "db.system": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.query.summary": "commitTransaction admin", + "db.command.name": "commitTransaction", + "db.mongodb.lsid": { + "$$sessionLsid": "session0" + }, + "db.mongodb.txn_number": 1, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "commitTransaction": 1, + "txnNumber": 1, + "autocommit": false + } + } + } + } + } + ] + } + ] + }, + { + "name": "find transaction-tests.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "transaction-tests", + "db.collection.name": "test", + "db.operation.summary": "find transaction-tests.test", + "db.operation.name": "find" + }, + "nested": [ + { + "name": "find", + "attributes": { + "db.system": "mongodb", + "db.namespace": "transaction-tests", + "db.collection.name": "test", + "db.command.name": "find", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.text": { + "$$exists": true + }, + "db.query.summary": "find transaction-tests.test" + } + } + ] + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "abort transaction", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "transaction", + "attributes": { + "db.system": "mongodb", + "db.namespace": { + "$$exists": false + }, + "db.collection.name": { + "$$exists": false + }, + "db.operation.name": { + "$$exists": false + }, + "db.operation.summary": { + "$$exists": false + } + }, + "nested": [ + { + "name": "insert transaction-tests.test", + "attributes": { + "db.system": "mongodb", + "db.namespace": "transaction-tests", + "db.collection.name": "test", + "db.operation.name": "insert", + "db.operation.summary": "insert transaction-tests.test" + }, + "nested": [ + { + "name": "insert", + "attributes": { + "db.system": "mongodb", + "db.namespace": "transaction-tests", + "db.collection.name": "test", + "db.command.name": "insert", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.query.summary": "insert transaction-tests.test", + "db.mongodb.lsid": { + "$$sessionLsid": "session0" + }, + "db.mongodb.txn_number": 1, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "insert": "test", + "ordered": true, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "documents": [ + { + "_id": 1 + } + ] + } + } + } + } + } + ] + }, + { + "name": "abortTransaction admin", + "attributes": { + "db.system": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.operation.name": "abortTransaction", + "db.operation.summary": "abortTransaction admin" + }, + "nested": [ + { + "name": "abortTransaction", + "attributes": { + "db.system": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.query.summary": "abortTransaction admin", + "db.command.name": "abortTransaction", + "db.mongodb.lsid": { + "$$sessionLsid": "session0" + }, + "db.mongodb.txn_number": 1, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "abortTransaction": 1, + "txnNumber": 1, + "autocommit": false + } + } + } + } + } + ] + } + ] + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + } + ] +} diff --git a/specifications/open-telemetry/transaction/core_api.yml b/specifications/open-telemetry/transaction/core_api.yml new file mode 100644 index 00000000000..fe10899ed0d --- /dev/null +++ b/specifications/open-telemetry/transaction/core_api.yml @@ -0,0 +1,269 @@ +description: transaction spans +schemaVersion: '1.27' +runOnRequirements: + - minServerVersion: '4.0' + topologies: + - replicaset + - minServerVersion: '4.1.8' + topologies: + - sharded + - load-balanced +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: transaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: test + - session: + id: &session0 session0 + client: client0 +initialData: + - collectionName: test + databaseName: transaction-tests + documents: [] +tests: + - description: commit transaction + operations: + - object: *session0 + name: startTransaction + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 1 + - object: *session0 + name: commitTransaction + - name: find + object: *collection0 + arguments: { filter: { x: 1 } } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: transaction + attributes: + db.system: mongodb + nested: + - name: insert transaction-tests.test + attributes: + db.system: mongodb + db.namespace: transaction-tests + db.collection.name: test + db.operation.name: insert + db.operation.summary: insert transaction-tests.test + + nested: + - name: insert + attributes: + db.system: mongodb + db.namespace: transaction-tests + db.collection.name: test + db.command.name: insert + server.address: { $$type: string } + server.port: { $$type: [long, string] } + db.query.summary: insert transaction-tests.test + db.mongodb.lsid: { $$sessionLsid: *session0 } + db.mongodb.txn_number: 1 + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + insert: test + ordered: true + txnNumber: 1 + startTransaction: true + autocommit: false + documents: + - _id: 1 + - name: commitTransaction admin + attributes: + db.system: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.operation.name: commitTransaction + db.operation.summary: commitTransaction admin + + nested: + - name: commitTransaction + attributes: + db.system: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.query.summary: commitTransaction admin + db.command.name: commitTransaction + db.mongodb.lsid: { $$sessionLsid: *session0 } + db.mongodb.txn_number: 1 + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + commitTransaction: 1 + txnNumber: 1 + autocommit: false + - name: find transaction-tests.test + attributes: + db.system: mongodb + db.namespace: transaction-tests + db.collection.name: test + db.operation.summary: find transaction-tests.test + db.operation.name: find + nested: + - name: find + attributes: + db.system: mongodb + db.namespace: transaction-tests + db.collection.name: test + db.command.name: find + server.address: { $$type: string } + server.port: { $$type: [long, string] } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.text: { $$exists: true } + db.query.summary: find transaction-tests.test + outcome: + - collectionName: test + databaseName: transaction-tests + documents: + - _id: 1 + + - description: abort transaction + operations: + - object: *session0 + name: startTransaction + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 1 + - object: *session0 + name: abortTransaction + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: transaction + attributes: + db.system: mongodb + db.namespace: { $$exists: false } + db.collection.name: { $$exists: false } + db.operation.name: { $$exists: false } + db.operation.summary: { $$exists: false } + nested: + - name: insert transaction-tests.test + attributes: + db.system: mongodb + db.namespace: transaction-tests + db.collection.name: test + db.operation.name: insert + db.operation.summary: insert transaction-tests.test + nested: + - name: insert + attributes: + db.system: mongodb + db.namespace: transaction-tests + db.collection.name: test + db.command.name: insert + server.address: { $$type: string } + server.port: { $$type: [long, string] } + db.query.summary: insert transaction-tests.test + db.mongodb.lsid: { $$sessionLsid: *session0 } + db.mongodb.txn_number: 1 + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + insert: test + ordered: true + txnNumber: 1 + startTransaction: true + autocommit: false + documents: + - _id: 1 + - name: abortTransaction admin + attributes: + db.system: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.operation.name: abortTransaction + db.operation.summary: abortTransaction admin + nested: + - name: abortTransaction + attributes: + db.system: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.query.summary: abortTransaction admin + db.command.name: abortTransaction + db.mongodb.lsid: { $$sessionLsid: *session0 } + db.mongodb.txn_number: 1 + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + abortTransaction: 1 + txnNumber: 1 + autocommit: false + + outcome: + - collectionName: test + databaseName: transaction-tests + documents: [] From e863c98fcc7a3332b12868953a65dae2294645f3 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Tue, 9 Dec 2025 16:46:58 -0500 Subject: [PATCH 02/11] Add core OpenTelemetry infrastructure --- .../Core/Configuration/TracingOptions.cs | 72 ++++++ src/MongoDB.Driver/MongoDB.Driver.csproj | 1 + src/MongoDB.Driver/MongoTelemetry.cs | 239 ++++++++++++++++++ 3 files changed, 312 insertions(+) create mode 100644 src/MongoDB.Driver/Core/Configuration/TracingOptions.cs create mode 100644 src/MongoDB.Driver/MongoTelemetry.cs diff --git a/src/MongoDB.Driver/Core/Configuration/TracingOptions.cs b/src/MongoDB.Driver/Core/Configuration/TracingOptions.cs new file mode 100644 index 00000000000..988349db443 --- /dev/null +++ b/src/MongoDB.Driver/Core/Configuration/TracingOptions.cs @@ -0,0 +1,72 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using MongoDB.Shared; + +namespace MongoDB.Driver.Core.Configuration; + +/// +/// Tracing-related settings for MongoDB operations. +/// +public sealed class TracingOptions +{ + /// + /// Gets or sets whether tracing is disabled for this client. + /// When set to true, no OpenTelemetry activities will be created for this client's operations. + /// Default is false (tracing enabled if configured via TracerProvider). + /// + public bool Disabled { get; set; } + + /// + /// Gets or sets the maximum length of the db.query.text attribute. + /// Default is 0 (attribute not added). + /// + public int QueryTextMaxLength { get; set; } + + internal TracingOptions Clone() + { + return new TracingOptions + { + QueryTextMaxLength = QueryTextMaxLength, + Disabled = Disabled + }; + } + + /// + /// Determines whether the specified TracingOptions is equal to this instance. + /// + public bool Equals(TracingOptions other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return QueryTextMaxLength == other.QueryTextMaxLength && Disabled == other.Disabled; + } + + /// + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj) || obj is TracingOptions other && Equals(other); + } + + /// + public override int GetHashCode() + { + return new Hasher() + .Hash(QueryTextMaxLength) + .Hash(Disabled) + .GetHashCode(); + } +} \ No newline at end of file diff --git a/src/MongoDB.Driver/MongoDB.Driver.csproj b/src/MongoDB.Driver/MongoDB.Driver.csproj index 34add76dfbe..2bafad80017 100644 --- a/src/MongoDB.Driver/MongoDB.Driver.csproj +++ b/src/MongoDB.Driver/MongoDB.Driver.csproj @@ -26,6 +26,7 @@ + diff --git a/src/MongoDB.Driver/MongoTelemetry.cs b/src/MongoDB.Driver/MongoTelemetry.cs new file mode 100644 index 00000000000..ed35c815ab9 --- /dev/null +++ b/src/MongoDB.Driver/MongoTelemetry.cs @@ -0,0 +1,239 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using MongoDB.Bson; +using MongoDB.Driver.Core.Connections; + +namespace MongoDB.Driver; + +/// +/// Provides access to MongoDB driver's OpenTelemetry instrumentation. +/// +public static class MongoTelemetry +{ + private static readonly string s_driverVersion = ClientDocumentHelper.GetAssemblyVersion(typeof(MongoClient).Assembly); + + /// + /// The ActivitySource used by MongoDB driver for OpenTelemetry tracing. + /// Applications can subscribe to this source to receive MongoDB traces. + /// + public static readonly ActivitySource ActivitySource = new("MongoDB.Driver", s_driverVersion); + + internal static Activity StartOperationActivity(string operationName, string databaseName, string collectionName = null) + { + if (string.IsNullOrEmpty(operationName)) + { + return null; + } + + var spanName = GetSpanName(operationName, databaseName, collectionName); + var activity = ActivitySource.StartActivity(spanName, ActivityKind.Client); + + if (activity?.IsAllDataRequested == true) + { + activity.SetTag("db.system", "mongodb"); + activity.SetTag("db.operation.name", operationName); + activity.SetTag("db.operation.summary", spanName); + + if (!string.IsNullOrEmpty(databaseName)) + { + activity.SetTag("db.namespace", databaseName); + } + + if (!string.IsNullOrEmpty(collectionName)) + { + activity.SetTag("db.collection.name", collectionName); + } + } + + return activity; + } + + internal static Activity StartTransactionActivity() + { + var activity = ActivitySource.StartActivity("transaction", ActivityKind.Client); + + if (activity?.IsAllDataRequested == true) + { + activity.SetTag("db.system", "mongodb"); + } + + return activity; + } + + internal static string GetSpanName(string name, string databaseName, string collectionName) + { + if (!string.IsNullOrEmpty(collectionName)) + { + return $"{name} {databaseName}.{collectionName}"; + } + if (!string.IsNullOrEmpty(databaseName)) + { + return $"{name} {databaseName}"; + } + return name; + } + + internal static Activity StartCommandActivity( + string commandName, + BsonDocument command, + DatabaseNamespace databaseNamespace, + ConnectionId connectionId, + int queryTextMaxLength = 0) + { + var activity = ActivitySource.StartActivity(commandName, ActivityKind.Client); + + if (activity == null) + { + return null; + } + + if (activity.IsAllDataRequested) + { + var collectionName = ExtractCollectionName(command); + activity.SetTag("db.system", "mongodb"); + activity.SetTag("db.command.name", commandName); + activity.SetTag("db.namespace", databaseNamespace.DatabaseName); + + if (!string.IsNullOrEmpty(collectionName)) + { + activity.SetTag("db.collection.name", collectionName); + } + + // db.query.summary uses the full format like operation-level spans + var querySummary = GetSpanName(commandName, databaseNamespace.DatabaseName, collectionName); + activity.SetTag("db.query.summary", querySummary); + + SetConnectionTags(activity, connectionId); + + if (command != null) + { + if (command.TryGetValue("lsid", out var lsid)) + { + // Materialize the lsid to avoid accessing disposed RawBsonDocument later + var materializedLsid = lsid.IsBsonDocument + ? new BsonDocument(lsid.AsBsonDocument) + : lsid; + activity.SetTag("db.mongodb.lsid", materializedLsid); + } + + if (command.TryGetValue("txnNumber", out var txnNumber)) + { + activity.SetTag("db.mongodb.txn_number", txnNumber.ToInt64()); + } + + if (queryTextMaxLength > 0) + { + SetQueryText(activity, command, queryTextMaxLength); + } + } + } + + return activity; + } + + internal static void RecordException(Activity activity, Exception exception) + { + if (activity == null) + { + return; + } + + activity.SetTag("exception.type", exception.GetType().FullName); + activity.SetTag("exception.message", exception.Message); + if (exception.StackTrace != null) + { + activity.SetTag("exception.stacktrace", exception.StackTrace); + } + activity.SetStatus(ActivityStatusCode.Error); + } + + private static void SetConnectionTags(Activity activity, ConnectionId connectionId) + { + var endPoint = connectionId?.ServerId?.EndPoint; + switch (endPoint) + { + case IPEndPoint ipEndPoint: + activity.SetTag("server.address", ipEndPoint.Address.ToString()); + activity.SetTag("server.port", (long)ipEndPoint.Port); + activity.SetTag("network.transport", "tcp"); + break; + case DnsEndPoint dnsEndPoint: + activity.SetTag("server.address", dnsEndPoint.Host); + activity.SetTag("server.port", (long)dnsEndPoint.Port); + activity.SetTag("network.transport", "tcp"); + break; +#if NET5_0_OR_GREATER || NETCOREAPP3_0_OR_GREATER + case UnixDomainSocketEndPoint unixEndPoint: + activity.SetTag("network.transport", "unix"); + activity.SetTag("server.address", unixEndPoint.ToString()); + break; +#endif + } + + if (connectionId != null) + { + if (connectionId.LongServerValue.HasValue) + { + activity.SetTag("db.mongodb.server_connection_id", connectionId.LongServerValue.Value); + } + activity.SetTag("db.mongodb.driver_connection_id", connectionId.LongLocalValue); + } + } + + private static void SetQueryText(Activity activity, BsonDocument command, int maxLength) + { + var commandToLog = FilterSensitiveData(command); + var commandText = commandToLog.ToJson(); + + if (commandText.Length > maxLength) + { + commandText = commandText.Substring(0, maxLength); + } + + activity?.SetTag("db.query.text", commandText); + } + + private static BsonDocument FilterSensitiveData(BsonDocument command) + { + var filtered = new BsonDocument(command); + filtered.Remove("lsid"); + filtered.Remove("$db"); + filtered.Remove("$clusterTime"); + filtered.Remove("signature"); + return filtered; + } + + private static string ExtractCollectionName(BsonDocument command) + { + if (command == null) return null; + + var firstElement = command.GetElement(0); + if (firstElement.Value.IsString) + { + var value = firstElement.Value.AsString; + if (value != "1" && value != "admin" && !string.IsNullOrEmpty(value)) + { + return value; + } + } + + return null; + } +} From fc9f61ff4f1d5fb784e3b0adab284483dcc9ce29 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Tue, 9 Dec 2025 16:59:07 -0500 Subject: [PATCH 03/11] Integrate tracing configuration into MongoClientSettings --- src/MongoDB.Driver/ClusterKey.cs | 4 ++ src/MongoDB.Driver/ClusterRegistry.cs | 3 +- .../Core/Configuration/ClusterBuilder.cs | 16 ++++++++ src/MongoDB.Driver/MongoClient.cs | 39 ++++++++++++------- src/MongoDB.Driver/MongoClientSettings.cs | 17 ++++++++ tests/MongoDB.Driver.Tests/ClusterKeyTests.cs | 2 + .../ClusterRegistryTests.cs | 1 + 7 files changed, 67 insertions(+), 15 deletions(-) diff --git a/src/MongoDB.Driver/ClusterKey.cs b/src/MongoDB.Driver/ClusterKey.cs index dec3b93bad6..60eca94d396 100644 --- a/src/MongoDB.Driver/ClusterKey.cs +++ b/src/MongoDB.Driver/ClusterKey.cs @@ -42,6 +42,7 @@ internal sealed class ClusterKey private readonly bool _loadBalanced; private readonly TimeSpan _localThreshold; private readonly LoggingSettings _loggingSettings; + private readonly TracingOptions _tracingOptions; private readonly int _maxConnecting; private readonly TimeSpan _maxConnectionIdleTime; private readonly TimeSpan _maxConnectionLifeTime; @@ -81,6 +82,7 @@ public ClusterKey( bool loadBalanced, TimeSpan localThreshold, LoggingSettings loggingSettings, + TracingOptions tracingOptions, int maxConnecting, TimeSpan maxConnectionIdleTime, TimeSpan maxConnectionLifeTime, @@ -118,6 +120,7 @@ public ClusterKey( _loadBalanced = loadBalanced; _localThreshold = localThreshold; _loggingSettings = loggingSettings; + _tracingOptions = tracingOptions; _maxConnecting = maxConnecting; _maxConnectionIdleTime = maxConnectionIdleTime; _maxConnectionLifeTime = maxConnectionLifeTime; @@ -159,6 +162,7 @@ public ClusterKey( public bool LoadBalanced => _loadBalanced; public TimeSpan LocalThreshold { get { return _localThreshold; } } public LoggingSettings LoggingSettings { get { return _loggingSettings; } } + public TracingOptions TracingOptions { get { return _tracingOptions; } } public int MaxConnecting{ get { return _maxConnecting; } } public TimeSpan MaxConnectionIdleTime { get { return _maxConnectionIdleTime; } } public TimeSpan MaxConnectionLifeTime { get { return _maxConnectionLifeTime; } } diff --git a/src/MongoDB.Driver/ClusterRegistry.cs b/src/MongoDB.Driver/ClusterRegistry.cs index 49e599be8d1..60abf4c85d5 100644 --- a/src/MongoDB.Driver/ClusterRegistry.cs +++ b/src/MongoDB.Driver/ClusterRegistry.cs @@ -62,7 +62,8 @@ private IClusterInternal CreateCluster(ClusterKey clusterKey) .ConfigureConnectionPool(settings => ConfigureConnectionPool(settings, clusterKey)) .ConfigureConnection(settings => ConfigureConnection(settings, clusterKey)) .ConfigureTcp(settings => ConfigureTcp(settings, clusterKey)) - .ConfigureLoggingSettings(_ => clusterKey.LoggingSettings); + .ConfigureLoggingSettings(_ => clusterKey.LoggingSettings) + .ConfigureTracingOptions(_ => clusterKey.TracingOptions); #pragma warning restore CS0618 // Type or member is obsolete if (clusterKey.UseTls) diff --git a/src/MongoDB.Driver/Core/Configuration/ClusterBuilder.cs b/src/MongoDB.Driver/Core/Configuration/ClusterBuilder.cs index cb868f198ad..db58878eb11 100644 --- a/src/MongoDB.Driver/Core/Configuration/ClusterBuilder.cs +++ b/src/MongoDB.Driver/Core/Configuration/ClusterBuilder.cs @@ -36,6 +36,7 @@ public class ClusterBuilder private ConnectionPoolSettings _connectionPoolSettings; private ConnectionSettings _connectionSettings; private LoggingSettings _loggingSettings; + private TracingOptions _tracingOptions; private ServerSettings _serverSettings; private SslStreamSettings _sslStreamSettings; private Func _streamFactoryWrapper; @@ -123,6 +124,19 @@ public ClusterBuilder ConfigureLoggingSettings(Func + /// Configures the tracing options. + /// + /// The tracing options configurator delegate. + /// A reconfigured cluster builder. + public ClusterBuilder ConfigureTracingOptions(Func configurator) + { + Ensure.IsNotNull(configurator, nameof(configurator)); + + _tracingOptions = configurator(_tracingOptions); + return this; + } + /// /// Configures the server settings. /// @@ -228,6 +242,7 @@ private IConnectionPoolFactory CreateConnectionPoolFactory() _eventAggregator, _clusterSettings.ServerApi, _loggingSettings.ToInternalLoggerFactory(), + _tracingOptions, _tcpStreamSettings.ReadTimeout, _tcpStreamSettings.WriteTimeout); @@ -290,6 +305,7 @@ private IServerMonitorFactory CreateServerMonitorFactory() new EventAggregator(), _clusterSettings.ServerApi, loggerFactory: null, + tracingOptions: new TracingOptions { Disabled = true }, _tcpStreamSettings.ReadTimeout, _tcpStreamSettings.WriteTimeout); diff --git a/src/MongoDB.Driver/MongoClient.cs b/src/MongoDB.Driver/MongoClient.cs index 4b70bfdfd16..bdeffbce99c 100644 --- a/src/MongoDB.Driver/MongoClient.cs +++ b/src/MongoDB.Driver/MongoClient.cs @@ -144,7 +144,7 @@ public ClientBulkWriteResult BulkWrite(IClientSessionHandle session, IReadOnlyLi Ensure.IsNotNull(session, nameof(session)); ThrowIfDisposed(); var operation = CreateClientBulkWriteOperation(models, options); - return ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken); + return ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken, "bulkWrite"); } /// @@ -161,7 +161,7 @@ public Task BulkWriteAsync(IClientSessionHandle session, Ensure.IsNotNull(session, nameof(session)); ThrowIfDisposed(); var operation = CreateClientBulkWriteOperation(models, options); - return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken); + return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken, "bulkWrite"); } /// @@ -341,7 +341,7 @@ public IAsyncCursor ListDatabases( ThrowIfDisposed(); Ensure.IsNotNull(session, nameof(session)); var operation = CreateListDatabasesOperation(options); - return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken); + return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken, "listDatabases"); } /// @@ -377,7 +377,7 @@ public Task> ListDatabasesAsync( Ensure.IsNotNull(session, nameof(session)); ThrowIfDisposed(); var operation = CreateListDatabasesOperation(options); - return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken); + return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken, "listDatabases"); } /// @@ -563,7 +563,7 @@ private ChangeStreamOperation CreateChangeStreamOperation( _settings.RetryReads, _settings.TranslationOptions); - private OperationContext CreateOperationContext(IClientSessionHandle session, TimeSpan? timeout, CancellationToken cancellationToken) + private OperationContext CreateOperationContext(IClientSessionHandle session, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) { var operationContext = session.WrappedCoreSession.CurrentTransaction?.OperationContext; if (operationContext != null && timeout != null) @@ -571,32 +571,43 @@ private OperationContext CreateOperationContext(IClientSessionHandle session, Ti throw new InvalidOperationException("Cannot specify per operation timeout inside transaction."); } - return operationContext?.Fork() ?? new OperationContext(timeout ?? _settings.Timeout, cancellationToken); + var context = operationContext?.Fork() ?? new OperationContext(timeout ?? _settings.Timeout, cancellationToken); + + // Set operation metadata for OpenTelemetry tracing + // Client-level operations (like bulkWrite) use "admin" as database name + if (operationName != null) + { + var tracingOptions = _settings.TracingOptions; + var isTracingEnabled = tracingOptions == null || !tracingOptions.Disabled; + context = context.WithOperationMetadata(operationName, "admin", null, isTracingEnabled); + } + + return context; } - private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, TimeSpan? timeout, CancellationToken cancellationToken) + private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) { var readPreference = session.GetEffectiveReadPreference(_settings.ReadPreference); - using var operationContext = CreateOperationContext(session, timeout, cancellationToken); + using var operationContext = CreateOperationContext(session, timeout, cancellationToken, operationName); return _operationExecutor.ExecuteReadOperation(operationContext, session, operation, readPreference, false); } - private async Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, TimeSpan? timeout, CancellationToken cancellationToken) + private async Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) { var readPreference = session.GetEffectiveReadPreference(_settings.ReadPreference); - using var operationContext = CreateOperationContext(session, timeout, cancellationToken); + using var operationContext = CreateOperationContext(session, timeout, cancellationToken, operationName); return await _operationExecutor.ExecuteReadOperationAsync(operationContext, session, operation, readPreference, false).ConfigureAwait(false); } - private TResult ExecuteWriteOperation(IClientSessionHandle session, IWriteOperation operation, TimeSpan? timeout, CancellationToken cancellationToken) + private TResult ExecuteWriteOperation(IClientSessionHandle session, IWriteOperation operation, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) { - using var operationContext = CreateOperationContext(session, timeout, cancellationToken); + using var operationContext = CreateOperationContext(session, timeout, cancellationToken, operationName); return _operationExecutor.ExecuteWriteOperation(operationContext, session, operation, false); } - private async Task ExecuteWriteOperationAsync(IClientSessionHandle session, IWriteOperation operation, TimeSpan? timeout, CancellationToken cancellationToken) + private async Task ExecuteWriteOperationAsync(IClientSessionHandle session, IWriteOperation operation, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) { - using var operationContext = CreateOperationContext(session, timeout, cancellationToken); + using var operationContext = CreateOperationContext(session, timeout, cancellationToken, operationName); return await _operationExecutor.ExecuteWriteOperationAsync(operationContext, session, operation, false).ConfigureAwait(false); } diff --git a/src/MongoDB.Driver/MongoClientSettings.cs b/src/MongoDB.Driver/MongoClientSettings.cs index 652e1ccf87e..41f54a5ddbf 100644 --- a/src/MongoDB.Driver/MongoClientSettings.cs +++ b/src/MongoDB.Driver/MongoClientSettings.cs @@ -76,6 +76,7 @@ public class MongoClientSettings : IEquatable, IInheritable private string _srvServiceName; private SslSettings _sslSettings; private TimeSpan? _timeout; + private TracingOptions _tracingOptions; private ExpressionTranslationOptions _translationOptions; private bool _useTls; private int _waitQueueSize; @@ -367,6 +368,19 @@ public LoggingSettings LoggingSettings } } + /// + /// Gets or sets the tracing options for OpenTelemetry instrumentation. + /// + public TracingOptions TracingOptions + { + get { return _tracingOptions; } + set + { + if (_isFrozen) { throw new InvalidOperationException("MongoClientSettings is frozen."); } + _tracingOptions = value; + } + } + /// /// Gets or sets the maximum concurrently connecting connections. /// @@ -952,6 +966,7 @@ public MongoClientSettings Clone() clone._loadBalanced = _loadBalanced; clone._localThreshold = _localThreshold; clone._loggingSettings = _loggingSettings; + clone._tracingOptions = _tracingOptions?.Clone(); clone._maxConnecting = _maxConnecting; clone._maxConnectionIdleTime = _maxConnectionIdleTime; clone._maxConnectionLifeTime = _maxConnectionLifeTime; @@ -1023,6 +1038,7 @@ public override bool Equals(object obj) _loadBalanced == rhs._loadBalanced && _localThreshold == rhs._localThreshold && _loggingSettings == rhs._loggingSettings && + object.Equals(_tracingOptions, rhs._tracingOptions) && _maxConnecting == rhs._maxConnecting && _maxConnectionIdleTime == rhs._maxConnectionIdleTime && _maxConnectionLifeTime == rhs._maxConnectionLifeTime && @@ -1258,6 +1274,7 @@ internal ClusterKey ToClusterKey() _loadBalanced, _localThreshold, _loggingSettings, + _tracingOptions, _maxConnecting, _maxConnectionIdleTime, _maxConnectionLifeTime, diff --git a/tests/MongoDB.Driver.Tests/ClusterKeyTests.cs b/tests/MongoDB.Driver.Tests/ClusterKeyTests.cs index b5cba0293ce..a143b5a71df 100644 --- a/tests/MongoDB.Driver.Tests/ClusterKeyTests.cs +++ b/tests/MongoDB.Driver.Tests/ClusterKeyTests.cs @@ -258,6 +258,7 @@ private ClusterKey CreateSubject(string notEqualFieldName = null) loadBalanced, localThreshold, loggingSettings, + tracingOptions: null, maxConnecting, maxConnectionIdleTime, maxConnectionLifeTime, @@ -345,6 +346,7 @@ internal ClusterKey CreateSubjectWith( loadBalanced, localThreshold, loggingSettings, + tracingOptions: null, maxConnecting, maxConnectionIdleTime, maxConnectionLifeTime, diff --git a/tests/MongoDB.Driver.Tests/ClusterRegistryTests.cs b/tests/MongoDB.Driver.Tests/ClusterRegistryTests.cs index 2a63791de2f..2d43e1c11ac 100644 --- a/tests/MongoDB.Driver.Tests/ClusterRegistryTests.cs +++ b/tests/MongoDB.Driver.Tests/ClusterRegistryTests.cs @@ -128,6 +128,7 @@ public void GetOrCreateCluster_should_return_a_cluster_with_the_correct_settings loadBalanced: false, localThreshold: TimeSpan.FromSeconds(4), loggingSettings: null, + tracingOptions: null, maxConnecting: 3, maxConnectionIdleTime: TimeSpan.FromSeconds(5), maxConnectionLifeTime: TimeSpan.FromSeconds(6), From b549899a13998c3b8358249f3b77f59c88be8218 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Tue, 9 Dec 2025 16:59:17 -0500 Subject: [PATCH 04/11] Implement operation-level tracing --- src/MongoDB.Driver/OperationContext.cs | 18 +++ src/MongoDB.Driver/OperationExecutor.cs | 184 ++++++++++++++++++++++-- 2 files changed, 194 insertions(+), 8 deletions(-) diff --git a/src/MongoDB.Driver/OperationContext.cs b/src/MongoDB.Driver/OperationContext.cs index 7d0ce583df8..fea7c6f13da 100644 --- a/src/MongoDB.Driver/OperationContext.cs +++ b/src/MongoDB.Driver/OperationContext.cs @@ -51,6 +51,12 @@ internal OperationContext(IClock clock, long initialTimestamp, TimeSpan? timeout public OperationContext RootContext { get; private init; } + // OpenTelemetry operation metadata + internal string OperationName { get; init; } + internal string DatabaseName { get; init; } + internal string CollectionName { get; init; } + internal bool IsTracingEnabled { get; init; } + public TimeSpan RemainingTimeout { get @@ -213,5 +219,17 @@ public OperationContext WithTimeout(TimeSpan timeout) RootContext = RootContext }; } + + internal OperationContext WithOperationMetadata(string operationName, string databaseName, string collectionName, bool isTracingEnabled) + { + return new OperationContext(Clock, InitialTimestamp, Timeout, CancellationToken) + { + RootContext = RootContext, + OperationName = operationName, + DatabaseName = databaseName, + CollectionName = collectionName, + IsTracingEnabled = isTracingEnabled + }; + } } } diff --git a/src/MongoDB.Driver/OperationExecutor.cs b/src/MongoDB.Driver/OperationExecutor.cs index 929c28b6063..061e18b083f 100644 --- a/src/MongoDB.Driver/OperationExecutor.cs +++ b/src/MongoDB.Driver/OperationExecutor.cs @@ -14,6 +14,7 @@ */ using System; +using System.Diagnostics; using System.Threading.Tasks; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Bindings; @@ -50,8 +51,41 @@ public TResult ExecuteReadOperation( Ensure.IsNotNull(readPreference, nameof(readPreference)); ThrowIfDisposed(); - using var binding = CreateReadBinding(session, readPreference, allowChannelPinning); - return operation.Execute(operationContext, binding); + using var activityScope = TransactionActivityScope.CreateIfNeeded(session); + using var activity = operationContext.IsTracingEnabled + ? MongoTelemetry.StartOperationActivity( + operationContext.OperationName, + operationContext.DatabaseName, + operationContext.CollectionName) + : null; + + try + { + using var binding = CreateReadBinding(session, readPreference, allowChannelPinning); + var result = operation.Execute(operationContext, binding); + activity?.SetStatus(ActivityStatusCode.Ok); + return result; + } + catch (Exception ex) + { + // Only record exceptions that originate at the operation level + // Command-level exceptions (MongoCommandException, MongoWriteException, etc.) + // are already recorded by CommandEventHelper on the command span + if (activity != null) + { + if (!IsCommandLevelException(ex)) + { + MongoTelemetry.RecordException(activity, ex); + } + else + { + // For command-level exceptions, only set error status without recording details + activity.SetStatus(ActivityStatusCode.Error); + } + } + + throw; + } } public async Task ExecuteReadOperationAsync( @@ -67,8 +101,41 @@ public async Task ExecuteReadOperationAsync( Ensure.IsNotNull(readPreference, nameof(readPreference)); ThrowIfDisposed(); - using var binding = CreateReadBinding(session, readPreference, allowChannelPinning); - return await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); + using var activityScope = TransactionActivityScope.CreateIfNeeded(session); + using var activity = operationContext.IsTracingEnabled + ? MongoTelemetry.StartOperationActivity( + operationContext.OperationName, + operationContext.DatabaseName, + operationContext.CollectionName) + : null; + + try + { + using var binding = CreateReadBinding(session, readPreference, allowChannelPinning); + var result = await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); + activity?.SetStatus(ActivityStatusCode.Ok); + return result; + } + catch (Exception ex) + { + // Only record exceptions that originate at the operation level + // Command-level exceptions (MongoCommandException, MongoWriteException, etc.) + // are already recorded by CommandEventHelper on the command span + if (activity != null) + { + if (!IsCommandLevelException(ex)) + { + MongoTelemetry.RecordException(activity, ex); + } + else + { + // For command-level exceptions, only set error status without recording details + activity.SetStatus(ActivityStatusCode.Error); + } + } + + throw; + } } public TResult ExecuteWriteOperation( @@ -82,8 +149,41 @@ public TResult ExecuteWriteOperation( Ensure.IsNotNull(operation, nameof(operation)); ThrowIfDisposed(); - using var binding = CreateReadWriteBinding(session, allowChannelPinning); - return operation.Execute(operationContext, binding); + using var activityScope = TransactionActivityScope.CreateIfNeeded(session); + using var activity = operationContext.IsTracingEnabled + ? MongoTelemetry.StartOperationActivity( + operationContext.OperationName, + operationContext.DatabaseName, + operationContext.CollectionName) + : null; + + try + { + using var binding = CreateReadWriteBinding(session, allowChannelPinning); + var result = operation.Execute(operationContext, binding); + activity?.SetStatus(ActivityStatusCode.Ok); + return result; + } + catch (Exception ex) + { + // Only record exceptions that originate at the operation level + // Command-level exceptions (MongoCommandException, MongoWriteException, etc.) + // are already recorded by CommandEventHelper on the command span + if (activity != null) + { + if (!IsCommandLevelException(ex)) + { + MongoTelemetry.RecordException(activity, ex); + } + else + { + // For command-level exceptions, only set error status without recording details + activity.SetStatus(ActivityStatusCode.Error); + } + } + + throw; + } } public async Task ExecuteWriteOperationAsync( @@ -97,8 +197,41 @@ public async Task ExecuteWriteOperationAsync( Ensure.IsNotNull(operation, nameof(operation)); ThrowIfDisposed(); - using var binding = CreateReadWriteBinding(session, allowChannelPinning); - return await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); + using var activityScope = TransactionActivityScope.CreateIfNeeded(session); + using var activity = operationContext.IsTracingEnabled + ? MongoTelemetry.StartOperationActivity( + operationContext.OperationName, + operationContext.DatabaseName, + operationContext.CollectionName) + : null; + + try + { + using var binding = CreateReadWriteBinding(session, allowChannelPinning); + var result = await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); + activity?.SetStatus(ActivityStatusCode.Ok); + return result; + } + catch (Exception ex) + { + // Only record exceptions that originate at the operation level + // Command-level exceptions (MongoCommandException, MongoWriteException, etc.) + // are already recorded by CommandEventHelper on the command span + if (activity != null) + { + if (!IsCommandLevelException(ex)) + { + MongoTelemetry.RecordException(activity, ex); + } + else + { + // For command-level exceptions, only set error status without recording details + activity.SetStatus(ActivityStatusCode.Error); + } + } + + throw; + } } public IClientSessionHandle StartImplicitSession() @@ -143,5 +276,40 @@ private void ThrowIfDisposed() throw new ObjectDisposedException(nameof(OperationExecutor)); } } + + private static bool IsCommandLevelException(Exception ex) + { + // Command-level exceptions are those that originate from MongoDB server + return ex is MongoServerException; + } + + /// + /// Manages Activity.Current scope when executing operations within a transaction. + /// Temporarily sets Activity.Current to the transaction activity so operation activities nest correctly, + /// then restores to the original value to prevent AsyncLocal flow issues. + /// + private sealed class TransactionActivityScope : IDisposable + { + private readonly Activity _originalActivity; + + private TransactionActivityScope(Activity transactionActivity) + { + _originalActivity = Activity.Current; + Activity.Current = transactionActivity; + } + + public static TransactionActivityScope CreateIfNeeded(IClientSessionHandle session) + { + var transactionActivity = session?.WrappedCoreSession?.CurrentTransaction?.TransactionActivity; + return transactionActivity != null + ? new TransactionActivityScope(transactionActivity) + : null; + } + + public void Dispose() + { + Activity.Current = _originalActivity; + } + } } } From 039aa244e9ba8e7849b86aeaa7edc231cf47f896 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Tue, 9 Dec 2025 16:59:19 -0500 Subject: [PATCH 05/11] Add tracing to collection and database operations --- src/MongoDB.Driver/MongoCollectionImpl.cs | 158 ++++++++++++++-------- src/MongoDB.Driver/MongoDatabase.cs | 78 ++++++----- 2 files changed, 145 insertions(+), 91 deletions(-) diff --git a/src/MongoDB.Driver/MongoCollectionImpl.cs b/src/MongoDB.Driver/MongoCollectionImpl.cs index a083a40c453..e60e5701189 100644 --- a/src/MongoDB.Driver/MongoCollectionImpl.cs +++ b/src/MongoDB.Driver/MongoCollectionImpl.cs @@ -109,13 +109,13 @@ public override IAsyncCursor Aggregate(IClientSessionHandle se if (isAggregateToCollection) { var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); - ExecuteWriteOperation(session, aggregateOperation, options.Timeout, cancellationToken); + ExecuteWriteOperation(session, aggregateOperation, options.Timeout, cancellationToken, "aggregate"); return CreateAggregateToCollectionResultCursor(session, renderedPipeline, options); } else { var aggregateOperation = CreateAggregateOperation(renderedPipeline, options); - return ExecuteReadOperation(session, aggregateOperation, options.Timeout, cancellationToken); + return ExecuteReadOperation(session, aggregateOperation, options.Timeout, cancellationToken, "aggregate"); } } @@ -136,13 +136,13 @@ public override async Task> AggregateAsync(IClien if (isAggregateToCollection) { var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); - await ExecuteWriteOperationAsync(session, aggregateOperation, options.Timeout, cancellationToken).ConfigureAwait(false); + await ExecuteWriteOperationAsync(session, aggregateOperation, options.Timeout, cancellationToken, "aggregate").ConfigureAwait(false); return CreateAggregateToCollectionResultCursor(session, renderedPipeline, options); } else { var aggregateOperation = CreateAggregateOperation(renderedPipeline, options); - return await ExecuteReadOperationAsync(session, aggregateOperation, options.Timeout, cancellationToken).ConfigureAwait(false); + return await ExecuteReadOperationAsync(session, aggregateOperation, options.Timeout, cancellationToken, "aggregate").ConfigureAwait(false); } } @@ -166,7 +166,7 @@ public override void AggregateToCollection(IClientSessionHandle session } var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); - ExecuteWriteOperation(session, aggregateOperation, options.Timeout, cancellationToken); + ExecuteWriteOperation(session, aggregateOperation, options.Timeout, cancellationToken, "aggregate"); } public override async Task AggregateToCollectionAsync(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) @@ -208,10 +208,13 @@ public override BulkWriteResult BulkWrite(IClientSessionHandle sessio throw new ArgumentException("Must contain at least 1 request.", nameof(requests)); } + // Infer operation name from write model types + var operationName = GetOperationNameFromWriteModels(requestsArray); + var operation = CreateBulkWriteOperation(session, requestsArray, options); try { - var result = ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken); + var result = ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken, operationName); return BulkWriteResult.FromCore(result, requestsArray); } catch (MongoBulkWriteOperationException ex) @@ -236,10 +239,13 @@ public override async Task> BulkWriteAsync(IClientSes throw new ArgumentException("Must contain at least 1 request.", nameof(requests)); } + // Infer operation name from write model types + var operationName = GetOperationNameFromWriteModels(requestsArray); + var operation = CreateBulkWriteOperation(session, requestsArray, options); try { - var result = await ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken).ConfigureAwait(false); + var result = await ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken, operationName).ConfigureAwait(false); return BulkWriteResult.FromCore(result, requestsArray); } catch (MongoBulkWriteOperationException ex) @@ -262,7 +268,7 @@ public override long Count(IClientSessionHandle session, FilterDefinition CountAsync(IClientSessionHandle session, FilterDefini Ensure.IsNotNull(filter, nameof(filter)); var operation = CreateCountOperation(filter, options); - return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken); + return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken, "count"); } public override long CountDocuments(FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) @@ -294,7 +300,7 @@ public override long CountDocuments(IClientSessionHandle session, FilterDefiniti Ensure.IsNotNull(filter, nameof(filter)); var operation = CreateCountDocumentsOperation(filter, options); - return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken); + return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken, "countDocuments"); } public override async Task CountDocumentsAsync(FilterDefinition filter, CountOptions options, CancellationToken cancellationToken = default) @@ -309,7 +315,7 @@ public override Task CountDocumentsAsync(IClientSessionHandle session, Fil Ensure.IsNotNull(filter, nameof(filter)); var operation = CreateCountDocumentsOperation(filter, options); - return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken); + return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken, "countDocuments"); } public override IAsyncCursor Distinct(FieldDefinition field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default) @@ -325,7 +331,7 @@ public override IAsyncCursor Distinct(IClientSessionHandle sessi Ensure.IsNotNull(filter, nameof(filter)); var operation = CreateDistinctOperation(field, filter, options); - return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken); + return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken, "distinct"); } public override async Task> DistinctAsync(FieldDefinition field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default) @@ -341,7 +347,7 @@ public override Task> DistinctAsync(IClientSessionH Ensure.IsNotNull(filter, nameof(filter)); var operation = CreateDistinctOperation(field, filter, options); - return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken); + return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken, "distinct"); } public override IAsyncCursor DistinctMany(FieldDefinition> field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default) @@ -357,7 +363,7 @@ public override IAsyncCursor DistinctMany(IClientSessionHandle ses Ensure.IsNotNull(filter, nameof(filter)); var operation = CreateDistinctManyOperation(field, filter, options); - return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken); + return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken, "distinct"); } public override async Task> DistinctManyAsync(FieldDefinition> field, FilterDefinition filter, DistinctOptions options, CancellationToken cancellationToken = default) @@ -373,21 +379,21 @@ public override Task> DistinctManyAsync(IClientSessio Ensure.IsNotNull(filter, nameof(filter)); var operation = CreateDistinctManyOperation(field, filter, options); - return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken); + return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken, "distinct"); } public override long EstimatedDocumentCount(EstimatedDocumentCountOptions options, CancellationToken cancellationToken = default) { using var session = _operationExecutor.StartImplicitSession(); var operation = CreateEstimatedDocumentCountOperation(options); - return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken); + return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken, "count"); } public override async Task EstimatedDocumentCountAsync(EstimatedDocumentCountOptions options, CancellationToken cancellationToken = default) { using var session = _operationExecutor.StartImplicitSession(); var operation = CreateEstimatedDocumentCountOperation(options); - return await ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken).ConfigureAwait(false); + return await ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken, "count").ConfigureAwait(false); } public override IAsyncCursor FindSync(FilterDefinition filter, FindOptions options, CancellationToken cancellationToken = default) @@ -402,7 +408,7 @@ public override IAsyncCursor FindSync(IClientSessionHa Ensure.IsNotNull(filter, nameof(filter)); var operation = CreateFindOperation(filter, options); - return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken); + return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken, "find"); } public override async Task> FindAsync(FilterDefinition filter, FindOptions options, CancellationToken cancellationToken = default) @@ -417,7 +423,7 @@ public override Task> FindAsync(IClientSe Ensure.IsNotNull(filter, nameof(filter)); var operation = CreateFindOperation(filter, options); - return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken); + return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken, "find"); } public override TProjection FindOneAndDelete(FilterDefinition filter, FindOneAndDeleteOptions options, CancellationToken cancellationToken = default) @@ -432,7 +438,7 @@ public override TProjection FindOneAndDelete(IClientSessionHandle s Ensure.IsNotNull(filter, nameof(filter)); var operation = CreateFindOneAndDeleteOperation(filter, options); - return ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken); + return ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken, "findAndModify"); } public override async Task FindOneAndDeleteAsync(FilterDefinition filter, FindOneAndDeleteOptions options, CancellationToken cancellationToken = default) @@ -447,7 +453,7 @@ public override Task FindOneAndDeleteAsync(IClientSess Ensure.IsNotNull(filter, nameof(filter)); var operation = CreateFindOneAndDeleteOperation(filter, options); - return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken); + return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken, "findAndModify"); } public override TProjection FindOneAndReplace(FilterDefinition filter, TDocument replacement, FindOneAndReplaceOptions options, CancellationToken cancellationToken = default) @@ -463,7 +469,7 @@ public override TProjection FindOneAndReplace(IClientSessionHandle var replacementObject = Ensure.IsNotNull((object)replacement, nameof(replacement)); // only box once if it's a struct var operation = CreateFindOneAndReplaceOperation(filter, replacementObject, options); - return ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken); + return ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken, "findAndModify"); } public override async Task FindOneAndReplaceAsync(FilterDefinition filter, TDocument replacement, FindOneAndReplaceOptions options, CancellationToken cancellationToken = default) @@ -479,7 +485,7 @@ public override Task FindOneAndReplaceAsync(IClientSes var replacementObject = Ensure.IsNotNull((object)replacement, nameof(replacement)); // only box once if it's a struct var operation = CreateFindOneAndReplaceOperation(filter, replacementObject, options); - return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken); + return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken, "findAndModify"); } public override TProjection FindOneAndUpdate(FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options, CancellationToken cancellationToken = default) @@ -501,7 +507,7 @@ public override TProjection FindOneAndUpdate(IClientSessionHandle s } var operation = CreateFindOneAndUpdateOperation(filter, update, options); - return ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken); + return ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken, "findAndModify"); } public override async Task FindOneAndUpdateAsync(FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options, CancellationToken cancellationToken = default) @@ -522,7 +528,7 @@ public override Task FindOneAndUpdateAsync(IClientSess } var operation = CreateFindOneAndUpdateOperation(filter, update, options); - return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken); + return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken, "findAndModify"); } [Obsolete("Use Aggregation pipeline instead.")] @@ -547,12 +553,12 @@ public override IAsyncCursor MapReduce(IClientSessionHandle se if (outputOptions == MapReduceOutputOptions.Inline) { var operation = CreateMapReduceOperation(map, reduce, options, resultSerializer, renderArgs); - return ExecuteReadOperation(session, operation, options.Timeout, cancellationToken); + return ExecuteReadOperation(session, operation, options.Timeout, cancellationToken, "mapReduce"); } else { var mapReduceOperation = CreateMapReduceOutputToCollectionOperation(map, reduce, options, outputOptions, renderArgs); - ExecuteWriteOperation(session, mapReduceOperation, options.Timeout, cancellationToken); + ExecuteWriteOperation(session, mapReduceOperation, options.Timeout, cancellationToken, "mapReduce"); return CreateMapReduceOutputToCollectionResultCursor(session, options, mapReduceOperation.OutputCollectionNamespace, resultSerializer); } } @@ -579,12 +585,12 @@ public override async Task> MapReduceAsync(IClien if (outputOptions == MapReduceOutputOptions.Inline) { var operation = CreateMapReduceOperation(map, reduce, options, resultSerializer, renderArgs); - return await ExecuteReadOperationAsync(session, operation, options.Timeout, cancellationToken).ConfigureAwait(false); + return await ExecuteReadOperationAsync(session, operation, options.Timeout, cancellationToken, "mapReduce").ConfigureAwait(false); } else { var mapReduceOperation = CreateMapReduceOutputToCollectionOperation(map, reduce, options, outputOptions, renderArgs); - await ExecuteWriteOperationAsync(session, mapReduceOperation, options.Timeout, cancellationToken).ConfigureAwait(false); + await ExecuteWriteOperationAsync(session, mapReduceOperation, options.Timeout, cancellationToken, "mapReduce").ConfigureAwait(false); return CreateMapReduceOutputToCollectionResultCursor(session, options, mapReduceOperation.OutputCollectionNamespace, resultSerializer); } } @@ -1199,7 +1205,7 @@ private IAsyncCursor CreateMapReduceOutputToCollectionResultCursor(IClientSessionHandle session, IReadOperation operation, TimeSpan? timeout, CancellationToken cancellationToken) - => ExecuteReadOperation(session, operation, null, timeout, cancellationToken); + private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) + => ExecuteReadOperation(session, operation, null, timeout, cancellationToken, operationName); - private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, ReadPreference explicitReadPreference, TimeSpan? timeout, CancellationToken cancellationToken) + private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, ReadPreference explicitReadPreference, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) { var readPreference = explicitReadPreference ?? session.GetEffectiveReadPreference(_settings.ReadPreference); - using var operationContext = CreateOperationContext(session, timeout, cancellationToken); + using var operationContext = CreateOperationContext(session, timeout, cancellationToken, operationName); return _operationExecutor.ExecuteReadOperation(operationContext, session, operation, readPreference, true); } - private Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, TimeSpan? timeout, CancellationToken cancellationToken) - => ExecuteReadOperationAsync(session, operation, null, timeout, cancellationToken); + private Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) + => ExecuteReadOperationAsync(session, operation, null, timeout, cancellationToken, operationName); - private async Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, ReadPreference explicitReadPreference, TimeSpan? timeout, CancellationToken cancellationToken) + private async Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, ReadPreference explicitReadPreference, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) { var readPreference = explicitReadPreference ?? session.GetEffectiveReadPreference(_settings.ReadPreference); - using var operationContext = CreateOperationContext(session, timeout, cancellationToken); + using var operationContext = CreateOperationContext(session, timeout, cancellationToken, operationName); return await _operationExecutor.ExecuteReadOperationAsync(operationContext, session, operation, readPreference, true).ConfigureAwait(false); } - private TResult ExecuteWriteOperation(IClientSessionHandle session, IWriteOperation operation, TimeSpan? timeout, CancellationToken cancellationToken) + private TResult ExecuteWriteOperation(IClientSessionHandle session, IWriteOperation operation, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) { - using var operationContext = CreateOperationContext(session, timeout, cancellationToken); + using var operationContext = CreateOperationContext(session, timeout, cancellationToken, operationName); return _operationExecutor.ExecuteWriteOperation(operationContext, session, operation, true); } - private async Task ExecuteWriteOperationAsync(IClientSessionHandle session, IWriteOperation operation, TimeSpan? timeout, CancellationToken cancellationToken) + private async Task ExecuteWriteOperationAsync(IClientSessionHandle session, IWriteOperation operation, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) { - using var operationContext = CreateOperationContext(session, timeout, cancellationToken); + using var operationContext = CreateOperationContext(session, timeout, cancellationToken, operationName); return await _operationExecutor.ExecuteWriteOperationAsync(operationContext, session, operation, true).ConfigureAwait(false); } + private string GetOperationNameFromWriteModels(WriteModel[] requests) + { + // Check if all requests are of the same type + var firstType = requests[0].ModelType; + var allSameType = requests.All(r => r.ModelType == firstType); + + if (allSameType) + { + return firstType switch + { + WriteModelType.InsertOne => "insert", + WriteModelType.DeleteOne => "delete", + WriteModelType.DeleteMany => "delete", + WriteModelType.UpdateOne => "update", + WriteModelType.UpdateMany => "update", + WriteModelType.ReplaceOne => "update", + _ => "bulkWrite" + }; + } + + // Mixed operation types + return "bulkWrite"; + } + private MessageEncoderSettings GetMessageEncoderSettings() { var messageEncoderSettings = new MessageEncoderSettings @@ -1402,7 +1446,7 @@ public override IEnumerable CreateMany( Ensure.IsNotNull(models, nameof(models)); var operation = CreateCreateIndexesOperation(models, options); - _collection.ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken); + _collection.ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken, "createIndexes"); return operation.Requests.Select(x => x.GetIndexName()); } @@ -1431,7 +1475,7 @@ public override async Task> CreateManyAsync( Ensure.IsNotNull(models, nameof(models)); var operation = CreateCreateIndexesOperation(models, options); - await _collection.ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken).ConfigureAwait(false); + await _collection.ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken, "createIndexes").ConfigureAwait(false); return operation.Requests.Select(x => x.GetIndexName()); } @@ -1451,7 +1495,7 @@ public override void DropAll(IClientSessionHandle session, DropIndexOptions opti { Ensure.IsNotNull(session, nameof(session)); var operation = CreateDropAllOperation(options); - _collection.ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken); + _collection.ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken, "dropIndexes"); } public override Task DropAllAsync(CancellationToken cancellationToken) @@ -1470,7 +1514,7 @@ public override Task DropAllAsync(IClientSessionHandle session, DropIndexOptions { Ensure.IsNotNull(session, nameof(session)); var operation = CreateDropAllOperation(options); - return _collection.ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken); + return _collection.ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken, "dropIndexes"); } public override void DropOne(string name, CancellationToken cancellationToken = default) @@ -1495,7 +1539,7 @@ public override void DropOne(IClientSessionHandle session, string name, DropInde } var operation = CreateDropOneOperation(name, options); - _collection.ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken); + _collection.ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken, "dropIndexes"); } public override Task DropOneAsync(string name, CancellationToken cancellationToken = default) @@ -1520,7 +1564,7 @@ public override Task DropOneAsync(IClientSessionHandle session, string name, Dro } var operation = CreateDropOneOperation(name, options); - return _collection.ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken); + return _collection.ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken, "dropIndexes"); } public override IAsyncCursor List(CancellationToken cancellationToken = default) @@ -1539,7 +1583,7 @@ public override IAsyncCursor List(IClientSessionHandle session, Li { Ensure.IsNotNull(session, nameof(session)); var operation = CreateListIndexesOperation(options); - return _collection.ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken); + return _collection.ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken, "listIndexes"); } public override Task> ListAsync(CancellationToken cancellationToken = default) @@ -1558,7 +1602,7 @@ public override Task> ListAsync(IClientSessionHandle { Ensure.IsNotNull(session, nameof(session)); var operation = CreateListIndexesOperation(options); - return _collection.ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken); + return _collection.ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken, "listIndexes"); } // private methods @@ -1660,7 +1704,7 @@ public IEnumerable CreateMany(IEnumerable models using var session = _collection._operationExecutor.StartImplicitSession(); var operation = CreateCreateIndexesOperation(models); // TODO: CSOT: find a way to add timeout parameter to the interface method - var result = _collection.ExecuteWriteOperation(session, operation, null, cancellationToken); + var result = _collection.ExecuteWriteOperation(session, operation, null, cancellationToken, "createSearchIndexes"); return GetIndexNames(result); } @@ -1669,7 +1713,7 @@ public async Task> CreateManyAsync(IEnumerable List(string indexName, AggregateOptions aggregateOptions = null, CancellationToken cancellationToken = default) @@ -1722,7 +1766,7 @@ public void Update(string indexName, BsonDocument definition, CancellationToken using var session = _collection._operationExecutor.StartImplicitSession(); var operation = new UpdateSearchIndexOperation(_collection.CollectionNamespace, indexName, definition, _collection._messageEncoderSettings); // TODO: CSOT: find a way to add timeout parameter to the interface method - _collection.ExecuteWriteOperation(session, operation, null, cancellationToken); + _collection.ExecuteWriteOperation(session, operation, null, cancellationToken, "updateSearchIndex"); } public async Task UpdateAsync(string indexName, BsonDocument definition, CancellationToken cancellationToken = default) @@ -1730,7 +1774,7 @@ public async Task UpdateAsync(string indexName, BsonDocument definition, Cancell using var session = _collection._operationExecutor.StartImplicitSession(); var operation = new UpdateSearchIndexOperation(_collection.CollectionNamespace, indexName, definition, _collection._messageEncoderSettings); // TODO: CSOT: find a way to add timeout parameter to the interface method - await _collection.ExecuteWriteOperationAsync(session, operation, null, cancellationToken).ConfigureAwait(false); + await _collection.ExecuteWriteOperationAsync(session, operation, null, cancellationToken, "updateSearchIndex").ConfigureAwait(false); } // private methods diff --git a/src/MongoDB.Driver/MongoDatabase.cs b/src/MongoDB.Driver/MongoDatabase.cs index 31b872da989..8ccc9aac2b6 100644 --- a/src/MongoDB.Driver/MongoDatabase.cs +++ b/src/MongoDB.Driver/MongoDatabase.cs @@ -73,13 +73,13 @@ public IAsyncCursor Aggregate(IClientSessionHandle session, Pi if (isAggregateToCollection) { var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); - ExecuteWriteOperation(session, aggregateOperation, options.Timeout, cancellationToken); + ExecuteWriteOperation(session, aggregateOperation, options.Timeout, cancellationToken, "aggregate"); return CreateAggregateToCollectionResultCursor(session, renderedPipeline, options); } else { var aggregateOperation = CreateAggregateOperation(renderedPipeline, options); - return ExecuteReadOperation(session, aggregateOperation, options.Timeout, cancellationToken); + return ExecuteReadOperation(session, aggregateOperation, options.Timeout, cancellationToken, "aggregate"); } } @@ -100,13 +100,13 @@ public async Task> AggregateAsync(IClientSessionH if (isAggregateToCollection) { var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); - await ExecuteWriteOperationAsync(session, aggregateOperation, options.Timeout, cancellationToken).ConfigureAwait(false); + await ExecuteWriteOperationAsync(session, aggregateOperation, options.Timeout, cancellationToken, "aggregate").ConfigureAwait(false); return CreateAggregateToCollectionResultCursor(session, renderedPipeline, options); } else { var aggregateOperation = CreateAggregateOperation(renderedPipeline, options); - return await ExecuteReadOperationAsync(session, aggregateOperation, options.Timeout, cancellationToken).ConfigureAwait(false); + return await ExecuteReadOperationAsync(session, aggregateOperation, options.Timeout, cancellationToken, "aggregate").ConfigureAwait(false); } } @@ -130,7 +130,7 @@ public void AggregateToCollection(IClientSessionHandle session, Pipelin } var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); - ExecuteWriteOperation(session, aggregateOperation, options.Timeout, cancellationToken); + ExecuteWriteOperation(session, aggregateOperation, options.Timeout, cancellationToken, "aggregate"); } public async Task AggregateToCollectionAsync(PipelineDefinition pipeline, AggregateOptions options, CancellationToken cancellationToken = default) @@ -153,7 +153,7 @@ public Task AggregateToCollectionAsync(IClientSessionHandle session, Pi } var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options); - return ExecuteWriteOperationAsync(session, aggregateOperation, options.Timeout, cancellationToken); + return ExecuteWriteOperationAsync(session, aggregateOperation, options.Timeout, cancellationToken, "aggregate"); } public void CreateCollection(string name, CreateCollectionOptions options, CancellationToken cancellationToken) @@ -244,7 +244,7 @@ public void CreateView(IClientSessionHandle session, string Ensure.IsNotNull(pipeline, nameof(pipeline)); var operation = CreateCreateViewOperation(viewName, viewOn, pipeline, options); - ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken); + ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken, "create", viewName); } public async Task CreateViewAsync(string viewName, string viewOn, PipelineDefinition pipeline, CreateViewOptions options = null, CancellationToken cancellationToken = default) @@ -261,7 +261,7 @@ public Task CreateViewAsync(IClientSessionHandle session, st Ensure.IsNotNull(pipeline, nameof(pipeline)); var operation = CreateCreateViewOperation(viewName, viewOn, pipeline, options); - return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken); + return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken, "create", viewName); } public void DropCollection(string name, CancellationToken cancellationToken) @@ -288,7 +288,7 @@ public void DropCollection(IClientSessionHandle session, string name, DropCollec var collectionNamespace = new CollectionNamespace(_databaseNamespace, name); var encryptedFields = GetEffectiveEncryptedFields(session, collectionNamespace, options, cancellationToken); var operation = CreateDropCollectionOperation(collectionNamespace, encryptedFields); - ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken); + ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken, "dropCollection", name); } public Task DropCollectionAsync(string name, CancellationToken cancellationToken) @@ -311,7 +311,7 @@ public async Task DropCollectionAsync(IClientSessionHandle session, string name, var collectionNamespace = new CollectionNamespace(_databaseNamespace, name); var encryptedFields = await GetEffectiveEncryptedFieldsAsync(session, collectionNamespace, options, cancellationToken).ConfigureAwait(false); var operation = CreateDropCollectionOperation(collectionNamespace, encryptedFields); - await ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken).ConfigureAwait(false); + await ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken, "dropCollection", name).ConfigureAwait(false); } public IMongoCollection GetCollection(string name, MongoCollectionSettings settings) @@ -338,7 +338,7 @@ public IAsyncCursor ListCollectionNames(IClientSessionHandle session, Li Ensure.IsNotNull(session, nameof(session)); var operation = CreateListCollectionNamesOperation(options); var readPreference = session.GetEffectiveReadPreference(ReadPreference.Primary); - var cursor = ExecuteReadOperation(session, operation, readPreference, options?.Timeout, cancellationToken); + var cursor = ExecuteReadOperation(session, operation, readPreference, options?.Timeout, cancellationToken, "listCollections"); return new BatchTransformingAsyncCursor(cursor, ExtractCollectionNames); } @@ -353,7 +353,7 @@ public async Task> ListCollectionNamesAsync(IClientSessionH Ensure.IsNotNull(session, nameof(session)); var operation = CreateListCollectionNamesOperation(options); var readPreference = session.GetEffectiveReadPreference(ReadPreference.Primary); - var cursor = await ExecuteReadOperationAsync(session, operation, readPreference, options?.Timeout, cancellationToken).ConfigureAwait(false); + var cursor = await ExecuteReadOperationAsync(session, operation, readPreference, options?.Timeout, cancellationToken, "listCollections").ConfigureAwait(false); return new BatchTransformingAsyncCursor(cursor, ExtractCollectionNames); } @@ -368,7 +368,7 @@ public IAsyncCursor ListCollections(IClientSessionHandle session, Ensure.IsNotNull(session, nameof(session)); var operation = CreateListCollectionsOperation(options); var readPreference = session.GetEffectiveReadPreference(ReadPreference.Primary); - return ExecuteReadOperation(session, operation, readPreference, options?.Timeout, cancellationToken); + return ExecuteReadOperation(session, operation, readPreference, options?.Timeout, cancellationToken, "listCollections"); } public async Task> ListCollectionsAsync(ListCollectionsOptions options, CancellationToken cancellationToken) @@ -382,7 +382,7 @@ public Task> ListCollectionsAsync(IClientSessionHandl Ensure.IsNotNull(session, nameof(session)); var operation = CreateListCollectionsOperation(options); var readPreference = session.GetEffectiveReadPreference(ReadPreference.Primary); - return ExecuteReadOperationAsync(session, operation, readPreference, options?.Timeout, cancellationToken); + return ExecuteReadOperationAsync(session, operation, readPreference, options?.Timeout, cancellationToken, "listCollections"); } public void RenameCollection(string oldName, string newName, RenameCollectionOptions options, CancellationToken cancellationToken) @@ -398,7 +398,7 @@ public void RenameCollection(IClientSessionHandle session, string oldName, strin Ensure.IsNotNullOrEmpty(newName, nameof(newName)); var operation = CreateRenameCollectionOperation(oldName, newName, options); - ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken); + ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken, "renameCollection"); } public async Task RenameCollectionAsync(string oldName, string newName, RenameCollectionOptions options, CancellationToken cancellationToken) @@ -414,7 +414,7 @@ public Task RenameCollectionAsync(IClientSessionHandle session, string oldName, Ensure.IsNotNullOrEmpty(newName, nameof(newName)); var operation = CreateRenameCollectionOperation(oldName, newName, options); - return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken); + return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken, "renameCollection"); } public TResult RunCommand(Command command, ReadPreference readPreference = null, CancellationToken cancellationToken = default) @@ -480,7 +480,7 @@ public IChangeStreamCursor Watch( Ensure.IsNotNull(pipeline, nameof(pipeline)); var operation = CreateChangeStreamOperation(pipeline, options); - return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken); + return ExecuteReadOperation(session, operation, options?.Timeout, cancellationToken, "aggregate"); } public async Task> WatchAsync( @@ -502,7 +502,7 @@ public Task> WatchAsync( Ensure.IsNotNull(pipeline, nameof(pipeline)); var operation = CreateChangeStreamOperation(pipeline, options); - return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken); + return ExecuteReadOperationAsync(session, operation, options?.Timeout, cancellationToken, "aggregate"); } public IMongoDatabase WithReadConcern(ReadConcern readConcern) @@ -608,13 +608,13 @@ private AggregateToCollectionOperation CreateAggregateToCollectionOperation(IClientSessionHandle session, string name, CreateCollectionOptions options, CancellationToken cancellationToken) { var operation = CreateCreateCollectionOperation(name, options); - ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken); + ExecuteWriteOperation(session, operation, options?.Timeout, cancellationToken, "createCollection", name); } private Task CreateCollectionHelperAsync(IClientSessionHandle session, string name, CreateCollectionOptions options, CancellationToken cancellationToken) { var operation = CreateCreateCollectionOperation(name, options); - return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken); + return ExecuteWriteOperationAsync(session, operation, options?.Timeout, cancellationToken, "createCollection", name); } private IWriteOperation CreateCreateCollectionOperation(string name, CreateCollectionOptions options) @@ -755,7 +755,7 @@ private ChangeStreamOperation CreateChangeStreamOperation( translationOptions); } - private OperationContext CreateOperationContext(IClientSessionHandle session, TimeSpan? timeout, CancellationToken cancellationToken) + private OperationContext CreateOperationContext(IClientSessionHandle session, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null, string collectionName = null) { var operationContext = session.WrappedCoreSession.CurrentTransaction?.OperationContext; if (operationContext != null && timeout != null) @@ -763,38 +763,48 @@ private OperationContext CreateOperationContext(IClientSessionHandle session, Ti throw new InvalidOperationException("Cannot specify per operation timeout inside transaction."); } - return operationContext?.Fork() ?? new OperationContext(timeout ?? _settings.Timeout, cancellationToken); + var context = operationContext?.Fork() ?? new OperationContext(timeout ?? _settings.Timeout, cancellationToken); + + // Set operation metadata for OpenTelemetry tracing + if (operationName != null) + { + var tracingOptions = _client.Settings.TracingOptions; + var isTracingEnabled = tracingOptions == null || !tracingOptions.Disabled; + context = context.WithOperationMetadata(operationName, _databaseNamespace.DatabaseName, collectionName, isTracingEnabled); + } + + return context; } - private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, TimeSpan? timeout, CancellationToken cancellationToken) - => ExecuteReadOperation(session, operation, null, timeout, cancellationToken); + private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) + => ExecuteReadOperation(session, operation, null, timeout, cancellationToken, operationName); - private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, ReadPreference explicitReadPreference, TimeSpan? timeout, CancellationToken cancellationToken) + private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, ReadPreference explicitReadPreference, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) { var readPreference = explicitReadPreference ?? session.GetEffectiveReadPreference(_settings.ReadPreference); - using var operationContext = CreateOperationContext(session, timeout, cancellationToken); + using var operationContext = CreateOperationContext(session, timeout, cancellationToken, operationName); return _operationExecutor.ExecuteReadOperation(operationContext, session, operation, readPreference, true); } - private Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, TimeSpan? timeout, CancellationToken cancellationToken) - => ExecuteReadOperationAsync(session, operation, null, timeout, cancellationToken); + private Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) + => ExecuteReadOperationAsync(session, operation, null, timeout, cancellationToken, operationName); - private async Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, ReadPreference explicitReadPreference, TimeSpan? timeout, CancellationToken cancellationToken) + private async Task ExecuteReadOperationAsync(IClientSessionHandle session, IReadOperation operation, ReadPreference explicitReadPreference, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) { var readPreference = explicitReadPreference ?? session.GetEffectiveReadPreference(_settings.ReadPreference); - using var operationContext = CreateOperationContext(session, timeout, cancellationToken); + using var operationContext = CreateOperationContext(session, timeout, cancellationToken, operationName); return await _operationExecutor.ExecuteReadOperationAsync(operationContext, session, operation, readPreference, true).ConfigureAwait(false); } - private TResult ExecuteWriteOperation(IClientSessionHandle session, IWriteOperation operation, TimeSpan? timeout, CancellationToken cancellationToken) + private TResult ExecuteWriteOperation(IClientSessionHandle session, IWriteOperation operation, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null, string collectionName = null) { - using var operationContext = CreateOperationContext(session, timeout, cancellationToken); + using var operationContext = CreateOperationContext(session, timeout, cancellationToken, operationName, collectionName); return _operationExecutor.ExecuteWriteOperation(operationContext, session, operation, true); } - private async Task ExecuteWriteOperationAsync(IClientSessionHandle session, IWriteOperation operation, TimeSpan? timeout, CancellationToken cancellationToken) + private async Task ExecuteWriteOperationAsync(IClientSessionHandle session, IWriteOperation operation, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null, string collectionName = null) { - using var operationContext = CreateOperationContext(session, timeout, cancellationToken); + using var operationContext = CreateOperationContext(session, timeout, cancellationToken, operationName, collectionName); return await _operationExecutor.ExecuteWriteOperationAsync(operationContext, session, operation, true).ConfigureAwait(false); } From a075ce1751c046583677ecc8e43ff2d1ce4d2708 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Tue, 9 Dec 2025 16:59:21 -0500 Subject: [PATCH 06/11] Implement session and transaction tracing --- src/MongoDB.Driver/ClientSessionHandle.cs | 7 +- .../Core/Bindings/CoreSession.cs | 157 +++++++++++++++++- .../Core/Bindings/CoreTransaction.cs | 41 +++++ .../Core/Bindings/ICoreSessionInternal.cs | 1 + .../Core/Bindings/NoCoreSession.cs | 5 + .../Core/Bindings/WrappingCoreSession.cs | 6 + 6 files changed, 209 insertions(+), 8 deletions(-) diff --git a/src/MongoDB.Driver/ClientSessionHandle.cs b/src/MongoDB.Driver/ClientSessionHandle.cs index 144e6a94991..ef6be0ef19a 100644 --- a/src/MongoDB.Driver/ClientSessionHandle.cs +++ b/src/MongoDB.Driver/ClientSessionHandle.cs @@ -158,7 +158,12 @@ public IClientSessionHandle Fork() public void StartTransaction(TransactionOptions transactionOptions = null) { var effectiveTransactionOptions = GetEffectiveTransactionOptions(transactionOptions); - _coreSession.StartTransaction(effectiveTransactionOptions); + + // Check if tracing is enabled for this client + var tracingOptions = _client.Settings.TracingOptions; + var isTracingEnabled = tracingOptions == null || !tracingOptions.Disabled; + + ((ICoreSessionInternal)_coreSession).StartTransaction(effectiveTransactionOptions, isTracingEnabled); } /// diff --git a/src/MongoDB.Driver/Core/Bindings/CoreSession.cs b/src/MongoDB.Driver/Core/Bindings/CoreSession.cs index 954c639e244..15a3ab9735c 100644 --- a/src/MongoDB.Driver/Core/Bindings/CoreSession.cs +++ b/src/MongoDB.Driver/Core/Bindings/CoreSession.cs @@ -14,10 +14,12 @@ */ using System; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Driver; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Operations; @@ -190,6 +192,20 @@ void ICoreSessionInternal.AbortTransaction(AbortTransactionOptions options, Canc _currentTransaction.SetState(CoreTransactionState.Aborted); // The transaction is aborted.The session MUST be unpinned regardless // of whether the abortTransaction command succeeds or fails + if (_currentTransaction.TransactionActivity != null) + { + var transactionActivity = _currentTransaction.TransactionActivity; + _currentTransaction.TransactionActivity = null; + + // Set status to Ok for successfully aborted transaction + transactionActivity.SetStatus(ActivityStatusCode.Ok); + + // Dispose the transaction activity. Note: Activity.Current was already restored to the + // parent in StartTransaction() to prevent AsyncLocal flow issues, so the transaction + // activity was never persisted in Activity.Current. Set it explicitly to be defensive. + Activity.Current = _currentTransaction.ParentActivity; + transactionActivity.Dispose(); + } _currentTransaction.UnpinAll(); } } @@ -244,6 +260,20 @@ async Task ICoreSessionInternal.AbortTransactionAsync(AbortTransactionOptions op _currentTransaction.SetState(CoreTransactionState.Aborted); // The transaction is aborted.The session MUST be unpinned regardless // of whether the abortTransaction command succeeds or fails + if (_currentTransaction.TransactionActivity != null) + { + var transactionActivity = _currentTransaction.TransactionActivity; + _currentTransaction.TransactionActivity = null; + + // Set status to Ok for successfully aborted transaction + transactionActivity.SetStatus(ActivityStatusCode.Ok); + + // Dispose the transaction activity. Note: Activity.Current was already restored to the + // parent in StartTransaction() to prevent AsyncLocal flow issues, so the transaction + // activity was never persisted in Activity.Current. Set it explicitly to be defensive. + Activity.Current = _currentTransaction.ParentActivity; + transactionActivity.Dispose(); + } _currentTransaction.UnpinAll(); } } @@ -334,6 +364,21 @@ void ICoreSessionInternal.CommitTransaction(CommitTransactionOptions options, Ca { _isCommitTransactionInProgress = false; _currentTransaction.SetState(CoreTransactionState.Committed); + // Stop the transaction span immediately so it's captured for testing + if (_currentTransaction.TransactionActivity != null) + { + var transactionActivity = _currentTransaction.TransactionActivity; + _currentTransaction.TransactionActivity = null; + + // Set status to Ok for successfully committed transaction + transactionActivity.SetStatus(ActivityStatusCode.Ok); + + // Dispose the transaction activity. Note: Activity.Current was already restored to the + // parent in StartTransaction() to prevent AsyncLocal flow issues, so the transaction + // activity was never persisted in Activity.Current. Set it explicitly to be defensive. + Activity.Current = _currentTransaction.ParentActivity; + transactionActivity.Dispose(); + } } } @@ -374,6 +419,21 @@ async Task ICoreSessionInternal.CommitTransactionAsync(CommitTransactionOptions { _isCommitTransactionInProgress = false; _currentTransaction.SetState(CoreTransactionState.Committed); + // Stop the transaction span immediately so it's captured for testing + if (_currentTransaction.TransactionActivity != null) + { + var transactionActivity = _currentTransaction.TransactionActivity; + _currentTransaction.TransactionActivity = null; + + // Set status to Ok for successfully committed transaction + transactionActivity.SetStatus(ActivityStatusCode.Ok); + + // Dispose the transaction activity. Note: Activity.Current was already restored to the + // parent in StartTransaction() to prevent AsyncLocal flow issues, so the transaction + // activity was never persisted in Activity.Current. Set it explicitly to be defensive. + Activity.Current = _currentTransaction.ParentActivity; + transactionActivity.Dispose(); + } } } @@ -414,6 +474,9 @@ public void MarkDirty() /// public void StartTransaction(TransactionOptions transactionOptions = null) + => ((ICoreSessionInternal)this).StartTransaction(transactionOptions, isTracingEnabled: false); + + void ICoreSessionInternal.StartTransaction(TransactionOptions transactionOptions, bool isTracingEnabled) { EnsureStartTransactionCanBeCalled(); @@ -425,7 +488,19 @@ public void StartTransaction(TransactionOptions transactionOptions = null) } _currentTransaction?.UnpinAll(); // unpin data if any when a new transaction is started - _currentTransaction = new CoreTransaction(transactionNumber, effectiveTransactionOptions); + _currentTransaction = new CoreTransaction(transactionNumber, effectiveTransactionOptions, isTracingEnabled); + + // Start transaction span for OpenTelemetry tracing (if enabled) + if (isTracingEnabled) + { + // Store the parent activity to restore later + _currentTransaction.ParentActivity = Activity.Current; + _currentTransaction.TransactionActivity = MongoTelemetry.StartTransactionActivity(); + + // Immediately restore Activity.Current to the parent to prevent AsyncLocal flow issues. + // The transaction activity will be explicitly set as parent for operations within the transaction. + Activity.Current = _currentTransaction.ParentActivity; + } } /// @@ -559,19 +634,87 @@ private void EnsureTransactionsAreSupported() private TResult ExecuteEndTransactionOnPrimary(OperationContext operationContext, IReadOperation operation) { - using (var sessionHandle = new NonDisposingCoreSessionHandle(this)) - using (var binding = ChannelPinningHelper.CreateReadWriteBinding(_cluster, sessionHandle)) + // Determine operation name and create operation-level span if tracing is enabled + string operationName = operation switch + { + CommitTransactionOperation => "commitTransaction", + AbortTransactionOperation => "abortTransaction", + _ => null + }; + + // Temporarily set Activity.Current to transaction activity so the operation nests under it + var transactionActivity = _currentTransaction?.TransactionActivity; + var previousActivity = Activity.Current; + if (transactionActivity != null) { - return operation.Execute(operationContext, binding); + Activity.Current = transactionActivity; + } + + using var activity = _currentTransaction?.IsTracingEnabled == true && operationName != null + ? MongoTelemetry.StartOperationActivity(operationName, "admin", collectionName: null) + : null; + + // Don't restore Activity.Current yet - let it stay as the operation activity + // so command activities nest under it. We'll restore after the operation completes. + + try + { + using (var sessionHandle = new NonDisposingCoreSessionHandle(this)) + using (var binding = ChannelPinningHelper.CreateReadWriteBinding(_cluster, sessionHandle)) + { + return operation.Execute(operationContext, binding); + } + } + finally + { + // Restore Activity.Current after operation completes + if (transactionActivity != null) + { + Activity.Current = previousActivity; + } } } private async Task ExecuteEndTransactionOnPrimaryAsync(OperationContext operationContext, IReadOperation operation) { - using (var sessionHandle = new NonDisposingCoreSessionHandle(this)) - using (var binding = ChannelPinningHelper.CreateReadWriteBinding(_cluster, sessionHandle)) + // Determine operation name and create operation-level span if tracing is enabled + string operationName = operation switch + { + CommitTransactionOperation => "commitTransaction", + AbortTransactionOperation => "abortTransaction", + _ => null + }; + + // Temporarily set Activity.Current to transaction activity so the operation nests under it + var transactionActivity = _currentTransaction?.TransactionActivity; + var previousActivity = Activity.Current; + if (transactionActivity != null) + { + Activity.Current = transactionActivity; + } + + using var activity = _currentTransaction?.IsTracingEnabled == true && operationName != null + ? MongoTelemetry.StartOperationActivity(operationName, "admin", collectionName: null) + : null; + + // Don't restore Activity.Current yet - let it stay as the operation activity + // so command activities nest under it. We'll restore after the operation completes. + + try + { + using (var sessionHandle = new NonDisposingCoreSessionHandle(this)) + using (var binding = ChannelPinningHelper.CreateReadWriteBinding(_cluster, sessionHandle)) + { + return await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); + } + } + finally { - return await operation.ExecuteAsync(operationContext, binding).ConfigureAwait(false); + // Restore Activity.Current after operation completes + if (transactionActivity != null) + { + Activity.Current = previousActivity; + } } } diff --git a/src/MongoDB.Driver/Core/Bindings/CoreTransaction.cs b/src/MongoDB.Driver/Core/Bindings/CoreTransaction.cs index 6ed2a4f849e..e152702d75e 100644 --- a/src/MongoDB.Driver/Core/Bindings/CoreTransaction.cs +++ b/src/MongoDB.Driver/Core/Bindings/CoreTransaction.cs @@ -13,6 +13,7 @@ * limitations under the License. */ +using System.Diagnostics; using MongoDB.Bson; using MongoDB.Driver.Core.Servers; @@ -32,6 +33,9 @@ public class CoreTransaction private readonly long _transactionNumber; private readonly TransactionOptions _transactionOptions; private readonly object _lock = new object(); + private Activity _transactionActivity; + private Activity _parentActivity; + private readonly bool _isTracingEnabled; // public constructors /// @@ -40,9 +44,21 @@ public class CoreTransaction /// The transaction number. /// The transaction options. public CoreTransaction(long transactionNumber, TransactionOptions transactionOptions) + : this(transactionNumber, transactionOptions, isTracingEnabled: false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The transaction number. + /// The transaction options. + /// Whether OpenTelemetry tracing is enabled for this transaction. + internal CoreTransaction(long transactionNumber, TransactionOptions transactionOptions, bool isTracingEnabled) { _transactionNumber = transactionNumber; _transactionOptions = transactionOptions; + _isTracingEnabled = isTracingEnabled; _state = CoreTransactionState.Starting; _isEmpty = true; } @@ -58,6 +74,29 @@ public CoreTransaction(long transactionNumber, TransactionOptions transactionOpt internal OperationContext OperationContext { get; set; } + /// + /// Gets or sets the transaction activity (for OpenTelemetry tracing). + /// + internal Activity TransactionActivity + { + get => _transactionActivity; + set => _transactionActivity = value; + } + + /// + /// Gets or sets the parent activity to restore after the transaction completes. + /// + internal Activity ParentActivity + { + get => _parentActivity; + set => _parentActivity = value; + } + + /// + /// Gets whether OpenTelemetry tracing is enabled for this transaction. + /// + internal bool IsTracingEnabled => _isTracingEnabled; + /// /// Gets the transaction state. /// @@ -138,6 +177,8 @@ internal void UnpinAll() _pinnedChannel?.Dispose(); _pinnedChannel = null; _pinnedServer = null; + _transactionActivity?.Dispose(); + _transactionActivity = null; } } } diff --git a/src/MongoDB.Driver/Core/Bindings/ICoreSessionInternal.cs b/src/MongoDB.Driver/Core/Bindings/ICoreSessionInternal.cs index 1844ae6fa8c..c8d84131911 100644 --- a/src/MongoDB.Driver/Core/Bindings/ICoreSessionInternal.cs +++ b/src/MongoDB.Driver/Core/Bindings/ICoreSessionInternal.cs @@ -25,4 +25,5 @@ internal interface ICoreSessionInternal Task AbortTransactionAsync(AbortTransactionOptions options, CancellationToken cancellationToken = default); void CommitTransaction(CommitTransactionOptions options, CancellationToken cancellationToken = default); Task CommitTransactionAsync(CommitTransactionOptions options, CancellationToken cancellationToken = default); + void StartTransaction(TransactionOptions transactionOptions, bool isTracingEnabled); } diff --git a/src/MongoDB.Driver/Core/Bindings/NoCoreSession.cs b/src/MongoDB.Driver/Core/Bindings/NoCoreSession.cs index ac7e68abf06..52a8d361820 100644 --- a/src/MongoDB.Driver/Core/Bindings/NoCoreSession.cs +++ b/src/MongoDB.Driver/Core/Bindings/NoCoreSession.cs @@ -173,6 +173,11 @@ public void StartTransaction(TransactionOptions transactionOptions = null) throw new NotSupportedException("NoCoreSession does not support StartTransaction."); } + void ICoreSessionInternal.StartTransaction(TransactionOptions transactionOptions, bool isTracingEnabled) + { + throw new NotSupportedException("NoCoreSession does not support StartTransaction."); + } + /// public void SetSnapshotTimeIfNeeded(BsonTimestamp snapshotTime) { diff --git a/src/MongoDB.Driver/Core/Bindings/WrappingCoreSession.cs b/src/MongoDB.Driver/Core/Bindings/WrappingCoreSession.cs index 1d61b552d9d..82c2e1288bd 100644 --- a/src/MongoDB.Driver/Core/Bindings/WrappingCoreSession.cs +++ b/src/MongoDB.Driver/Core/Bindings/WrappingCoreSession.cs @@ -286,6 +286,12 @@ public virtual void StartTransaction(TransactionOptions transactionOptions = nul _wrapped.StartTransaction(transactionOptions); } + void ICoreSessionInternal.StartTransaction(TransactionOptions transactionOptions, bool isTracingEnabled) + { + ThrowIfDisposed(); + ((ICoreSessionInternal)_wrapped).StartTransaction(transactionOptions, isTracingEnabled); + } + /// public void SetSnapshotTimeIfNeeded(BsonTimestamp snapshotTime) { From d763e6aecc70f24516a963d83f3e364ae3d22005 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Tue, 9 Dec 2025 16:59:23 -0500 Subject: [PATCH 07/11] Implement command-level tracing --- .../Core/Connections/BinaryConnection.cs | 3 +- .../Connections/BinaryConnectionFactory.cs | 4 + .../Core/Connections/ClientDocumentHelper.cs | 2 +- .../Core/Connections/CommandEventHelper.cs | 191 ++++++++++++++---- .../BinaryConnectionFactoryTests.cs | 5 + .../Core/Connections/BinaryConnectionTests.cs | 2 + .../BinaryConnection_CommandEventTests.cs | 1 + .../MongoDbHandshakeProseTests.cs | 1 + 8 files changed, 167 insertions(+), 42 deletions(-) diff --git a/src/MongoDB.Driver/Core/Connections/BinaryConnection.cs b/src/MongoDB.Driver/Core/Connections/BinaryConnection.cs index 3359a33821e..7382257b6b4 100644 --- a/src/MongoDB.Driver/Core/Connections/BinaryConnection.cs +++ b/src/MongoDB.Driver/Core/Connections/BinaryConnection.cs @@ -68,6 +68,7 @@ public BinaryConnection( IConnectionInitializer connectionInitializer, IEventSubscriber eventSubscriber, ILoggerFactory loggerFactory, + TracingOptions tracingOptions, TimeSpan socketReadTimeout, TimeSpan socketWriteTimeout) { @@ -83,7 +84,7 @@ public BinaryConnection( _compressorSource = new CompressorSource(settings.Compressors); _eventLogger = loggerFactory.CreateEventLogger(eventSubscriber); - _commandEventHelper = new CommandEventHelper(loggerFactory.CreateEventLogger(eventSubscriber)); + _commandEventHelper = new CommandEventHelper(loggerFactory.CreateEventLogger(eventSubscriber), tracingOptions); _socketReadTimeout = socketReadTimeout; _socketWriteTimeout = socketWriteTimeout; } diff --git a/src/MongoDB.Driver/Core/Connections/BinaryConnectionFactory.cs b/src/MongoDB.Driver/Core/Connections/BinaryConnectionFactory.cs index 591814e4422..ac47956b506 100644 --- a/src/MongoDB.Driver/Core/Connections/BinaryConnectionFactory.cs +++ b/src/MongoDB.Driver/Core/Connections/BinaryConnectionFactory.cs @@ -32,6 +32,7 @@ internal sealed class BinaryConnectionFactory : IConnectionFactory private readonly ILoggerFactory _loggerFactory; private readonly ConnectionSettings _settings; private readonly IStreamFactory _streamFactory; + private readonly TracingOptions _tracingOptions; // TODO: CSOT: temporary here, remove on the next major release, together with socketTimeout private readonly TimeSpan _socketReadTimeout; private readonly TimeSpan _socketWriteTimeout; @@ -43,6 +44,7 @@ public BinaryConnectionFactory( IEventSubscriber eventSubscriber, ServerApi serverApi, ILoggerFactory loggerFactory, + TracingOptions tracingOptions, TimeSpan? socketReadTimeout, TimeSpan? socketWriteTimeout) { @@ -51,6 +53,7 @@ public BinaryConnectionFactory( _eventSubscriber = Ensure.IsNotNull(eventSubscriber, nameof(eventSubscriber)); _connectionInitializer = new ConnectionInitializer(settings.ApplicationName, settings.Compressors, serverApi, settings.LibraryInfo); _loggerFactory = loggerFactory; + _tracingOptions = tracingOptions; _socketReadTimeout = socketReadTimeout.HasValue && socketReadTimeout > TimeSpan.Zero ? socketReadTimeout.Value : Timeout.InfiniteTimeSpan; _socketWriteTimeout = socketWriteTimeout.HasValue && socketWriteTimeout > TimeSpan.Zero ? socketWriteTimeout.Value : Timeout.InfiniteTimeSpan; } @@ -70,6 +73,7 @@ public IConnection CreateConnection(ServerId serverId, EndPoint endPoint) _connectionInitializer, _eventSubscriber, _loggerFactory, + _tracingOptions, _socketReadTimeout, _socketWriteTimeout); } diff --git a/src/MongoDB.Driver/Core/Connections/ClientDocumentHelper.cs b/src/MongoDB.Driver/Core/Connections/ClientDocumentHelper.cs index c4be5c2cf22..482ddfda32e 100644 --- a/src/MongoDB.Driver/Core/Connections/ClientDocumentHelper.cs +++ b/src/MongoDB.Driver/Core/Connections/ClientDocumentHelper.cs @@ -449,7 +449,7 @@ private static bool TryGetType(string typeName, out Type type) } } - private static string GetAssemblyVersion(Assembly assembly) + internal static string GetAssemblyVersion(Assembly assembly) { var versionAttribute = assembly.GetCustomAttribute(); var hashIndex = versionAttribute.InformationalVersion.IndexOf('+'); diff --git a/src/MongoDB.Driver/Core/Connections/CommandEventHelper.cs b/src/MongoDB.Driver/Core/Connections/CommandEventHelper.cs index 1678c55c273..159d3e470cd 100644 --- a/src/MongoDB.Driver/Core/Connections/CommandEventHelper.cs +++ b/src/MongoDB.Driver/Core/Connections/CommandEventHelper.cs @@ -21,6 +21,7 @@ using MongoDB.Bson; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; @@ -34,24 +35,32 @@ internal class CommandEventHelper { private readonly EventLogger _eventLogger; private readonly ConcurrentDictionary _state; + private readonly TracingOptions _tracingOptions; private readonly bool _shouldProcessRequestMessages; private readonly bool _shouldTrackState; private readonly bool _shouldTrackFailed; private readonly bool _shouldTrackSucceeded; + private readonly bool _shouldTrace; - public CommandEventHelper(EventLogger eventLogger) + public CommandEventHelper(EventLogger eventLogger, TracingOptions tracingOptions = null) { _eventLogger = eventLogger; + _tracingOptions = tracingOptions; + _shouldTrackSucceeded = _eventLogger.IsEventTracked(); _shouldTrackFailed = _eventLogger.IsEventTracked(); - _shouldTrackState = _shouldTrackSucceeded || _shouldTrackFailed; + + // Check if tracing is disabled for this client + _shouldTrace = _tracingOptions?.Disabled != true; + + _shouldTrackState = _shouldTrackSucceeded || _shouldTrackFailed || _shouldTrace; _shouldProcessRequestMessages = _eventLogger.IsEventTracked() || _shouldTrackState; if (_shouldTrackState) { // we only need to track state if we have to raise - // a succeeded or failed event + // a succeeded or failed event or for tracing _state = new ConcurrentDictionary(); } } @@ -104,6 +113,12 @@ public void AfterSending(RequestMessage message, ConnectionId connectionId, Obje { state.Stopwatch.Stop(); + if (state.CommandActivity != null) + { + state.CommandActivity.SetStatus(ActivityStatusCode.Ok); + state.CommandActivity.Stop(); + } + if (_shouldTrackSucceeded) { _eventLogger.LogAndPublish(new CommandSucceededEvent( @@ -128,6 +143,13 @@ public void ErrorSending(RequestMessage message, ConnectionId connectionId, Obje if (_state.TryRemove(message.RequestId, out state)) { state.Stopwatch.Stop(); + + if (state.CommandActivity != null) + { + MongoTelemetry.RecordException(state.CommandActivity, exception); + state.CommandActivity.Stop(); + } + _eventLogger.LogAndPublish(new CommandFailedEvent( state.CommandName, state.QueryNamespace.DatabaseNamespace, @@ -171,6 +193,12 @@ public void ErrorReceiving(int responseTo, ConnectionId connectionId, ObjectId? state.Stopwatch.Stop(); + if (state.CommandActivity != null) + { + MongoTelemetry.RecordException(state.CommandActivity, exception); + state.CommandActivity.Stop(); + } + _eventLogger.LogAndPublish(new CommandFailedEvent( state.CommandName, state.QueryNamespace.DatabaseNamespace, @@ -268,7 +296,7 @@ private void ProcessCommandRequestMessage(CommandRequestMessage message, Connect if (_shouldTrackState) { - _state.TryAdd(requestId, new CommandState + var commandState = new CommandState { CommandName = commandName, OperationId = operationId, @@ -276,7 +304,19 @@ private void ProcessCommandRequestMessage(CommandRequestMessage message, Connect QueryNamespace = new CollectionNamespace(databaseNamespace, "$cmd"), ExpectedResponseType = decodedMessage.MoreToCome ? ExpectedResponseType.None : ExpectedResponseType.Command, ShouldRedactReply = shouldRedactCommand - }); + }; + + if (_shouldTrace && !ShouldRedactCommand(command)) + { + commandState.CommandActivity = MongoTelemetry.StartCommandActivity( + commandName, + command, + databaseNamespace, + connectionId, + _tracingOptions?.QueryTextMaxLength ?? 0); + } + + _state.TryAdd(requestId, commandState); } } } @@ -302,6 +342,8 @@ private void ProcessCommandResponseMessage(CommandState state, CommandResponseMe if (ok.ToBoolean()) { + CompleteCommandActivityWithSuccess(state.CommandActivity, reply); + _eventLogger.LogAndPublish(new CommandSucceededEvent( state.CommandName, reply, @@ -315,23 +357,7 @@ private void ProcessCommandResponseMessage(CommandState state, CommandResponseMe } else { - if (_shouldTrackFailed) - { - _eventLogger.LogAndPublish(new CommandFailedEvent( - state.CommandName, - state.QueryNamespace.DatabaseNamespace, - new MongoCommandException( - connectionId, - string.Format("{0} command failed", state.CommandName), - null, - reply), - state.OperationId, - message.ResponseTo, - connectionId, - serviceId, - state.Stopwatch.Elapsed), - skipLogging); - } + HandleCommandFailure(state, reply, connectionId, serviceId, message.ResponseTo, skipLogging); } } @@ -380,7 +406,7 @@ private void ProcessQueryMessage(QueryMessage originalMessage, ConnectionId conn if (_shouldTrackState) { - _state.TryAdd(requestId, new CommandState + var commandState = new CommandState { CommandName = commandName, OperationId = operationId, @@ -388,7 +414,19 @@ private void ProcessQueryMessage(QueryMessage originalMessage, ConnectionId conn QueryNamespace = decodedMessage.CollectionNamespace, ExpectedResponseType = isCommand ? ExpectedResponseType.Command : ExpectedResponseType.Query, ShouldRedactReply = shouldRedactCommand - }); + }; + + if (_shouldTrace && !ShouldRedactCommand(command)) + { + commandState.CommandActivity = MongoTelemetry.StartCommandActivity( + commandName, + command, + decodedMessage.CollectionNamespace.DatabaseNamespace, + connectionId, + _tracingOptions?.QueryTextMaxLength ?? 0); + } + + _state.TryAdd(requestId, commandState); } } finally @@ -493,25 +531,12 @@ private void ProcessCommandReplyMessage(CommandState state, ReplyMessage Date: Tue, 9 Dec 2025 16:59:24 -0500 Subject: [PATCH 08/11] Add test infrastructure for OpenTelemetry --- .../Core/SpanCapturer.cs | 148 ++++++++++++ .../Specifications/UnifiedTestSpecRunner.cs | 6 + .../Matchers/UnifiedSpanMatcher.cs | 227 ++++++++++++++++++ .../Matchers/UnifiedValueMatcher.cs | 9 + .../UnifiedDropIndexesOperation.cs | 121 ++++++++++ .../UnifiedTestOperations/UnifiedEntityMap.cs | 42 +++- .../UnifiedTestOperationFactory.cs | 1 + .../UnifiedTestRunner.cs | 26 +- 8 files changed, 574 insertions(+), 6 deletions(-) create mode 100644 tests/MongoDB.Driver.TestHelpers/Core/SpanCapturer.cs create mode 100644 tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedSpanMatcher.cs create mode 100644 tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedDropIndexesOperation.cs diff --git a/tests/MongoDB.Driver.TestHelpers/Core/SpanCapturer.cs b/tests/MongoDB.Driver.TestHelpers/Core/SpanCapturer.cs new file mode 100644 index 00000000000..6d04760ae70 --- /dev/null +++ b/tests/MongoDB.Driver.TestHelpers/Core/SpanCapturer.cs @@ -0,0 +1,148 @@ +/* Copyright 2025-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace MongoDB.Driver.TestHelpers.Core +{ + public class CapturedSpan + { + public string Name { get; set; } + public Dictionary Attributes { get; set; } + public ActivityStatusCode StatusCode { get; set; } + public string StatusDescription { get; set; } + public List NestedSpans { get; set; } + public string ParentId { get; set; } + public string SpanId { get; set; } + + public CapturedSpan() + { + Attributes = new Dictionary(); + NestedSpans = new List(); + } + } + + public class SpanCapturer : IDisposable + { + private readonly object _lock = new object(); + private readonly List _completedActivities; + private readonly ActivityListener _listener; + + public SpanCapturer() + { + _completedActivities = new List(); + + _listener = new ActivityListener + { + ShouldListenTo = source => source.Name == MongoTelemetry.ActivitySource.Name, + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStopped = OnActivityStopped + }; + + ActivitySource.AddActivityListener(_listener); + } + + public List Spans + { + get + { + lock (_lock) + { + return BuildSpanTree(_completedActivities); + } + } + } + + public void Clear() + { + lock (_lock) + { + _completedActivities.Clear(); + } + } + + public void Dispose() + { + _listener?.Dispose(); + } + + private void OnActivityStopped(Activity activity) + { + if (activity == null) + { + return; + } + + // Filter out hello/isMaster handshake commands for now + // TODO: Discuss with spec owners whether handshake commands should be filtered + var commandName = activity.GetTagItem("db.command.name") as string; + if (commandName?.ToLowerInvariant() == "hello" || commandName?.ToLowerInvariant() == "ismaster") + { + return; + } + + lock (_lock) + { + _completedActivities.Add(activity); + } + } + + private List BuildSpanTree(List activities) + { + var spanMap = new Dictionary(); + + // Convert all activities to CapturedSpan + foreach (var activity in activities) + { + var capturedSpan = new CapturedSpan + { + Name = activity.DisplayName ?? activity.OperationName, + SpanId = activity.SpanId.ToString(), + ParentId = activity.ParentSpanId.ToString(), + StatusCode = activity.Status, + StatusDescription = activity.StatusDescription + }; + + // Capture all tags as attributes, preserving original types + foreach (var tag in activity.TagObjects) + { + capturedSpan.Attributes[tag.Key] = tag.Value; + } + + spanMap[capturedSpan.SpanId] = capturedSpan; + } + + // Build parent-child relationships + var rootSpans = new List(); + foreach (var span in spanMap.Values) + { + if (span.ParentId == "00000000000000000000000000000000" || !spanMap.ContainsKey(span.ParentId)) + { + // No parent or parent not in our captured set = root span + rootSpans.Add(span); + } + else + { + // Add as child to parent + spanMap[span.ParentId].NestedSpans.Add(span); + } + } + + return rootSpans; + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs index 7ade86c99c3..238e20846eb 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/UnifiedTestSpecRunner.cs @@ -157,6 +157,12 @@ public void LoadBalancers(JsonDrivenTestCase testCase) Run(testCase); } + [UnifiedTestsTheory("open_telemetry.operation")] + public void OpenTelemetry(JsonDrivenTestCase testCase) => Run(testCase); + + [UnifiedTestsTheory("open_telemetry.transaction")] + public void OpenTelemetryTransactions(JsonDrivenTestCase testCase) => Run(testCase); + [Category("SupportLoadBalancing")] [UnifiedTestsTheory("read_write_concern.tests.operation")] public void ReadWriteConcern(JsonDrivenTestCase testCase) => Run(testCase); diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedSpanMatcher.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedSpanMatcher.cs new file mode 100644 index 00000000000..a4da0755f89 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedSpanMatcher.cs @@ -0,0 +1,227 @@ +/* Copyright 2025-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Collections.Generic; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.IO; +using MongoDB.Driver.TestHelpers.Core; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit.Sdk; + +namespace MongoDB.Driver.Tests.UnifiedTestOperations.Matchers +{ + public class UnifiedSpanMatcher + { + private readonly UnifiedValueMatcher _valueMatcher; + + public UnifiedSpanMatcher(UnifiedValueMatcher valueMatcher) + { + _valueMatcher = valueMatcher; + } + + public void AssertSpansMatch(List actualSpans, BsonArray expectedSpans, bool ignoreExtraSpans) + { + try + { + AssertSpans(actualSpans, expectedSpans, ignoreExtraSpans); + } + catch (XunitException exception) + { + throw new AssertionException( + userMessage: GetAssertionErrorMessage(actualSpans, expectedSpans), + innerException: exception); + } + } + + private void AssertSpans(List actualSpans, BsonArray expectedSpans, bool ignoreExtraSpans) + { + if (ignoreExtraSpans) + { + actualSpans.Count.Should().BeGreaterOrEqualTo(expectedSpans.Count); + + // When ignoring extra spans, find each expected span in order within the actual spans + int actualIndex = 0; + for (int expectedIndex = 0; expectedIndex < expectedSpans.Count; expectedIndex++) + { + var expectedSpan = expectedSpans[expectedIndex].AsBsonDocument; + var expectedName = expectedSpan["name"].AsString; + + // Find the next actual span that matches this expected span's name + bool found = false; + while (actualIndex < actualSpans.Count) + { + var actualSpan = actualSpans[actualIndex]; + if (actualSpan.Name == expectedName) + { + AssertSpan(actualSpan, expectedSpan); + actualIndex++; + found = true; + break; + } + actualIndex++; + } + + if (!found) + { + throw new AssertionException($"Expected span with name '{expectedName}' not found in actual spans starting from index {actualIndex}"); + } + } + } + else + { + actualSpans.Should().HaveSameCount(expectedSpans); + + for (int i = 0; i < expectedSpans.Count; i++) + { + var actualSpan = actualSpans[i]; + var expectedSpan = expectedSpans[i].AsBsonDocument; + + AssertSpan(actualSpan, expectedSpan); + } + } + } + + private void AssertSpan(CapturedSpan actualSpan, BsonDocument expectedSpan) + { + foreach (var element in expectedSpan) + { + switch (element.Name) + { + case "name": + actualSpan.Name.Should().Be(element.Value.AsString); + break; + case "attributes": + AssertAttributes(actualSpan.Attributes, element.Value.AsBsonDocument); + break; + case "nested": + AssertNestedSpans(actualSpan.NestedSpans, element.Value.AsBsonArray); + break; + default: + throw new FormatException($"Unexpected span field: '{element.Name}'."); + } + } + } + + private void AssertAttributes(Dictionary actualAttributes, BsonDocument expectedAttributes) + { + foreach (var expectedAttribute in expectedAttributes) + { + var attributeName = expectedAttribute.Name; + var expectedValue = expectedAttribute.Value; + + // Check if this is a $$exists matcher + if (expectedValue.IsBsonDocument) + { + var expectedDoc = expectedValue.AsBsonDocument; + if (expectedDoc.Contains("$$exists")) + { + var shouldExist = expectedDoc["$$exists"].AsBoolean; + if (shouldExist) + { + actualAttributes.Should().ContainKey(attributeName, + $"span should have attribute '{attributeName}'"); + } + else + { + actualAttributes.Should().NotContainKey(attributeName, + $"span should not have attribute '{attributeName}'"); + } + continue; + } + } + + actualAttributes.Should().ContainKey(attributeName, $"span should have attribute '{attributeName}'"); + var actualValue = actualAttributes[attributeName]; + + // Convert the actual value to BsonValue + var actualBsonValue = ConvertToBsonValue(actualValue); + _valueMatcher.AssertValuesMatch(actualBsonValue, expectedValue); + } + } + + private BsonValue ConvertToBsonValue(object value) + { + return value switch + { + null => BsonNull.Value, + BsonValue bv => bv, // Already a BsonValue (including BsonDocument), return as-is + string s => new BsonString(s), + int i => new BsonInt32(i), + long l => new BsonInt64(l), + double d => new BsonDouble(d), + bool b => new BsonBoolean(b), + _ => throw new InvalidOperationException($"Unsupported span attribute type: {value.GetType().Name}") + }; + } + + private void AssertNestedSpans(List actualNestedSpans, BsonArray expectedNestedSpans) + { + actualNestedSpans.Should().HaveSameCount(expectedNestedSpans, "nested spans count should match"); + + for (int i = 0; i < expectedNestedSpans.Count; i++) + { + AssertSpan(actualNestedSpans[i], expectedNestedSpans[i].AsBsonDocument); + } + } + + private string GetAssertionErrorMessage(List actualSpans, BsonArray expectedSpans) + { + var jsonWriterSettings = new JsonWriterSettings { Indent = true }; + + var actualSpansDocuments = new BsonArray(); + foreach (var actualSpan in actualSpans) + { + actualSpansDocuments.Add(ConvertSpanToBsonDocument(actualSpan)); + } + + return + $"Expected spans to be: {expectedSpans.ToJson(jsonWriterSettings)}{Environment.NewLine}" + + $"But found: {actualSpansDocuments.ToJson(jsonWriterSettings)}."; + } + + private BsonDocument ConvertSpanToBsonDocument(CapturedSpan span) + { + var spanDocument = new BsonDocument + { + { "name", span.Name }, + { "status", span.StatusCode.ToString() } + }; + + if (span.Attributes.Count > 0) + { + var attributesDocument = new BsonDocument(); + foreach (var attribute in span.Attributes) + { + attributesDocument[attribute.Key] = ConvertToBsonValue(attribute.Value); + } + spanDocument["attributes"] = attributesDocument; + } + + if (span.NestedSpans.Count > 0) + { + var nestedArray = new BsonArray(); + foreach (var nestedSpan in span.NestedSpans) + { + nestedArray.Add(ConvertSpanToBsonDocument(nestedSpan)); + } + spanDocument["nested"] = nestedArray; + } + + return spanDocument; + } + } +} diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedValueMatcher.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedValueMatcher.cs index 236c12d82db..d7193d7b135 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedValueMatcher.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedValueMatcher.cs @@ -74,12 +74,21 @@ private void AssertValuesMatch(BsonValue actual, BsonValue expected, bool isRoot case "$$matchAsRoot": AssertValuesMatch(actual, operatorValue, true); break; + case "$$matchAsDocument": + var parsedDocument = BsonDocument.Parse(actual.AsString); + AssertValuesMatch(parsedDocument, operatorValue, false); + break; case "$$unsetOrMatches": if (actual != null) { AssertValuesMatch(actual, operatorValue, true); } break; + case "$$sessionLsid": + var sessionId = operatorValue.AsString; + var expectedSessionLsid = _entityMap.SessionIds[sessionId]; + AssertValuesMatch(actual, expectedSessionLsid, isRoot: false); + break; default: throw new FormatException($"Unrecognized root level special operator: '{operatorName}'."); } diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedDropIndexesOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedDropIndexesOperation.cs new file mode 100644 index 00000000000..53afdb41bc4 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedDropIndexesOperation.cs @@ -0,0 +1,121 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Bson; + +namespace MongoDB.Driver.Tests.UnifiedTestOperations +{ + public class UnifiedDropIndexesOperation : IUnifiedEntityTestOperation + { + private readonly IMongoCollection _collection; + private readonly IClientSessionHandle _session; + private readonly DropIndexOptions _options; + + public UnifiedDropIndexesOperation( + IClientSessionHandle session, + IMongoCollection collection, + DropIndexOptions options) + { + _session = session; + _collection = collection; + _options = options; + } + + public OperationResult Execute(CancellationToken cancellationToken) + { + try + { + if (_session == null) + { + _collection.Indexes.DropAll(_options, cancellationToken); + } + else + { + _collection.Indexes.DropAll(_session, _options, cancellationToken); + } + + return OperationResult.Empty(); + } + catch (Exception exception) + { + return OperationResult.FromException(exception); + } + } + + public async Task ExecuteAsync(CancellationToken cancellationToken) + { + try + { + if (_session == null) + { + await _collection.Indexes.DropAllAsync(_options, cancellationToken); + } + else + { + await _collection.Indexes.DropAllAsync(_session, _options, cancellationToken); + } + + return OperationResult.Empty(); + } + catch (Exception exception) + { + return OperationResult.FromException(exception); + } + } + } + + public class UnifiedDropIndexesOperationBuilder + { + private readonly UnifiedEntityMap _entityMap; + + public UnifiedDropIndexesOperationBuilder(UnifiedEntityMap entityMap) + { + _entityMap = entityMap; + } + + public UnifiedDropIndexesOperation Build(string targetCollectionId, BsonDocument arguments) + { + var collection = _entityMap.Collections[targetCollectionId]; + IClientSessionHandle session = null; + DropIndexOptions options = null; + + foreach (var argument in arguments ?? new BsonDocument()) + { + switch (argument.Name) + { + case "maxTimeMS": + options ??= new DropIndexOptions(); + options.MaxTime = TimeSpan.FromMilliseconds(argument.Value.AsInt32); + break; + case "session": + var sessionId = argument.Value.AsString; + session = _entityMap.Sessions[sessionId]; + break; + case "timeoutMS": + options ??= new DropIndexOptions(); + options.Timeout = UnifiedEntityMap.ParseTimeout(argument.Value); + break; + default: + throw new FormatException($"Invalid DropIndexesOperation argument name: '{argument.Name}'."); + } + } + + return new UnifiedDropIndexesOperation(session, collection, options); + } + } +} diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedEntityMap.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedEntityMap.cs index 5649ca8a3c2..857742b8f17 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedEntityMap.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedEntityMap.cs @@ -33,6 +33,7 @@ using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.Encryption; using MongoDB.Driver.GridFS; +using MongoDB.Driver.TestHelpers.Core; using MongoDB.Driver.Tests.Specifications.client_side_encryption; using LogLevel = Microsoft.Extensions.Logging.LogLevel; @@ -57,6 +58,7 @@ public static UnifiedEntityMap Create(Dictionary eventF private readonly Dictionary _clients = new(); private readonly Dictionary _clientEncryptions = new(); private readonly Dictionary _clientEventCapturers = new(); + private readonly Dictionary _spanCapturers = new(); private readonly Dictionary _clientIdToClusterId = new(); private readonly Dictionary> _collections = new(); private readonly Dictionary> _cursors = new(); @@ -177,6 +179,15 @@ public Dictionary EventCapturers } } + public Dictionary SpanCapturers + { + get + { + ThrowIfDisposed(); + return _spanCapturers; + } + } + public Dictionary FailureDocuments { get @@ -284,7 +295,7 @@ public void Dispose() _disposables[i].Dispose(); } - var toDisposeCollection = SelectDisposables(_changeStreams?.Values, _sessions?.Values, _clients?.Values, _clientEncryptions?.Values); + var toDisposeCollection = SelectDisposables(_changeStreams?.Values, _sessions?.Values, _clients?.Values, _clientEncryptions?.Values, _spanCapturers?.Values); foreach (var toDispose in toDisposeCollection) { toDispose.Dispose(); @@ -387,7 +398,7 @@ private void CreateEntities(BsonArray entitiesArray) break; case "client": EnsureIsNotHandled(_clients, id); - var (client, eventCapturers, clientLoggingComponents) = CreateClient(entity, _async); + var (client, eventCapturers, clientLoggingComponents, clientSpanCapturer) = CreateClient(entity, _async); _clients.Add(id, client); _clientIdToClusterId.Add(id, client.Cluster.ClusterId); foreach (var createdEventCapturer in eventCapturers) @@ -396,6 +407,10 @@ private void CreateEntities(BsonArray entitiesArray) } _loggingComponents.Add(id, clientLoggingComponents); + if (clientSpanCapturer != null) + { + _spanCapturers.Add(id, clientSpanCapturer); + } break; case "clientEncryption": { @@ -463,7 +478,7 @@ private IGridFSBucket CreateBucket(BsonDocument entity, Dictionary ClientEventCapturers, Dictionary LoggingComponents) CreateClient(BsonDocument entity, bool async) + private (IMongoClient Client, Dictionary ClientEventCapturers, Dictionary LoggingComponents, SpanCapturer SpanCapturer) CreateClient(BsonDocument entity, bool async) { string appName = null; string authMechanism = null; @@ -473,6 +488,8 @@ private IGridFSBucket CreateBucket(BsonDocument entity, Dictionary loggingComponents = null; string clientId = null; var commandNamesToSkipInEvents = new List(); + SpanCapturer spanCapturer = null; + TracingOptions tracingOptions = null; TimeSpan? connectTimeout = null; List<(string Key, IEnumerable Events, List CommandNotToCapture)> eventTypesToCapture = new(); TimeSpan? heartbeatFrequency = null; @@ -646,6 +663,15 @@ private IGridFSBucket CreateBucket(BsonDocument entity, Dictionary x.AsString)); break; @@ -769,6 +795,8 @@ private IGridFSBucket CreateBucket(BsonDocument entity, Dictionary 0) { settings.ClusterConfigurator = c => @@ -817,7 +845,13 @@ private IGridFSBucket CreateBucket(BsonDocument entity, Dictionary clients, BsonDocument entity) diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs index 32e6ec08f3d..79d083205e8 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs @@ -99,6 +99,7 @@ public IUnifiedTestOperation CreateOperation(string operationName, string target "deleteOne" => new UnifiedDeleteOneOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "distinct" => new UnifiedDistinctOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "dropIndex" => new UnifiedDropIndexOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), + "dropIndexes" => new UnifiedDropIndexesOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "dropSearchIndex" => new UnifiedDropSearchIndexOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "estimatedDocumentCount" => new UnifiedEstimatedDocumentCountOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "find" => new UnifiedFindOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs index 3b4625bc4f1..87da95cbc49 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs @@ -81,10 +81,11 @@ public void Run(JsonDrivenTestCase testCase) var operations = testCase.Test["operations"].AsBsonArray; // cannot be null var expectEvents = testCase.Test.GetValue("expectEvents", null)?.AsBsonArray; var expectedLogs = testCase.Test.GetValue("expectLogMessages", null)?.AsBsonArray; + var expectTracingMessages = testCase.Test.GetValue("expectTracingMessages", null)?.AsBsonArray; var outcome = testCase.Test.GetValue("outcome", null)?.AsBsonArray; var async = testCase.Test["async"].AsBoolean; // cannot be null - Run(schemaVersion, testSetRunOnRequirements, entities, initialData, runOnRequirements, skipReason, operations, expectEvents, expectedLogs, outcome, async); + Run(schemaVersion, testSetRunOnRequirements, entities, initialData, runOnRequirements, skipReason, operations, expectEvents, expectedLogs, expectTracingMessages, outcome, async); } public void Run( @@ -97,6 +98,7 @@ public void Run( BsonArray operations, BsonArray expectedEvents, BsonArray expectedLogs, + BsonArray expectTracingMessages, BsonArray outcome, bool async) { @@ -108,7 +110,7 @@ public void Run( var schemaSemanticVersion = SemanticVersion.Parse(schemaVersion); if (schemaSemanticVersion < new SemanticVersion(1, 0, 0) || - schemaSemanticVersion > new SemanticVersion(1, 26, 0)) + schemaSemanticVersion > new SemanticVersion(1, 27, 0)) { throw new FormatException($"Schema version '{schemaVersion}' is not supported."); } @@ -143,6 +145,10 @@ public void Run( { AssertLogs(expectedLogs, _entityMap); } + if (expectTracingMessages != null) + { + AssertSpans(expectTracingMessages, _entityMap); + } if (outcome != null) { AssertOutcome(DriverTestConfiguration.Client, outcome); @@ -243,6 +249,22 @@ private void AssertEvents(BsonArray eventItems, UnifiedEntityMap entityMap) } } + private void AssertSpans(BsonArray spanItems, UnifiedEntityMap entityMap) + { + _logger.LogDebug("Asserting spans"); + + var unifiedSpanMatcher = new UnifiedSpanMatcher(new UnifiedValueMatcher(entityMap)); + foreach (var spanItem in spanItems.Cast()) + { + var clientId = spanItem["client"].AsString; + var ignoreExtraSpans = spanItem.GetValue("ignoreExtraSpans", false).AsBoolean; + var spanCapturer = entityMap.SpanCapturers[clientId]; + var actualSpans = spanCapturer.Spans; + + unifiedSpanMatcher.AssertSpansMatch(actualSpans, spanItem["spans"].AsBsonArray, ignoreExtraSpans); + } + } + private void AssertLogs(BsonArray expectedLogs, UnifiedEntityMap entityMap) { _logger?.LogDebug("Asserting logs"); From 723a6e6a2cd70ad074e7837af8eae6c57d308958 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Tue, 9 Dec 2025 23:21:07 -0500 Subject: [PATCH 09/11] Fix ClientSessionHandleTests for tests. --- src/MongoDB.Driver/ClientSessionHandle.cs | 2 +- .../ClientSessionHandleTests.cs | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/MongoDB.Driver/ClientSessionHandle.cs b/src/MongoDB.Driver/ClientSessionHandle.cs index ef6be0ef19a..a662ce92c2d 100644 --- a/src/MongoDB.Driver/ClientSessionHandle.cs +++ b/src/MongoDB.Driver/ClientSessionHandle.cs @@ -160,7 +160,7 @@ public void StartTransaction(TransactionOptions transactionOptions = null) var effectiveTransactionOptions = GetEffectiveTransactionOptions(transactionOptions); // Check if tracing is enabled for this client - var tracingOptions = _client.Settings.TracingOptions; + var tracingOptions = _client?.Settings?.TracingOptions; var isTracingEnabled = tracingOptions == null || !tracingOptions.Disabled; ((ICoreSessionInternal)_coreSession).StartTransaction(effectiveTransactionOptions, isTracingEnabled); diff --git a/tests/MongoDB.Driver.Tests/ClientSessionHandleTests.cs b/tests/MongoDB.Driver.Tests/ClientSessionHandleTests.cs index 23021893994..176c836011b 100644 --- a/tests/MongoDB.Driver.Tests/ClientSessionHandleTests.cs +++ b/tests/MongoDB.Driver.Tests/ClientSessionHandleTests.cs @@ -275,7 +275,7 @@ public void StartTransaction_should_call_coreSession() subject.StartTransaction(transactionOptions); - Mock.Get(subject.WrappedCoreSession).Verify(m => m.StartTransaction(It.IsAny()), Times.Once); + Mock.Get(subject.WrappedCoreSession).As().Verify(m => m.StartTransaction(It.IsAny(), It.IsAny()), Times.Once); } [Theory] @@ -405,7 +405,7 @@ public void WithTransaction_callback_should_be_processed_with_expected_result( mockCoreSession.Verify(handle => handle.CommitTransaction(It.IsAny()), Times.Exactly(expectedCommitTransactionAttempts)); } - mockCoreSession.Verify(handle => handle.StartTransaction(It.IsAny()), Times.Exactly(expectedStartTransactionAttempts)); + mockCoreSession.As().Verify(handle => handle.StartTransaction(It.IsAny(), It.IsAny()), Times.Exactly(expectedStartTransactionAttempts)); } [Theory] @@ -428,7 +428,7 @@ public void WithTransaction_callback_with_a_custom_error_should_not_be_retried() Assert.Throws(() => subject.WithTransaction((handle, cancellationToken) => throw new MongoException("test"))); - mockCoreSession.Verify(handle => handle.StartTransaction(It.IsAny()), Times.Once); + mockCoreSession.As().Verify(handle => handle.StartTransaction(It.IsAny(), It.IsAny()), Times.Once); mockCoreSession.Verify(handle => handle.CommitTransaction(It.IsAny()), Times.Never); } @@ -595,7 +595,7 @@ public void WithTransaction_with_error_in_callback_should_call_AbortTransaction_ Assert.Throws(() => subject.WithTransaction((handle, cancellationToken) => throw new Exception("test"))); - mockCoreSession.Verify(handle => handle.StartTransaction(It.IsAny()), Times.Once); + mockCoreSession.As().Verify(handle => handle.StartTransaction(It.IsAny(), It.IsAny()), Times.Once); mockCoreSession.Verify(handle => handle.AbortTransaction(It.IsAny()), shouldAbortTransactionBeCalled ? Times.Once() : Times.Never()); mockCoreSession.Verify(handle => handle.CommitTransaction(It.IsAny()), Times.Never); } @@ -604,13 +604,13 @@ public void WithTransaction_with_error_in_callback_should_call_AbortTransaction_ public void WithTransaction_with_error_in_StartTransaction_should_return_control_immediately() { var mockCoreSession = CreateCoreSessionMock(); - mockCoreSession - .Setup(c => c.StartTransaction(It.IsAny())) + mockCoreSession.As() + .Setup(c => c.StartTransaction(It.IsAny(), It.IsAny())) .Throws(); var subject = CreateSubject(coreSession: mockCoreSession.Object); Assert.Throws(() => subject.WithTransaction((handle, cancellationToken) => 1)); - mockCoreSession.Verify(handle => handle.StartTransaction(It.IsAny()), Times.Once); + mockCoreSession.As().Verify(handle => handle.StartTransaction(It.IsAny(), It.IsAny()), Times.Once); mockCoreSession.Verify(handle => handle.AbortTransaction(It.IsAny()), Times.Never); mockCoreSession.Verify(handle => handle.CommitTransaction(It.IsAny()), Times.Never); } @@ -625,7 +625,7 @@ public void WithTransaction_without_errors_should_call_transaction_infrastructur subject.WithTransaction((handle, cancellationToken) => 1); - mockCoreSession.Verify(handle => handle.StartTransaction(It.IsAny()), Times.Once); + mockCoreSession.As().Verify(handle => handle.StartTransaction(It.IsAny(), It.IsAny()), Times.Once); mockCoreSession.Verify(handle => handle.CommitTransaction(It.IsAny()), Times.Once); } @@ -638,6 +638,7 @@ private Mock CreateCoreSessionMock( options = options ?? new CoreSessionOptions(); var mockCoreSession = new Mock(); + mockCoreSession.As(); mockCoreSession.Setup(m => m.CurrentTransaction).Returns(new CoreTransaction(It.IsAny(), It.IsAny())); mockCoreSession.SetupGet(m => m.Options).Returns(options); mockCoreSession.SetupGet(m => m.ServerSession).Returns(serverSession); From 823deee794bdecbce589f41a7266db6d24521d0f Mon Sep 17 00:00:00 2001 From: adelinowona Date: Tue, 9 Dec 2025 23:28:54 -0500 Subject: [PATCH 10/11] Fix CommandEventHelper tests --- src/MongoDB.Driver/Core/Connections/CommandEventHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MongoDB.Driver/Core/Connections/CommandEventHelper.cs b/src/MongoDB.Driver/Core/Connections/CommandEventHelper.cs index 159d3e470cd..f731d795c04 100644 --- a/src/MongoDB.Driver/Core/Connections/CommandEventHelper.cs +++ b/src/MongoDB.Driver/Core/Connections/CommandEventHelper.cs @@ -306,7 +306,7 @@ private void ProcessCommandRequestMessage(CommandRequestMessage message, Connect ShouldRedactReply = shouldRedactCommand }; - if (_shouldTrace && !ShouldRedactCommand(command)) + if (_shouldTrace && !shouldRedactCommand) { commandState.CommandActivity = MongoTelemetry.StartCommandActivity( commandName, @@ -416,7 +416,7 @@ private void ProcessQueryMessage(QueryMessage originalMessage, ConnectionId conn ShouldRedactReply = shouldRedactCommand }; - if (_shouldTrace && !ShouldRedactCommand(command)) + if (_shouldTrace && !shouldRedactCommand) { commandState.CommandActivity = MongoTelemetry.StartCommandActivity( commandName, From 0e674f60a33b86ca7fa2c976cf8042126e323177 Mon Sep 17 00:00:00 2001 From: adelinowona Date: Wed, 10 Dec 2025 01:29:00 -0500 Subject: [PATCH 11/11] fix some more CommandEventHelperTests --- .../Core/Connections/CommandEventHelperTests.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Core/Connections/CommandEventHelperTests.cs b/tests/MongoDB.Driver.Tests/Core/Connections/CommandEventHelperTests.cs index 50d07b3ab97..6980882fc45 100644 --- a/tests/MongoDB.Driver.Tests/Core/Connections/CommandEventHelperTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Connections/CommandEventHelperTests.cs @@ -17,6 +17,7 @@ using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; +using MongoDB.Driver.Core.Configuration; using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Logging; @@ -56,7 +57,8 @@ public void ShouldRedactCommand_should_return_expected_result(string commandJson public void ShouldTrackState_should_be_correct( [Values(false, true)] bool logCommands, [Values(false, true)] bool captureCommandSucceeded, - [Values(false, true)] bool captureCommandFailed) + [Values(false, true)] bool captureCommandFailed, + [Values(false, true)] bool traceCommands) { var mockLogger = new Mock>(); mockLogger.Setup(m => m.IsEnabled(LogLevel.Debug)).Returns(logCommands); @@ -74,9 +76,10 @@ public void ShouldTrackState_should_be_correct( } var eventLogger = new EventLogger(eventCapturer, mockLogger.Object); - var commandHelper = new CommandEventHelper(eventLogger); + var tracingOptions = traceCommands ? new TracingOptions() : new TracingOptions { Disabled = true }; + var commandHelper = new CommandEventHelper(eventLogger, tracingOptions); - commandHelper._shouldTrackState().Should().Be(logCommands || captureCommandSucceeded || captureCommandFailed); + commandHelper._shouldTrackState().Should().Be(logCommands || captureCommandSucceeded || captureCommandFailed || traceCommands); } }