Skip to content

Commit d5adadb

Browse files
authored
DRIVERS-2985: Allow on-demand client metadata updates after MongoClient initialization. (#1798)
1 parent 92789fe commit d5adadb

File tree

5 files changed

+322
-3
lines changed

5 files changed

+322
-3
lines changed

source/mongodb-handshake/handshake.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -405,10 +405,29 @@ class DriverInfoOptions {
405405
}
406406
```
407407

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

410-
If provided, these options MUST NOT replace the values used for metadata generation. The provided options MUST be
411-
appended to their respective fields, and be delimited by a `|` character. For example, when
410+
### Metadata updates after MongoClient initialization
411+
412+
Drivers MUST provide an API that allows appending `DriverInfoOptions` to a `MongoClient` instance after initialization,
413+
adhering to the pattern described below while following idioms of the language of the driver:
414+
415+
```java
416+
interface MongoClient {
417+
void appendMetadata(DriverInfoOptions driverInfoOptions);
418+
// other existing members of MongoClient
419+
}
420+
```
421+
422+
After client metadata update, drivers MUST apply updated metadata to newly created connections. Drivers MUST NOT apply
423+
updated metadata to already established connections, create new connections, or close existing connections solely for
424+
the purpose of transferring updated metadata.
425+
426+
### Appending metadata
427+
428+
If `DriverInfoOptions` are provided during or after `MongoClient` initialization, these options MUST NOT replace any
429+
existing metadata values, including driver-generated metadata and previously provided options. The provided options MUST
430+
be appended to their respective fields, and be delimited by a `|` character. For example, when
412431
[Motor](https://www.mongodb.com/docs/drivers/motor/) wraps PyMongo, the following fields are updated to include Motor's
413432
"driver info":
414433

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

535554
## Changelog
536555

556+
- 2025-06-09: Add requirement to allow appending to client metadata after `MongoClient` initialization.
537557
- 2024-11-05: Move handshake prose tests from spec file to prose test file.
538558
- 2024-10-09: Clarify that FaaS and container metadata must both be populated when both are present.
539559
- 2024-08-16: Migrated from reStructuredText to Markdown.

source/mongodb-handshake/tests/README.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,128 @@ the following sets of environment variables:
8282
2. Create and connect a `Connection` object that connects to the server that returns the mocked response.
8383

8484
3. Assert that no error is raised.
85+
86+
## Client Metadata Update Prose Tests
87+
88+
Drivers that do not emit events for commands issued as part of the handshake with the server will need to create a
89+
test-only backdoor mechanism to intercept the handshake `hello` command for verification purposes.
90+
91+
### Test 1: Test that the driver updates metadata
92+
93+
Drivers should verify that metadata provided after `MongoClient` initialization is appended, not replaced, and is
94+
visible in the `hello` command of new connections.
95+
96+
There are multiple test cases parameterized with `DriverInfoOptions` to be appended after `MongoClient` initialization.
97+
Before each test case, perform the setup.
98+
99+
#### Setup
100+
101+
1. Create a `MongoClient` instance with the following:
102+
103+
- `maxIdleTimeMS` set to `1ms`
104+
105+
- Wrapping library metadata:
106+
107+
| Field | Value |
108+
| -------- | ---------------- |
109+
| name | library |
110+
| version | 1.2 |
111+
| platform | Library Platform |
112+
113+
2. Send a `ping` command to the server and verify that the command succeeds.
114+
115+
3. Save intercepted `client` document as `initialClientMetadata`.
116+
117+
4. Wait 5ms for the connection to become idle.
118+
119+
#### Parameterized test cases
120+
121+
| Case | Name | Version | Platform |
122+
| ---- | --------- | ------- | ------------------ |
123+
| 1 | framework | 2.0 | Framework Platform |
124+
| 2 | framework | 2.0 | null |
125+
| 3 | framework | null | Framework Platform |
126+
| 4 | framework | null | null |
127+
128+
#### Running a test case
129+
130+
1. Append the `DriverInfoOptions` from the selected test case to the `MongoClient` metadata.
131+
132+
2. Send a `ping` command to the server and verify:
133+
134+
- The command succeeds.
135+
136+
- The framework metadata is appended to the existing `DriverInfoOptions` in the `client.driver` fields of the `hello`
137+
command, with values separated by a pipe `|`.
138+
139+
- `client.driver.name`:
140+
- If test case's name is non-null: `library|<name>`
141+
- Otherwise, the field remains unchanged: `library`
142+
- `client.driver.version`:
143+
- If test case's version is non-null: `1.2|<version>`
144+
- Otherwise, the field remains unchanged: `1.2`
145+
- `client.driver.platform`:
146+
- If test case's platform is non-null: `Library Platform|<platform>`
147+
- Otherwise, the field remains unchanged: `Library Platform`
148+
149+
- All other subfields in the `client` document remain unchanged from `initialClientMetadata`.
150+
151+
## Test 2: Multiple Successive Metadata Updates
152+
153+
Drivers should verify that after `MongoClient` initialization, metadata can be updated multiple times, not replaced, and
154+
is visible in the `hello` command of new connections.
155+
156+
There are multiple test cases parameterized with `DriverInfoOptions` to be appended after a previous metadata update.
157+
Before each test case, perform the setup.
158+
159+
### Setup
160+
161+
1. Create a `MongoClient` instance with:
162+
163+
- `maxIdleTimeMS` set to `1ms`
164+
165+
2. Append the following `DriverInfoOptions` to the `MongoClient` metadata:
166+
167+
| Field | Value |
168+
| -------- | ---------------- |
169+
| name | library |
170+
| version | 1.2 |
171+
| platform | Library Platform |
172+
173+
3. Send a `ping` command to the server and verify that the command succeeds.
174+
175+
4. Save intercepted `client` document as `updatedClientMetadata`.
176+
177+
5. Wait 5ms for the connection to become idle.
178+
179+
#### Parameterized test cases
180+
181+
| Case | Name | Version | Platform |
182+
| ---- | --------- | ------- | ------------------ |
183+
| 1 | framework | 2.0 | Framework Platform |
184+
| 2 | framework | 2.0 | null |
185+
| 3 | framework | null | Framework Platform |
186+
| 4 | framework | null | null |
187+
188+
#### Running a test case
189+
190+
1. Append the `DriverInfoOptions` from the selected test case to the `MongoClient` metadata.
191+
192+
2. Send a `ping` command to the server and verify:
193+
194+
- The command succeeds.
195+
196+
- The framework metadata is appended to the existing `DriverInfoOptions` in the `client.driver` fields of the `hello`
197+
command, with values separated by a pipe `|`.
198+
199+
- `client.driver.name`:
200+
- If test case's name is non-null: `library|<name>`
201+
- Otherwise, the field remains unchanged: `library`
202+
- `client.driver.version`:
203+
- If test case's version is non-null: `1.2|<version>`
204+
- Otherwise, the field remains unchanged: `1.2`
205+
- `client.driver.platform`:
206+
- If test case's platform is non-null: `Library Platform|<platform>`
207+
- Otherwise, the field remains unchanged: `Library Platform`
208+
209+
- All other subfields in the `client` document remain unchanged from `updatedClientMetadata`.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
{
2+
"description": "client metadata is not propagated to the server",
3+
"schemaVersion": "1.9",
4+
"runOnRequirements": [
5+
{
6+
"minServerVersion": "6.0"
7+
}
8+
],
9+
"createEntities": [
10+
{
11+
"client": {
12+
"id": "client",
13+
"observeEvents": [
14+
"commandSucceededEvent",
15+
"commandFailedEvent",
16+
"connectionClosedEvent",
17+
"connectionCreatedEvent"
18+
]
19+
}
20+
},
21+
{
22+
"database": {
23+
"id": "database",
24+
"client": "client",
25+
"databaseName": "test"
26+
}
27+
}
28+
],
29+
"tests": [
30+
{
31+
"description": "metadata append does not create new connections or close existing ones and no hello command is sent",
32+
"operations": [
33+
{
34+
"name": "runCommand",
35+
"object": "database",
36+
"arguments": {
37+
"commandName": "ping",
38+
"command": {
39+
"ping": 1
40+
}
41+
},
42+
"expectResult": {
43+
"ok": 1
44+
}
45+
},
46+
{
47+
"name": "appendMetadata",
48+
"object": "client",
49+
"arguments": {
50+
"driverInfoOptions": {
51+
"name": "framework",
52+
"version": "2.0",
53+
"platform": "Framework Platform"
54+
}
55+
}
56+
},
57+
{
58+
"name": "runCommand",
59+
"object": "database",
60+
"arguments": {
61+
"commandName": "ping",
62+
"command": {
63+
"ping": 1
64+
}
65+
},
66+
"expectResult": {
67+
"ok": 1
68+
}
69+
}
70+
],
71+
"expectEvents": [
72+
{
73+
"client": "client",
74+
"eventType": "cmap",
75+
"events": [
76+
{
77+
"connectionCreatedEvent": {}
78+
}
79+
]
80+
},
81+
{
82+
"client": "client",
83+
"eventType": "command",
84+
"events": [
85+
{
86+
"commandSucceededEvent": {
87+
"commandName": "ping"
88+
}
89+
},
90+
{
91+
"commandSucceededEvent": {
92+
"commandName": "ping"
93+
}
94+
}
95+
]
96+
}
97+
]
98+
}
99+
]
100+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
description: client metadata is not propagated to the server
2+
schemaVersion: '1.9'
3+
runOnRequirements:
4+
- minServerVersion: '6.0'
5+
createEntities:
6+
- client:
7+
id: &client client
8+
observeEvents:
9+
- commandSucceededEvent
10+
- commandFailedEvent
11+
- connectionClosedEvent
12+
- connectionCreatedEvent
13+
- database:
14+
id: &database database
15+
client: *client
16+
databaseName: test
17+
tests:
18+
# Test that appending metadata after `MongoClient` initialization does not close existing
19+
# connections, create new ones, and that no new `hello` command is sent.
20+
- description: metadata append does not create new connections or close existing ones and no hello command is sent
21+
operations:
22+
- name: runCommand
23+
object: *database
24+
arguments:
25+
commandName: ping
26+
command:
27+
ping: 1
28+
expectResult:
29+
ok: 1
30+
- name: appendMetadata
31+
object: *client
32+
arguments:
33+
driverInfoOptions:
34+
name: framework
35+
version: '2.0'
36+
platform: Framework Platform
37+
- name: runCommand
38+
object: *database
39+
arguments:
40+
commandName: ping
41+
command:
42+
ping: 1
43+
expectResult:
44+
ok: 1
45+
expectEvents:
46+
- client: *client
47+
eventType: cmap
48+
events:
49+
# Expect only one connection to be created for the first 'ping' command.
50+
- connectionCreatedEvent: { }
51+
- client: *client
52+
eventType: command
53+
events:
54+
- commandSucceededEvent:
55+
commandName: ping
56+
- commandSucceededEvent:
57+
commandName: ping
58+

source/unified-test-format/unified-test-format.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,20 @@ driver behavior when an operation is attempted on a closed client or one of its
14911491

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

1494+
#### appendMetadata
1495+
1496+
Appends client metadata to the client.
1497+
1498+
The following arguments are supported:
1499+
1500+
- `driverInfoOptions`: Required object with the following fields:
1501+
- `name`: Required string. The name of the wrapping library or framework.
1502+
- `version`: Optional string. The version of the wrapping library or framework.
1503+
- `platform`: Optional string. The platform of the wrapping library or framework.
1504+
1505+
See the [handshake spec](../mongodb-handshake/handshake.md#metadata-updates-after-mongoclient-initialization) for more
1506+
details.
1507+
14941508
#### createChangeStream
14951509

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

35843598
## Changelog
35853599

3600+
- 2025-06-09: Add `appendMetadata` operation.
3601+
35863602
- 2025-06-04: Deprecate the `serverless` runOnRequirement
35873603

35883604
- 2025-04-25: Drop `_enxcol` collections.

0 commit comments

Comments
 (0)