From 0ea1b3c630ff3bcc94045ea7ad58a489e65a5988 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 11 Aug 2026 08:55:13 -0500 Subject: [PATCH 01/12] DRIVERS-3598 Specify getMore operation span nesting --- source/open-telemetry/open-telemetry.md | 40 ++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/source/open-telemetry/open-telemetry.md b/source/open-telemetry/open-telemetry.md index eab35911f4..79fe73b22f 100644 --- a/source/open-telemetry/open-telemetry.md +++ b/source/open-telemetry/open-telemetry.md @@ -135,6 +135,30 @@ When a user commits or aborts a transaction with `commitTransaction` or `abortTr In case of `withTransaction` operation spans for operations that are executed inside the callbacks SHOULD be nested into the `withTransaction` span. +##### Cursor Iteration (`getMore`) + +When a host application iterates a cursor and the driver sends a `getMore` command to fetch a further batch, the driver +MUST create a new operation span for that `getMore`. This span MUST follow the same rules as any other driver operation +span: it MUST be named according to [Operation Span Name](#operation-span-name) (e.g., +`getMore warehouse_db.users_coll`) and MUST be created within the current span of the host application. + +This operation span MUST NOT be nested under the operation span of the command that created the cursor (e.g., the `find` +or `aggregate` operation span). A host application may perform unrelated work between consuming one batch and requesting +the next — processing documents, calling other services — and nesting each `getMore` under the cursor-creating +operation's span would incorrectly attribute that unrelated work to the original operation. + +Since the span is created within the host application's current span, the usual nesting rules continue to apply. In +particular, if a cursor is iterated inside a `withTransaction` callback, the `getMore` operation span is nested into the +`withTransaction` span like any other operation executed in that callback. + +Because each `getMore` operation span is scoped to a single batch fetch, no span remains open for the lifetime of a +cursor. This also resolves the case of a cursor that is iterated indefinitely or never exhausted (e.g., a tailable +cursor): there is no lifetime-scoped span, so there is nothing left unfinished. Each `getMore` operation span MUST be +finished once its command, including any retries, completes — exactly like any other operation span. + +The `getMore` command span MUST be nested under this `getMore` operation span, following the rules in +[Instrumenting Server Commands](#instrumenting-server-commands). + ##### Operation Span Name The span name SHOULD be: @@ -310,7 +334,17 @@ On the `MongoClient` level this configuration can be implemented with a `MongoCl ###### db.mongodb.cursor_id -If the command returns a cursor, or uses a cursor, the `cursor_id` attribute SHOULD be added. +If the command creates a cursor (e.g., `find`, `aggregate`, `listIndexes`) and the server returns a non-zero cursor id, +the `cursor_id` attribute MUST be added. + +If the command operates on an existing cursor (e.g., `getMore`), the `cursor_id` attribute MUST be added, using the +cursor id the driver sent in the command. It MUST still be added when the server's reply to that command returns a +cursor id of `0` to indicate the cursor is now exhausted: the command did operate on a cursor, and the id it operated on +is the one worth recording. + +A cursor id of `0` means no server-side cursor remains. Drivers MUST NOT add the `cursor_id` attribute with a value of +`0`, and MUST omit it entirely when a cursor-creating command's reply returns a cursor id of `0` (i.e., the command +returned all of its results in the first batch and no cursor was left open). ##### Exceptions @@ -357,6 +391,7 @@ The OpenTelemetry specification covers all driver operations including but not l | `dropCollection` | [tests/operation/drop_collection.yml](tests/operation/drop_collection.yml) | | `dropIndexes` | [tests/operation/drop_indexes.yml](tests/operation/drop_indexes.yml) | | `find` | [tests/operation/find.yml](tests/operation/find.yml) | +| `getMore` | [tests/operation/get_more.yml](tests/operation/get_more.yml) | | `listCollections` | [tests/operation/list_collections.yml](tests/operation/list_collections.yml) | | `listDatabases` | [tests/operation/list_databases.yml](tests/operation/list_databases.yml) | | `listIndexes` | [tests/operation/list_indexes.yml](tests/operation/list_indexes.yml) | @@ -425,6 +460,9 @@ A URI options can be added later if we realise our users need it, while the oppo ## Changelog +- 2026-08-11: Specified that each `getMore` command is nested under its own new operation span, sibling to the operation + span of the command that created the cursor. Specified that `db.mongodb.cursor_id` MUST be added to command spans + that create or use a cursor, and MUST be omitted rather than set to `0` when no server-side cursor remains. - 2026-06-16: Clarified that the `db.query.text` attribute should be serialized to Relaxed Extended JSON. - 2026-02-09: Renamed `db.system` to `db.system.name` according to the corresponding update of OpenTelemetry semantic conventions. From 158ebc8e8d36ed3ba2de20979f5d84be3e6db0fa Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 11 Aug 2026 09:41:48 -0500 Subject: [PATCH 02/12] DRIVERS-3598 Test getMore span nesting and cursor_id presence --- .../tests/operation/get_more.json | 303 ++++++++++++++++++ .../tests/operation/get_more.yml | 154 +++++++++ 2 files changed, 457 insertions(+) create mode 100644 source/open-telemetry/tests/operation/get_more.json create mode 100644 source/open-telemetry/tests/operation/get_more.yml diff --git a/source/open-telemetry/tests/operation/get_more.json b/source/open-telemetry/tests/operation/get_more.json new file mode 100644 index 0000000000..2b1b077394 --- /dev/null +++ b/source/open-telemetry/tests/operation/get_more.json @@ -0,0 +1,303 @@ +{ + "description": "operation getMore", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": {} + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-get-more" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-get-more", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ], + "tests": [ + { + "description": "getMore is nested under its own operation span", + "operations": [ + { + "name": "createFindCursor", + "object": "collection0", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 1 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 2 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 3 + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "find operation-get-more.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-get-more", + "db.collection.name": "test", + "db.operation.name": "find", + "db.operation.summary": "find operation-get-more.test" + }, + "nested": [ + { + "name": "find", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-get-more", + "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-get-more.test", + "db.mongodb.cursor_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + }, + { + "name": "getMore operation-get-more.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-get-more", + "db.collection.name": "test", + "db.operation.name": "getMore", + "db.operation.summary": "getMore operation-get-more.test" + }, + "nested": [ + { + "name": "getMore", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-get-more", + "db.collection.name": "test", + "db.command.name": "getMore", + "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": "getMore operation-get-more.test", + "db.mongodb.cursor_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + }, + { + "description": "cursor_id is omitted when the server returns cursor id 0", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "find operation-get-more.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-get-more", + "db.collection.name": "test", + "db.operation.name": "find", + "db.operation.summary": "find operation-get-more.test" + }, + "nested": [ + { + "name": "find", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-get-more", + "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-get-more.test", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/get_more.yml b/source/open-telemetry/tests/operation/get_more.yml new file mode 100644 index 0000000000..65b5a21a82 --- /dev/null +++ b/source/open-telemetry/tests/operation/get_more.yml @@ -0,0 +1,154 @@ +description: operation getMore +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: {} + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name operation-get-more + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } +tests: + - description: getMore is nested under its own operation span + operations: + - name: createFindCursor + object: *collection0 + arguments: + filter: {} + batchSize: 2 + saveResultAsEntity: &cursor0 cursor0 + - name: iterateUntilDocumentOrError + object: *cursor0 + expectResult: { _id: 1 } + - name: iterateUntilDocumentOrError + object: *cursor0 + expectResult: { _id: 2 } + - name: iterateUntilDocumentOrError + object: *cursor0 + expectResult: { _id: 3 } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: find operation-get-more.test + attributes: + db.system.name: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.operation.name: find + db.operation.summary: find operation-get-more.test + # Negative assertion: this `nested` array lists the `find` command span and nothing + # else. A driver that nests the getMore under the cursor-creating operation produces a + # second entry here, which `ignoreExtraSpans: false` must reject. See Task 3 — the + # unified test format spec has to say that ignoreExtraSpans applies to nested spans + # for this half of the assertion to be enforceable. + nested: + - name: find + attributes: + db.system.name: 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-get-more.test + db.mongodb.cursor_id: { $$type: [ int, long ] } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + - name: getMore operation-get-more.test + attributes: + db.system.name: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.operation.name: getMore + db.operation.summary: getMore operation-get-more.test + nested: + - name: getMore + attributes: + db.system.name: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.command.name: getMore + 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: getMore operation-get-more.test + # This getMore drains the cursor, so the server's reply carries cursor id 0. + # The attribute must still be present, holding the non-zero id the driver sent. + db.mongodb.cursor_id: { $$type: [ int, long ] } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + + - description: cursor_id is omitted when the server returns cursor id 0 + operations: + # No batchSize: all three documents come back in the first batch, the server replies with + # cursor id 0, and no cursor is left open. The UTF `find` operation fully iterates, so a + # driver that still issued a getMore here would also be caught by ignoreExtraSpans: false. + - name: find + object: *collection0 + arguments: + filter: {} + expectResult: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: find operation-get-more.test + attributes: + db.system.name: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.operation.name: find + db.operation.summary: find operation-get-more.test + nested: + - name: find + attributes: + db.system.name: 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-get-more.test + # Cursor id 0 means no server-side cursor remains: the attribute must be + # omitted, not emitted as 0. + db.mongodb.cursor_id: { $$exists: false } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] From 225ecef1c7567c6a5fabd4d239a8b90a0135a7f0 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 11 Aug 2026 11:32:33 -0500 Subject: [PATCH 03/12] DRIVERS-3598 Clarify ignoreExtraSpans applies to nested spans --- source/unified-test-format/unified-test-format.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/unified-test-format/unified-test-format.md b/source/unified-test-format/unified-test-format.md index 13871503fe..a9be1145fb 100644 --- a/source/unified-test-format/unified-test-format.md +++ b/source/unified-test-format/unified-test-format.md @@ -794,6 +794,11 @@ The structure of this object is as follows: - If `true`, additional unexpected spans are allowed. - If `false` or omitted, the test runner MUST fail if any unexpected spans are detected. + This applies at every level of the span tree, not only to the top-level `spans` array. If `false` or omitted, a span + nested under an expected span but absent from that span's [nested](#expectTracingMessages) array is an unexpected + span, and the test runner MUST fail. This allows a test to assert that a command span is *not* nested under a + particular operation span by omitting it from that operation's `nested` array. + - `spans`: Required array of span objects. Each span describes an expected tracing event. Span object properties: @@ -3453,6 +3458,9 @@ other specs *and* collating spec changes developed in parallel or during the sam ## Changelog +- 2026-08-11: Clarified that `ignoreExtraSpans` applies at every level of the span tree, so that a test can assert a + span is not nested under a given parent by omitting it from that parent's `nested` array. + - 2026-06-17: Remove pre-4.2 version references. - 2026-03-17: **Schema version 1.28.** From e2e71cbe199f13d6f8e6dc00788f9e35457782bf Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 11 Aug 2026 13:17:45 -0500 Subject: [PATCH 04/12] DRIVERS-3598 Address review: scope getMore iteration rule and cursor_id edge cases --- source/open-telemetry/open-telemetry.md | 39 ++++++++++++++----- source/open-telemetry/tests/README.md | 9 +++++ .../tests/operation/get_more.yml | 6 ++- .../unified-test-format.md | 6 +-- 4 files changed, 46 insertions(+), 14 deletions(-) diff --git a/source/open-telemetry/open-telemetry.md b/source/open-telemetry/open-telemetry.md index 79fe73b22f..f7c1f0243b 100644 --- a/source/open-telemetry/open-telemetry.md +++ b/source/open-telemetry/open-telemetry.md @@ -137,10 +137,20 @@ the `withTransaction` span. ##### Cursor Iteration (`getMore`) -When a host application iterates a cursor and the driver sends a `getMore` command to fetch a further batch, the driver -MUST create a new operation span for that `getMore`. This span MUST follow the same rules as any other driver operation -span: it MUST be named according to [Operation Span Name](#operation-span-name) (e.g., -`getMore warehouse_db.users_coll`) and MUST be created within the current span of the host application. +The rules in this section apply when a cursor is returned to the caller and the caller drives iteration — for example, +the application holds a cursor object and calls its iteration method. When the driver instead iterates a cursor +internally in order to satisfy a single public API call — for example a `find` helper that returns an array of all +matching documents, or `listCollections` returning a list — the driver MUST NOT create additional operation spans. All +`getMore` command spans for that call MUST be nested under that call's single operation span, because the driver +operation has not finished. + +When the caller drives iteration and the driver sends a `getMore` command to fetch a further batch, the driver MUST +create a new operation span for that `getMore`. This span MUST follow the same rules as any other driver operation span: +it MUST be named according to [Operation Span Name](#operation-span-name) and MUST be created within the current span of +the host application. For a cursor over a collection the name is `getMore db.collection_name` (e.g., +`getMore warehouse_db.users_coll`). For a cursor that does not target a specific collection — for example a cursor from +`listCollections` or a database-level `aggregate` — `db.collection.name` is omitted and the name is `getMore db` (e.g., +`getMore warehouse_db`), per [db.collection.name](#dbcollectionname). This operation span MUST NOT be nested under the operation span of the command that created the cursor (e.g., the `find` or `aggregate` operation span). A host application may perform unrelated work between consuming one batch and requesting @@ -154,7 +164,11 @@ particular, if a cursor is iterated inside a `withTransaction` callback, the `ge Because each `getMore` operation span is scoped to a single batch fetch, no span remains open for the lifetime of a cursor. This also resolves the case of a cursor that is iterated indefinitely or never exhausted (e.g., a tailable cursor): there is no lifetime-scoped span, so there is nothing left unfinished. Each `getMore` operation span MUST be -finished once its command, including any retries, completes — exactly like any other operation span. +finished once its command completes. + +A `getMore` command is not retryable, but a change stream may resume after a `getMore` fails. A resume MUST NOT extend +the failed `getMore` operation span. Drivers MUST finish that span with its error, and the `killCursors`, `aggregate`, +and `getMore` commands issued to re-establish the cursor MUST be nested under new operation spans of their own. The `getMore` command span MUST be nested under this `getMore` operation span, following the rules in [Instrumenting Server Commands](#instrumenting-server-commands). @@ -270,7 +284,7 @@ Spans SHOULD have the following attributes: | `db.mongodb.server_connection_id` | `int64` | Server connection id | Required if available | | `db.mongodb.driver_connection_id` | `int64` | Local connection id | Required if available | | `db.query.text` | `string` | Database command that was sent to the server. Content should be equivalent to the `document` field of the CommandStartedEvent of the command monitoring. | Conditional | -| `db.mongodb.cursor_id` | `int64` | If a cursor is created or used in the operation | Required if available | +| `db.mongodb.cursor_id` | `int64` | If a cursor is created or used in the command (see below) | Conditional | | `db.mongodb.lsid` | `string` | Logical session id | Required if available | | `db.mongodb.txn_number` | `int64` | Transaction number | Required if available | @@ -337,11 +351,15 @@ On the `MongoClient` level this configuration can be implemented with a `MongoCl If the command creates a cursor (e.g., `find`, `aggregate`, `listIndexes`) and the server returns a non-zero cursor id, the `cursor_id` attribute MUST be added. -If the command operates on an existing cursor (e.g., `getMore`), the `cursor_id` attribute MUST be added, using the -cursor id the driver sent in the command. It MUST still be added when the server's reply to that command returns a +If the command operates on a single existing cursor (e.g., `getMore`), the `cursor_id` attribute MUST be added, using +the cursor id the driver sent in the command. It MUST still be added when the server's reply to that command returns a cursor id of `0` to indicate the cursor is now exhausted: the command did operate on a cursor, and the id it operated on is the one worth recording. +If a command may operate on more than one cursor at once — for example `killCursors`, whose `cursors` field is an array +— the `cursor_id` attribute MUST be omitted. The attribute is a single `int64` and has no defined value for such a +command. + A cursor id of `0` means no server-side cursor remains. Drivers MUST NOT add the `cursor_id` attribute with a value of `0`, and MUST omit it entirely when a cursor-creating command's reply returns a cursor id of `0` (i.e., the command returned all of its results in the first batch and no cursor was left open). @@ -461,8 +479,9 @@ A URI options can be added later if we realise our users need it, while the oppo ## Changelog - 2026-08-11: Specified that each `getMore` command is nested under its own new operation span, sibling to the operation - span of the command that created the cursor. Specified that `db.mongodb.cursor_id` MUST be added to command spans - that create or use a cursor, and MUST be omitted rather than set to `0` when no server-side cursor remains. + span of the command that created the cursor, when the caller drives cursor iteration. Specified that + `db.mongodb.cursor_id` MUST be added to command spans that create a cursor with a non-zero id or that operate on a + single existing cursor, and MUST be omitted rather than set to `0` when no server-side cursor remains. - 2026-06-16: Clarified that the `db.query.text` attribute should be serialized to Relaxed Extended JSON. - 2026-02-09: Renamed `db.system` to `db.system.name` according to the corresponding update of OpenTelemetry semantic conventions. diff --git a/source/open-telemetry/tests/README.md b/source/open-telemetry/tests/README.md index 99bcc8d293..2f6d0bb08b 100644 --- a/source/open-telemetry/tests/README.md +++ b/source/open-telemetry/tests/README.md @@ -58,3 +58,12 @@ expectTracingMessages: 7. Create a new `MongoClient`. 8. Perform the same database operation. 9. Assert that the emitted tracing span does not include the `db.query.text` attribute. + +*Test 3: `getMore` records the cursor id it sent, not the cursor id returned* + +1. Set the environment variable `OTEL_#{LANG}_INSTRUMENTATION_MONGODB_ENABLED` to `true`. +2. Create a `MongoClient` and insert three documents into a test collection. +3. Create a cursor over that collection with `find` and a `batchSize` of `2`, then iterate the cursor until it is + exhausted. This sends exactly one `getMore`, and the server's reply to that `getMore` returns a cursor id of `0`. +4. Assert that the `getMore` command span has a `db.mongodb.cursor_id` attribute. +5. Assert that its value is the non-zero cursor id the driver sent in the `getMore` command, and is not `0`. diff --git a/source/open-telemetry/tests/operation/get_more.yml b/source/open-telemetry/tests/operation/get_more.yml index 65b5a21a82..6b76da4c30 100644 --- a/source/open-telemetry/tests/operation/get_more.yml +++ b/source/open-telemetry/tests/operation/get_more.yml @@ -98,7 +98,11 @@ tests: server.port: { $$type: [ int, long ] } db.query.summary: getMore operation-get-more.test # This getMore drains the cursor, so the server's reply carries cursor id 0. - # The attribute must still be present, holding the non-zero id the driver sent. + # The attribute must still be present. $$type only enforces presence and BSON + # type — it cannot express "non-zero", because the unified test format has no + # numeric comparison operator, and 0 satisfies $$type: int. The requirement that + # this holds the non-zero id the driver sent rather than the reply's 0 is covered + # by prose test 3 in ../README.md. db.mongodb.cursor_id: { $$type: [ int, long ] } db.mongodb.server_connection_id: $$type: [ int, long ] diff --git a/source/unified-test-format/unified-test-format.md b/source/unified-test-format/unified-test-format.md index a9be1145fb..cc584ba44b 100644 --- a/source/unified-test-format/unified-test-format.md +++ b/source/unified-test-format/unified-test-format.md @@ -795,9 +795,9 @@ The structure of this object is as follows: - If `false` or omitted, the test runner MUST fail if any unexpected spans are detected. This applies at every level of the span tree, not only to the top-level `spans` array. If `false` or omitted, a span - nested under an expected span but absent from that span's [nested](#expectTracingMessages) array is an unexpected - span, and the test runner MUST fail. This allows a test to assert that a command span is *not* nested under a - particular operation span by omitting it from that operation's `nested` array. + nested under an expected span but absent from that span's `nested` array is an unexpected span, and the test + runner MUST fail. This allows a test to assert that a command span is *not* nested under a particular operation + span by omitting it from that operation's `nested` array. - `spans`: Required array of span objects. Each span describes an expected tracing event. From ce362318a29f01aeb81afe8cf8b383583a9b46fe Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 11 Aug 2026 15:19:49 -0500 Subject: [PATCH 05/12] DRIVERS-3598 Add db.mongodb.cursor_id to the operation span attributes --- source/open-telemetry/open-telemetry.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/source/open-telemetry/open-telemetry.md b/source/open-telemetry/open-telemetry.md index f7c1f0243b..6ff364b494 100644 --- a/source/open-telemetry/open-telemetry.md +++ b/source/open-telemetry/open-telemetry.md @@ -201,6 +201,7 @@ Spans SHOULD have the following attributes: | `db.collection.name` | `string` | The collection being accessed within the database stated in `db.namespace` | Required if available | | `db.operation.name` | `string` | The name of the driver operation being executed | Required | | `db.operation.summary` | `string` | Equivalent to span name | Required | +| `db.mongodb.cursor_id` | `int64` | If a cursor is created or used in the operation (see below) | Conditional | Not all attributes are available at the moment of span creation. Drivers need to add attributes at later stages, which requires an operation span to be available throughout the complete operation lifecycle. @@ -236,6 +237,18 @@ Examples: - `abortTransaction` → *omitted* - client `bulkWrite` → *omitted* + + +###### db.mongodb.cursor_id + +If the operation creates a cursor, or operates on an existing cursor, the `cursor_id` attribute MUST be added to the +operation span, following the same rules as the command span attribute of the same name (see +[db.mongodb.cursor_id](#command-cursor-id) under Command Span Attributes). In particular, it MUST be omitted rather than +added with a value of `0`. + +When a driver iterates a cursor internally to satisfy a single public API call, the value is the id of the cursor the +operation created. For a caller-driven `getMore` operation, the value is the cursor id the driver sent. + ##### Exceptions If the driver operation fails with an exception, drivers MUST record an exception to the current operation span. This @@ -346,6 +359,8 @@ added and truncated to the provided value (similar to the Logging specification) On the `MongoClient` level this configuration can be implemented with a `MongoClient` option, for example, `tracing.query_text_max_length`. + + ###### db.mongodb.cursor_id If the command creates a cursor (e.g., `find`, `aggregate`, `listIndexes`) and the server returns a non-zero cursor id, @@ -480,8 +495,9 @@ A URI options can be added later if we realise our users need it, while the oppo - 2026-08-11: Specified that each `getMore` command is nested under its own new operation span, sibling to the operation span of the command that created the cursor, when the caller drives cursor iteration. Specified that - `db.mongodb.cursor_id` MUST be added to command spans that create a cursor with a non-zero id or that operate on a - single existing cursor, and MUST be omitted rather than set to `0` when no server-side cursor remains. + `db.mongodb.cursor_id` MUST be added to operation spans and to command spans that create a cursor with a non-zero id + or that operate on a single existing cursor, and MUST be omitted rather than set to `0` when no server-side cursor + remains. - 2026-06-16: Clarified that the `db.query.text` attribute should be serialized to Relaxed Extended JSON. - 2026-02-09: Renamed `db.system` to `db.system.name` according to the corresponding update of OpenTelemetry semantic conventions. From 00e4d5792965678d230adf3a85ce4e38c3eaa5c5 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 11 Aug 2026 17:32:26 -0500 Subject: [PATCH 06/12] DRIVERS-3598 Assert cursor_id on operation spans --- .../tests/operation/get_more.json | 21 ++++++++++++++++--- .../tests/operation/get_more.yml | 13 +++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/source/open-telemetry/tests/operation/get_more.json b/source/open-telemetry/tests/operation/get_more.json index 2b1b077394..aa13a0afdd 100644 --- a/source/open-telemetry/tests/operation/get_more.json +++ b/source/open-telemetry/tests/operation/get_more.json @@ -88,7 +88,13 @@ "db.namespace": "operation-get-more", "db.collection.name": "test", "db.operation.name": "find", - "db.operation.summary": "find operation-get-more.test" + "db.operation.summary": "find operation-get-more.test", + "db.mongodb.cursor_id": { + "$$type": [ + "int", + "long" + ] + } }, "nested": [ { @@ -150,7 +156,13 @@ "db.namespace": "operation-get-more", "db.collection.name": "test", "db.operation.name": "getMore", - "db.operation.summary": "getMore operation-get-more.test" + "db.operation.summary": "getMore operation-get-more.test", + "db.mongodb.cursor_id": { + "$$type": [ + "int", + "long" + ] + } }, "nested": [ { @@ -243,7 +255,10 @@ "db.namespace": "operation-get-more", "db.collection.name": "test", "db.operation.name": "find", - "db.operation.summary": "find operation-get-more.test" + "db.operation.summary": "find operation-get-more.test", + "db.mongodb.cursor_id": { + "$$exists": false + } }, "nested": [ { diff --git a/source/open-telemetry/tests/operation/get_more.yml b/source/open-telemetry/tests/operation/get_more.yml index 6b76da4c30..d3ac726a5a 100644 --- a/source/open-telemetry/tests/operation/get_more.yml +++ b/source/open-telemetry/tests/operation/get_more.yml @@ -50,11 +50,13 @@ tests: db.collection.name: *collection0Name db.operation.name: find db.operation.summary: find operation-get-more.test + # This find created a cursor with a non-zero id, since batchSize 2 leaves it open, + # so cursor_id is required on the operation span as well as the command span. + db.mongodb.cursor_id: { $$type: [ int, long ] } # Negative assertion: this `nested` array lists the `find` command span and nothing # else. A driver that nests the getMore under the cursor-creating operation produces a - # second entry here, which `ignoreExtraSpans: false` must reject. See Task 3 — the - # unified test format spec has to say that ignoreExtraSpans applies to nested spans - # for this half of the assertion to be enforceable. + # second entry here, which `ignoreExtraSpans: false` rejects, because the unified test + # format spec states that ignoreExtraSpans applies at every level of the span tree. nested: - name: find attributes: @@ -82,6 +84,8 @@ tests: db.collection.name: *collection0Name db.operation.name: getMore db.operation.summary: getMore operation-get-more.test + # This operation operates on an existing cursor, so cursor_id is required here too. + db.mongodb.cursor_id: { $$type: [ int, long ] } nested: - name: getMore attributes: @@ -134,6 +138,9 @@ tests: db.collection.name: *collection0Name db.operation.name: find db.operation.summary: find operation-get-more.test + # This find returned everything in the first batch, so no cursor was left open and + # cursor_id must be omitted from the operation span as well as the command span. + db.mongodb.cursor_id: { $$exists: false } nested: - name: find attributes: From 21b9e864088ee027e00d9be91f8de2bf981990d8 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 11 Aug 2026 17:47:13 -0500 Subject: [PATCH 07/12] DRIVERS-3598 Clarify cursor iteration discriminator and cursor_id omissions --- source/open-telemetry/open-telemetry.md | 25 +++++++++++++++---------- source/open-telemetry/tests/README.md | 5 +++-- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/source/open-telemetry/open-telemetry.md b/source/open-telemetry/open-telemetry.md index 6ff364b494..ab8c881eb3 100644 --- a/source/open-telemetry/open-telemetry.md +++ b/source/open-telemetry/open-telemetry.md @@ -137,12 +137,16 @@ the `withTransaction` span. ##### Cursor Iteration (`getMore`) -The rules in this section apply when a cursor is returned to the caller and the caller drives iteration — for example, -the application holds a cursor object and calls its iteration method. When the driver instead iterates a cursor -internally in order to satisfy a single public API call — for example a `find` helper that returns an array of all -matching documents, or `listCollections` returning a list — the driver MUST NOT create additional operation spans. All -`getMore` command spans for that call MUST be nested under that call's single operation span, because the driver -operation has not finished. +This section covers both ways a cursor can be iterated, and the rule differs between them. + +If the driver does not expose the cursor to the caller, and instead iterates it internally to produce the return value +of one public API call — a `find` helper that returns an array of all matching documents, or `listCollections` returning +a list — then the driver MUST NOT create additional operation spans for that iteration. Every `getMore` command span +belonging to that call MUST be nested under that call's single operation span, because the driver operation has not +finished. + +If the driver returns the cursor to the caller and the caller drives iteration, the rules in the rest of this section +apply. When the caller drives iteration and the driver sends a `getMore` command to fetch a further batch, the driver MUST create a new operation span for that `getMore`. This span MUST follow the same rules as any other driver operation span: @@ -241,10 +245,11 @@ Examples: ###### db.mongodb.cursor_id -If the operation creates a cursor, or operates on an existing cursor, the `cursor_id` attribute MUST be added to the -operation span, following the same rules as the command span attribute of the same name (see -[db.mongodb.cursor_id](#command-cursor-id) under Command Span Attributes). In particular, it MUST be omitted rather than -added with a value of `0`. +If the operation creates a cursor, or operates on a single existing cursor, the `cursor_id` attribute MUST be added to +the operation span, following the same rules as the command span attribute of the same name, including the omissions +described there (see [db.mongodb.cursor_id](#command-cursor-id) under Command Span Attributes). In particular, it MUST +be omitted rather than added with a value of `0`, and it MUST be omitted from an operation span for a command that may +operate on more than one cursor at once, such as `killCursors`. When a driver iterates a cursor internally to satisfy a single public API call, the value is the id of the cursor the operation created. For a caller-driven `getMore` operation, the value is the cursor id the driver sent. diff --git a/source/open-telemetry/tests/README.md b/source/open-telemetry/tests/README.md index 2f6d0bb08b..0db79a8477 100644 --- a/source/open-telemetry/tests/README.md +++ b/source/open-telemetry/tests/README.md @@ -65,5 +65,6 @@ expectTracingMessages: 2. Create a `MongoClient` and insert three documents into a test collection. 3. Create a cursor over that collection with `find` and a `batchSize` of `2`, then iterate the cursor until it is exhausted. This sends exactly one `getMore`, and the server's reply to that `getMore` returns a cursor id of `0`. -4. Assert that the `getMore` command span has a `db.mongodb.cursor_id` attribute. -5. Assert that its value is the non-zero cursor id the driver sent in the `getMore` command, and is not `0`. +4. Assert that both the `getMore` operation span and the `getMore` command span have a `db.mongodb.cursor_id` attribute. +5. Assert that on each of those two spans, the value is the non-zero cursor id the driver sent in the `getMore` command, + and is not `0`. From 0cbe9bf545ce8fc00e2c758653dd105f0f6cf482 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 11 Aug 2026 19:33:24 -0500 Subject: [PATCH 08/12] DRIVERS-3598 Enable tracing via client option in prose test 3 --- source/open-telemetry/tests/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/open-telemetry/tests/README.md b/source/open-telemetry/tests/README.md index 0db79a8477..5787d49ed4 100644 --- a/source/open-telemetry/tests/README.md +++ b/source/open-telemetry/tests/README.md @@ -61,8 +61,8 @@ expectTracingMessages: *Test 3: `getMore` records the cursor id it sent, not the cursor id returned* -1. Set the environment variable `OTEL_#{LANG}_INSTRUMENTATION_MONGODB_ENABLED` to `true`. -2. Create a `MongoClient` and insert three documents into a test collection. +1. Create a `MongoClient` with tracing enabled on the client, using the `tracing.enabled` client option. +2. Insert three documents into a test collection. 3. Create a cursor over that collection with `find` and a `batchSize` of `2`, then iterate the cursor until it is exhausted. This sends exactly one `getMore`, and the server's reply to that `getMore` returns a cursor id of `0`. 4. Assert that both the `getMore` operation span and the `getMore` command span have a `db.mongodb.cursor_id` attribute. From 5dfdde1086ca7ee10b76d400026dcb0601c0233d Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 11 Aug 2026 19:38:15 -0500 Subject: [PATCH 09/12] DRIVERS-3598 Assert sent vs returned cursor id in prose test 3 --- source/open-telemetry/tests/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/open-telemetry/tests/README.md b/source/open-telemetry/tests/README.md index 5787d49ed4..bea2b03e64 100644 --- a/source/open-telemetry/tests/README.md +++ b/source/open-telemetry/tests/README.md @@ -66,5 +66,5 @@ expectTracingMessages: 3. Create a cursor over that collection with `find` and a `batchSize` of `2`, then iterate the cursor until it is exhausted. This sends exactly one `getMore`, and the server's reply to that `getMore` returns a cursor id of `0`. 4. Assert that both the `getMore` operation span and the `getMore` command span have a `db.mongodb.cursor_id` attribute. -5. Assert that on each of those two spans, the value is the non-zero cursor id the driver sent in the `getMore` command, - and is not `0`. +5. Assert that on each of those two spans, the value is the cursor id the driver sent in the `getMore` command, and not + the `0` returned in that command's reply. From b20f0e9cbd9a94e72698f824388ee318a64ea46c Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 11 Aug 2026 19:41:48 -0500 Subject: [PATCH 10/12] DRIVERS-3598 Tighten getMore and cursor_id spec prose --- source/open-telemetry/open-telemetry.md | 88 ++++++++++--------------- 1 file changed, 33 insertions(+), 55 deletions(-) diff --git a/source/open-telemetry/open-telemetry.md b/source/open-telemetry/open-telemetry.md index ab8c881eb3..f06ac60405 100644 --- a/source/open-telemetry/open-telemetry.md +++ b/source/open-telemetry/open-telemetry.md @@ -137,46 +137,30 @@ the `withTransaction` span. ##### Cursor Iteration (`getMore`) -This section covers both ways a cursor can be iterated, and the rule differs between them. - -If the driver does not expose the cursor to the caller, and instead iterates it internally to produce the return value -of one public API call — a `find` helper that returns an array of all matching documents, or `listCollections` returning -a list — then the driver MUST NOT create additional operation spans for that iteration. Every `getMore` command span -belonging to that call MUST be nested under that call's single operation span, because the driver operation has not -finished. - -If the driver returns the cursor to the caller and the caller drives iteration, the rules in the rest of this section -apply. - -When the caller drives iteration and the driver sends a `getMore` command to fetch a further batch, the driver MUST -create a new operation span for that `getMore`. This span MUST follow the same rules as any other driver operation span: -it MUST be named according to [Operation Span Name](#operation-span-name) and MUST be created within the current span of -the host application. For a cursor over a collection the name is `getMore db.collection_name` (e.g., -`getMore warehouse_db.users_coll`). For a cursor that does not target a specific collection — for example a cursor from -`listCollections` or a database-level `aggregate` — `db.collection.name` is omitted and the name is `getMore db` (e.g., -`getMore warehouse_db`), per [db.collection.name](#dbcollectionname). - -This operation span MUST NOT be nested under the operation span of the command that created the cursor (e.g., the `find` -or `aggregate` operation span). A host application may perform unrelated work between consuming one batch and requesting -the next — processing documents, calling other services — and nesting each `getMore` under the cursor-creating -operation's span would incorrectly attribute that unrelated work to the original operation. - -Since the span is created within the host application's current span, the usual nesting rules continue to apply. In -particular, if a cursor is iterated inside a `withTransaction` callback, the `getMore` operation span is nested into the -`withTransaction` span like any other operation executed in that callback. - -Because each `getMore` operation span is scoped to a single batch fetch, no span remains open for the lifetime of a -cursor. This also resolves the case of a cursor that is iterated indefinitely or never exhausted (e.g., a tailable -cursor): there is no lifetime-scoped span, so there is nothing left unfinished. Each `getMore` operation span MUST be -finished once its command completes. - -A `getMore` command is not retryable, but a change stream may resume after a `getMore` fails. A resume MUST NOT extend -the failed `getMore` operation span. Drivers MUST finish that span with its error, and the `killCursors`, `aggregate`, -and `getMore` commands issued to re-establish the cursor MUST be nested under new operation spans of their own. - -The `getMore` command span MUST be nested under this `getMore` operation span, following the rules in +If the driver does not expose the cursor to the caller, but iterates it internally to produce the return value of one +public API call (e.g., a `find` helper returning an array of all matching documents), the driver MUST NOT create +additional operation spans. Every `getMore` command span for that call MUST be nested under the call's single operation +span. + +If the driver returns the cursor to the caller and the caller drives iteration, the driver MUST create a new operation +span for each `getMore` it sends. The span MUST be created within the current span of the host application and MUST be +named according to [Operation Span Name](#operation-span-name): `getMore db.collection_name` for a cursor over a +collection, or `getMore db` when the cursor targets no specific collection (e.g., a cursor from `listCollections`), per +[db.collection.name](#dbcollectionname). The `getMore` command span MUST be nested under it, per [Instrumenting Server Commands](#instrumenting-server-commands). +This operation span MUST NOT be nested under the operation span of the command that created the cursor. A host +application may do unrelated work between batches, and nesting each `getMore` under the cursor-creating operation would +attribute that work to the original operation. Ordinary nesting still applies otherwise: a cursor iterated inside a +`withTransaction` callback nests into the `withTransaction` span. + +Each `getMore` operation span MUST be finished once its command completes. No span is scoped to a cursor's lifetime, so +a cursor that is never exhausted (e.g., a tailable cursor) leaves nothing unfinished. + +A `getMore` is not retryable, but a change stream may resume after one fails. A resume MUST NOT extend the failed +`getMore` operation span: drivers MUST finish that span with its error, and the `killCursors`, `aggregate`, and +`getMore` commands that re-establish the cursor MUST each be nested under new operation spans. + ##### Operation Span Name The span name SHOULD be: @@ -246,13 +230,10 @@ Examples: ###### db.mongodb.cursor_id If the operation creates a cursor, or operates on a single existing cursor, the `cursor_id` attribute MUST be added to -the operation span, following the same rules as the command span attribute of the same name, including the omissions -described there (see [db.mongodb.cursor_id](#command-cursor-id) under Command Span Attributes). In particular, it MUST -be omitted rather than added with a value of `0`, and it MUST be omitted from an operation span for a command that may -operate on more than one cursor at once, such as `killCursors`. - -When a driver iterates a cursor internally to satisfy a single public API call, the value is the id of the cursor the -operation created. For a caller-driven `getMore` operation, the value is the cursor id the driver sent. +the operation span, following the same rules as the command span attribute of the same name, including its omissions: +never a literal `0`, and never for a command that may operate on several cursors at once, such as `killCursors` (see +[db.mongodb.cursor_id](#command-cursor-id)). When the driver iterates a cursor internally, the value is the id of the +cursor the operation created; for a caller-driven `getMore`, it is the id the driver sent. ##### Exceptions @@ -371,18 +352,15 @@ On the `MongoClient` level this configuration can be implemented with a `MongoCl If the command creates a cursor (e.g., `find`, `aggregate`, `listIndexes`) and the server returns a non-zero cursor id, the `cursor_id` attribute MUST be added. -If the command operates on a single existing cursor (e.g., `getMore`), the `cursor_id` attribute MUST be added, using -the cursor id the driver sent in the command. It MUST still be added when the server's reply to that command returns a -cursor id of `0` to indicate the cursor is now exhausted: the command did operate on a cursor, and the id it operated on -is the one worth recording. +If the command operates on a single existing cursor (e.g., `getMore`), the attribute MUST be added, holding the cursor +id the driver sent. It MUST still be added when the reply returns a cursor id of `0` to signal the cursor is now +exhausted; the id the command operated on is the one worth recording. -If a command may operate on more than one cursor at once — for example `killCursors`, whose `cursors` field is an array -— the `cursor_id` attribute MUST be omitted. The attribute is a single `int64` and has no defined value for such a -command. +If a command may operate on several cursors at once (e.g., `killCursors`, whose `cursors` field is an array), the +attribute MUST be omitted: it is a single `int64` and has no defined value for such a command. -A cursor id of `0` means no server-side cursor remains. Drivers MUST NOT add the `cursor_id` attribute with a value of -`0`, and MUST omit it entirely when a cursor-creating command's reply returns a cursor id of `0` (i.e., the command -returned all of its results in the first batch and no cursor was left open). +A cursor id of `0` means no server-side cursor remains. Drivers MUST NOT add the attribute with a value of `0`, and MUST +omit it when a cursor-creating command's reply returns `0`. ##### Exceptions From 3bc6022ab33ab29b3e4b9bdd49a5a19a711506ad Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 11 Aug 2026 19:45:19 -0500 Subject: [PATCH 11/12] DRIVERS-3598 Shorten ignoreExtraSpans wording --- source/unified-test-format/unified-test-format.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/unified-test-format/unified-test-format.md b/source/unified-test-format/unified-test-format.md index cc584ba44b..4073efb282 100644 --- a/source/unified-test-format/unified-test-format.md +++ b/source/unified-test-format/unified-test-format.md @@ -794,10 +794,9 @@ The structure of this object is as follows: - If `true`, additional unexpected spans are allowed. - If `false` or omitted, the test runner MUST fail if any unexpected spans are detected. - This applies at every level of the span tree, not only to the top-level `spans` array. If `false` or omitted, a span - nested under an expected span but absent from that span's `nested` array is an unexpected span, and the test - runner MUST fail. This allows a test to assert that a command span is *not* nested under a particular operation - span by omitting it from that operation's `nested` array. + This applies at every level of the span tree, not only to the top-level `spans` array. A span nested under an + expected span, but absent from that span's `nested` array, is an unexpected span. A test can therefore assert that + a command span is *not* nested under a given operation span by omitting it from that operation's `nested` array. - `spans`: Required array of span objects. Each span describes an expected tracing event. From 87dbfed268c1d1720360976cc40cf8d873d0e699 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 11 Aug 2026 20:13:33 -0500 Subject: [PATCH 12/12] DRIVERS-3598 Add prose test for getMore inside a transaction --- source/open-telemetry/tests/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/source/open-telemetry/tests/README.md b/source/open-telemetry/tests/README.md index bea2b03e64..0c4c2d00ff 100644 --- a/source/open-telemetry/tests/README.md +++ b/source/open-telemetry/tests/README.md @@ -68,3 +68,16 @@ expectTracingMessages: 4. Assert that both the `getMore` operation span and the `getMore` command span have a `db.mongodb.cursor_id` attribute. 5. Assert that on each of those two spans, the value is the cursor id the driver sent in the `getMore` command, and not the `0` returned in that command's reply. + +*Test 4: `getMore` inside a transaction nests under the transaction span* + +This test requires a replica set or a sharded cluster running server version 4.4 or later. + +1. Create a `MongoClient` with tracing enabled on the client, using the `tracing.enabled` client option. +2. Insert three documents into a test collection. +3. Start a session and call `withTransaction`. In the callback, create a cursor over that collection with `find` and a + `batchSize` of `2`, then iterate the cursor until it is exhausted. This sends exactly one `getMore` inside the + transaction. +4. Assert that a `transaction` span was emitted, and that both the `find` operation span and the `getMore` operation + span are nested directly under it. +5. Assert that the `getMore` operation span is a sibling of the `find` operation span, and is not nested under it.