Skip to content

DRIVERS-2985: Allow on-demand client metadata updates after MongoClient initialization. #1798

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
026c92b
Update spec to allow on-demand client metadata updates for instantiat…
vbabanin May 7, 2025
a9eddb4
Use MongoClient terminology.
vbabanin May 7, 2025
324019d
Add prose tests.
vbabanin May 8, 2025
ab28ac3
Merge branch 'master' into DRIVERS-2985
vbabanin May 9, 2025
03b5c1d
Add prose tests.
vbabanin May 9, 2025
450528c
Change wording.
vbabanin May 10, 2025
5ce8b92
Replace phrase for correctness.
vbabanin May 17, 2025
c6a042b
Add parametrized test-cases.
vbabanin May 18, 2025
e02503b
Disallow creating new connections solely to transfer updated metadata.
vbabanin May 19, 2025
1faef56
Split saving of intercepted client document into a distinct step.
vbabanin May 20, 2025
8fc6f1c
Update source/mongodb-handshake/handshake.md
vbabanin May 20, 2025
c572c4d
Revert "during MongoClient initialization".
vbabanin May 20, 2025
0b7fbfa
Remove "MAY" from prose tests.
vbabanin May 21, 2025
dfde2d1
Add additional test-cases.
vbabanin Jun 4, 2025
9f58bfb
Merge branch 'master' into DRIVERS-2985
vbabanin Jun 4, 2025
b4a4b0e
Merge branch 'master' into DRIVERS-2985
vbabanin Jun 5, 2025
4be80aa
Add API guidance.
vbabanin Jun 6, 2025
f0315f4
Remove redundant assertions.
vbabanin Jun 6, 2025
da284da
Add unified-tests.
vbabanin Jun 10, 2025
72f1150
Update changelog.
vbabanin Jun 10, 2025
ae795a6
Fix changelog.
vbabanin Jun 10, 2025
a34752b
Rename test files.
vbabanin Jun 10, 2025
43dea26
Fix database name.
vbabanin Jun 10, 2025
944329d
Add commandName "ping" to runCommand arguments.
vbabanin Jun 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions source/mongodb-handshake/handshake.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,10 +405,29 @@ class DriverInfoOptions {
}
```

Note that how these options are provided to a driver is left up to the implementer.
Note that how these options are provided to a driver during `MongoClient` initialization is left up to the implementer.

If provided, these options MUST NOT replace the values used for metadata generation. The provided options MUST be
appended to their respective fields, and be delimited by a `|` character. For example, when
### Metadata updates after MongoClient initialization

Drivers MUST provide an API that allows appending `DriverInfoOptions` to a `MongoClient` instance after initialization,
adhering to the pattern described below while following idioms of the language of the driver:

```java
interface MongoClient {
void appendMetadata(DriverInfoOptions driverInfoOptions);
// other existing members of MongoClient
}
```

After client metadata update, drivers MUST apply updated metadata to newly created connections. Drivers MUST NOT apply
updated metadata to already established connections, create new connections, or close existing connections solely for
the purpose of transferring updated metadata.

### Appending metadata

If `DriverInfoOptions` are provided during or after `MongoClient` initialization, these options MUST NOT replace any
existing metadata values, including driver-generated metadata and previously provided options. The provided options MUST
be appended to their respective fields, and be delimited by a `|` character. For example, when
[Motor](https://www.mongodb.com/docs/drivers/motor/) wraps PyMongo, the following fields are updated to include Motor's
"driver info":

Expand Down Expand Up @@ -534,6 +553,7 @@ support the `hello` command, the `helloOk: true` argument is ignored and the leg

## Changelog

- 2025-06-09: Add requirement to allow appending to client metadata after `MongoClient` initialization.
- 2024-11-05: Move handshake prose tests from spec file to prose test file.
- 2024-10-09: Clarify that FaaS and container metadata must both be populated when both are present.
- 2024-08-16: Migrated from reStructuredText to Markdown.
Expand Down
125 changes: 125 additions & 0 deletions source/mongodb-handshake/tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,128 @@ the following sets of environment variables:
2. Create and connect a `Connection` object that connects to the server that returns the mocked response.

3. Assert that no error is raised.

## Client Metadata Update Prose Tests

Drivers that do not emit events for commands issued as part of the handshake with the server will need to create a
test-only backdoor mechanism to intercept the handshake `hello` command for verification purposes.

### Test 1: Test that the driver updates metadata

Drivers should verify that metadata provided after `MongoClient` initialization is appended, not replaced, and is
visible in the `hello` command of new connections.

There are multiple test cases parameterized with `DriverInfoOptions` to be appended after `MongoClient` initialization.
Before each test case, perform the setup.

#### Setup

1. Create a `MongoClient` instance with the following:

- `maxIdleTimeMS` set to `1ms`

- Wrapping library metadata:

| Field | Value |
| -------- | ---------------- |
| name | library |
| version | 1.2 |
| platform | Library Platform |

2. Send a `ping` command to the server and verify that the command succeeds.

3. Save intercepted `client` document as `initialClientMetadata`.

4. Wait 5ms for the connection to become idle.

#### Parameterized test cases

| Case | Name | Version | Platform |
| ---- | --------- | ------- | ------------------ |
| 1 | framework | 2.0 | Framework Platform |
| 2 | framework | 2.0 | null |
| 3 | framework | null | Framework Platform |
| 4 | framework | null | null |

#### Running a test case

1. Append the `DriverInfoOptions` from the selected test case to the `MongoClient` metadata.

2. Send a `ping` command to the server and verify:

- The command succeeds.

- The framework metadata is appended to the existing `DriverInfoOptions` in the `client.driver` fields of the `hello`
command, with values separated by a pipe `|`.

- `client.driver.name`:
- If test case's name is non-null: `library|<name>`
- Otherwise, the field remains unchanged: `library`
- `client.driver.version`:
- If test case's version is non-null: `1.2|<version>`
- Otherwise, the field remains unchanged: `1.2`
- `client.driver.platform`:
- If test case's platform is non-null: `Library Platform|<platform>`
- Otherwise, the field remains unchanged: `Library Platform`

- All other subfields in the `client` document remain unchanged from `initialClientMetadata`.

## Test 2: Multiple Successive Metadata Updates

Drivers should verify that after `MongoClient` initialization, metadata can be updated multiple times, not replaced, and
is visible in the `hello` command of new connections.

There are multiple test cases parameterized with `DriverInfoOptions` to be appended after a previous metadata update.
Before each test case, perform the setup.

### Setup

1. Create a `MongoClient` instance with:

- `maxIdleTimeMS` set to `1ms`

2. Append the following `DriverInfoOptions` to the `MongoClient` metadata:

| Field | Value |
| -------- | ---------------- |
| name | library |
| version | 1.2 |
| platform | Library Platform |

3. Send a `ping` command to the server and verify that the command succeeds.

4. Save intercepted `client` document as `updatedClientMetadata`.

5. Wait 5ms for the connection to become idle.

#### Parameterized test cases

| Case | Name | Version | Platform |
| ---- | --------- | ------- | ------------------ |
| 1 | framework | 2.0 | Framework Platform |
| 2 | framework | 2.0 | null |
| 3 | framework | null | Framework Platform |
| 4 | framework | null | null |

#### Running a test case

1. Append the `DriverInfoOptions` from the selected test case to the `MongoClient` metadata.

2. Send a `ping` command to the server and verify:

- The command succeeds.

- The framework metadata is appended to the existing `DriverInfoOptions` in the `client.driver` fields of the `hello`
command, with values separated by a pipe `|`.

- `client.driver.name`:
- If test case's name is non-null: `library|<name>`
- Otherwise, the field remains unchanged: `library`
- `client.driver.version`:
- If test case's version is non-null: `1.2|<version>`
- Otherwise, the field remains unchanged: `1.2`
- `client.driver.platform`:
- If test case's platform is non-null: `Library Platform|<platform>`
- Otherwise, the field remains unchanged: `Library Platform`

- All other subfields in the `client` document remain unchanged from `updatedClientMetadata`.
100 changes: 100 additions & 0 deletions source/mongodb-handshake/tests/unified/metadata-not-propagated.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{
"description": "client metadata is not propagated to the server",
"schemaVersion": "1.9",
"runOnRequirements": [
{
"minServerVersion": "6.0"
}
],
"createEntities": [
{
"client": {
"id": "client",
"observeEvents": [
"commandSucceededEvent",
"commandFailedEvent",
"connectionClosedEvent",
"connectionCreatedEvent"
]
}
},
{
"database": {
"id": "database",
"client": "client",
"databaseName": "test"
}
}
],
"tests": [
{
"description": "metadata append does not create new connections or close existing ones and no hello command is sent",
"operations": [
{
"name": "runCommand",
"object": "database",
"arguments": {
"commandName": "ping",
"command": {
"ping": 1
}
},
"expectResult": {
"ok": 1
}
},
{
"name": "appendMetadata",
"object": "client",
"arguments": {
"driverInfoOptions": {
"name": "framework",
"version": "2.0",
"platform": "Framework Platform"
}
}
},
{
"name": "runCommand",
"object": "database",
"arguments": {
"commandName": "ping",
"command": {
"ping": 1
}
},
"expectResult": {
"ok": 1
}
}
],
"expectEvents": [
{
"client": "client",
"eventType": "cmap",
"events": [
{
"connectionCreatedEvent": {}
}
]
},
{
"client": "client",
"eventType": "command",
"events": [
{
"commandSucceededEvent": {
"commandName": "ping"
}
},
{
"commandSucceededEvent": {
"commandName": "ping"
}
}
]
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
description: client metadata is not propagated to the server
schemaVersion: '1.9'
runOnRequirements:
- minServerVersion: '6.0'
createEntities:
- client:
id: &client client
observeEvents:
- commandSucceededEvent
- commandFailedEvent
- connectionClosedEvent
- connectionCreatedEvent
- database:
id: &database database
client: *client
databaseName: test
tests:
# Test that appending metadata after `MongoClient` initialization does not close existing
# connections, create new ones, and that no new `hello` command is sent.
- description: metadata append does not create new connections or close existing ones and no hello command is sent
operations:
- name: runCommand
object: *database
arguments:
commandName: ping
command:
ping: 1
expectResult:
ok: 1
- name: appendMetadata
object: *client
arguments:
driverInfoOptions:
name: framework
version: '2.0'
platform: Framework Platform
- name: runCommand
object: *database
arguments:
commandName: ping
command:
ping: 1
expectResult:
ok: 1
expectEvents:
- client: *client
eventType: cmap
events:
# Expect only one connection to be created for the first 'ping' command.
- connectionCreatedEvent: { }
- client: *client
eventType: command
events:
- commandSucceededEvent:
commandName: ping
- commandSucceededEvent:
commandName: ping

15 changes: 15 additions & 0 deletions source/unified-test-format/unified-test-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -1491,6 +1491,19 @@ driver behavior when an operation is attempted on a closed client or one of its

<span id="client_createChangeStream"></span>

#### appendMetadata

Appends client metadata to the client.

The following arguments are supported:

- `driverInfoOptions`: Required object with the following fields:
- `name`: Required string. The name of the wrapping library or framework.
- `version`: Optional string. The version of the wrapping library or framework.
- `platform`: Optional string. The platform of the wrapping library or framework.

See [handshake](../mongodb-handshake/handshake.md#metadata-updates-after-mongoclient-initialization).

#### createChangeStream

Creates a cluster-level change stream and ensures that the server-side cursor has been created.
Expand Down Expand Up @@ -3583,6 +3596,8 @@ other specs *and* collating spec changes developed in parallel or during the sam

## Changelog

- 2025-06-09: Add `appendMetadata` operation.

- 2025-06-04: Deprecate the `serverless` runOnRequirement

- 2025-04-25: Drop `_enxcol` collections.
Expand Down
Loading