Skip to content

Commit e133c20

Browse files
DRIVERS-3262: appendMetadata() does not append duplicate metadata (#1833)
1 parent e8be2e9 commit e133c20

File tree

2 files changed

+299
-9
lines changed

2 files changed

+299
-9
lines changed

source/mongodb-handshake/handshake.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ Drivers MUST NOT provide a default value for this key.
185185

186186
This value is required and is not application configurable.
187187

188-
The internal driver name. For drivers written on-top of other core drivers, the underlying driver will typically expose
189-
a function to append additional name to this field.
188+
The internal driver name. For drivers written on-top of other core drivers, the `appendMetadata()` method can be used to
189+
add package information to an existing MongoClient.
190190

191191
Example:
192192

@@ -200,7 +200,7 @@ Example:
200200
This value is required and is not application configurable.
201201

202202
The internal driver version. The version formatting is not defined. For drivers written on-top of other core drivers,
203-
the underlying driver will typically expose a function to append additional name to this field.
203+
the `appendMetadata()` method can be used to add package information to an existing MongoClient.
204204

205205
Example:
206206

@@ -405,6 +405,10 @@ class DriverInfoOptions {
405405
}
406406
```
407407

408+
Two `DriverInfoOptions` objects are considered equal if they would result in the same metadata in the client handshake.
409+
In practice, this means if a field is the empty string (`""`), treat it as unset. Assert that each field is strictly
410+
equal with case sensitive string comparison.
411+
408412
Note that how these options are provided to a driver during `MongoClient` initialization is left up to the implementer.
409413

410414
### Metadata updates after MongoClient initialization
@@ -442,6 +446,12 @@ be appended to their respective fields, and be delimited by a `|` character. For
442446
}
443447
```
444448

449+
Some client libraries provide APIs that accept a pre-initialized MongoClient as an argument. In these circumstances, it
450+
is possible for multiple library objects to be associated with the same MongoClient, which could result in the same
451+
metadata being appended multiple times. Drivers MUST ensure that any duplicate `DriverInfoOptions` objects provided to a
452+
MongoClient or appended to a MongoClient do not result in additional metadata being appended. See
453+
[Supporting Wrapping Libraries](#supporting-wrapping-libraries).
454+
445455
**NOTE:** All strings provided as part of the driver info MUST NOT contain the delimiter used for metadata concatention.
446456
Drivers MUST throw an error if any of these strings contains that character.
447457

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

554564
## Changelog
555565

566+
- 2025-09-04: Clarify that drivers do not append the same metadata multiple times.
556567
- 2025-06-09: Add requirement to allow appending to client metadata after `MongoClient` initialization.
557568
- 2024-11-05: Move handshake prose tests from spec file to prose test file.
558569
- 2024-10-09: Clarify that FaaS and container metadata must both be populated when both are present.

source/mongodb-handshake/tests/README.md

Lines changed: 285 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,21 +142,21 @@ Before each test case, perform the setup.
142142
- `client.driver.version`:
143143
- If test case's version is non-null: `1.2|<version>`
144144
- Otherwise, the field remains unchanged: `1.2`
145-
- `client.driver.platform`:
145+
- `client.platform`:
146146
- If test case's platform is non-null: `Library Platform|<platform>`
147147
- Otherwise, the field remains unchanged: `Library Platform`
148148

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

151-
## Test 2: Multiple Successive Metadata Updates
151+
### Test 2: Multiple Successive Metadata Updates
152152

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

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

159-
### Setup
159+
#### Setup
160160

161161
1. Create a `MongoClient` instance with:
162162

@@ -176,7 +176,7 @@ Before each test case, perform the setup.
176176

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

179-
#### Parameterized test cases
179+
##### Parameterized test cases
180180

181181
| Case | Name | Version | Platform |
182182
| ---- | --------- | ------- | ------------------ |
@@ -185,7 +185,7 @@ Before each test case, perform the setup.
185185
| 3 | framework | null | Framework Platform |
186186
| 4 | framework | null | null |
187187

188-
#### Running a test case
188+
##### Running a test case
189189

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

@@ -202,8 +202,287 @@ Before each test case, perform the setup.
202202
- `client.driver.version`:
203203
- If test case's version is non-null: `1.2|<version>`
204204
- Otherwise, the field remains unchanged: `1.2`
205-
- `client.driver.platform`:
205+
- `client.platform`:
206206
- If test case's platform is non-null: `Library Platform|<platform>`
207207
- Otherwise, the field remains unchanged: `Library Platform`
208208

209209
- All other subfields in the `client` document remain unchanged from `updatedClientMetadata`.
210+
211+
### Test 3: Multiple Successive Metadata Updates with Duplicate Data
212+
213+
There are multiple test cases parameterized with `DriverInfoOptions` to be appended after a previous metadata update.
214+
Before each test case, perform the setup.
215+
216+
#### Setup
217+
218+
1. Create a `MongoClient` instance with:
219+
220+
- `maxIdleTimeMS` set to `1ms`
221+
222+
2. Append the following `DriverInfoOptions` to the `MongoClient` metadata:
223+
224+
| Field | Value |
225+
| -------- | ---------------- |
226+
| name | library |
227+
| version | 1.2 |
228+
| platform | Library Platform |
229+
230+
3. Send a `ping` command to the server and verify that the command succeeds.
231+
232+
4. Save intercepted `client` document as `updatedClientMetadata`.
233+
234+
5. Wait 5ms for the connection to become idle.
235+
236+
##### Parameterized test cases
237+
238+
| Case | Name | Version | Platform |
239+
| ---- | --------- | ------- | ------------------ |
240+
| 1 | library | 1.2 | Library Platform |
241+
| 2 | framework | 1.2 | Library Platform |
242+
| 3 | library | 2.0 | Library Platform |
243+
| 4 | library | 1.2 | Framework Platform |
244+
| 5 | framework | 2.0 | Library Platform |
245+
| 6 | framework | 1.2 | Framework Platform |
246+
| 7 | library | 2.0 | Framework Platform |
247+
248+
##### Running a test case
249+
250+
1. Append the `DriverInfoOptions` from the selected test case to the `MongoClient` metadata.
251+
252+
2. Send a `ping` command to the server and verify:
253+
254+
- The command succeeds.
255+
256+
- The framework metadata is appended to the existing `DriverInfoOptions` in the `client.driver` fields of the `hello`
257+
command, with values separated by a pipe `|`. To simplify assertions in these tests, strip out the default driver
258+
info that is automatically added by the driver (ex: `metadata.name.split('|').slice(1).join('|')`).
259+
260+
- If the test case's DriverInfo is identical to the driver info from setup step 2 (test case 1):
261+
- Assert `metadata.driver.name` is equal to `library`
262+
- Assert `metadata.driver.version` is equal to `1.2`
263+
- Assert `metadata.platform` is equal to `LibraryPlatform`
264+
- Otherwise:
265+
- Assert `metadata.driver.name` is equal to `library|<name>`
266+
- Assert `metadata.driver.version` is equal to `1.2|<version>`
267+
- Assert `metadata.platform` is equal to `LibraryPlatform|<platform>`
268+
269+
- All other subfields in the `client` document remain unchanged from `updatedClientMetadata`.
270+
271+
### Test 4: Multiple Metadata Updates with Duplicate Data
272+
273+
1. Create a `MongoClient` instance with:
274+
275+
- `maxIdleTimeMS` set to `1ms`
276+
277+
2. Append the following `DriverInfoOptions` to the `MongoClient` metadata:
278+
279+
| Field | Value |
280+
| -------- | ---------------- |
281+
| name | library |
282+
| version | 1.2 |
283+
| platform | Library Platform |
284+
285+
3. Send a `ping` command to the server and verify that the command succeeds.
286+
287+
4. Wait 5ms for the connection to become idle.
288+
289+
5. Append the following `DriverInfoOptions` to the `MongoClient` metadata:
290+
291+
| Field | Value |
292+
| -------- | ------------------ |
293+
| name | framework |
294+
| version | 2.0 |
295+
| platform | Framework Platform |
296+
297+
6. Send a `ping` command to the server and verify that the command succeeds.
298+
299+
7. Save intercepted `client` document as `clientMetadata`.
300+
301+
8. Wait 5ms for the connection to become idle.
302+
303+
9. Append the following `DriverInfoOptions` to the `MongoClient` metadata:
304+
305+
| Field | Value |
306+
| -------- | ---------------- |
307+
| name | library |
308+
| version | 1.2 |
309+
| platform | Library Platform |
310+
311+
10. Send a `ping` command to the server and verify that the command succeeds.
312+
313+
11. Save intercepted `client` document as `updatedClientMetadata`.
314+
315+
12. Assert that `clientMetadata` is identical to `updatedClientMetadata`.
316+
317+
### Test 5: Metadata is not appended if identical to initial metadata
318+
319+
1. Create a `MongoClient` instance with:
320+
321+
- `maxIdleTimeMS` set to `1ms`
322+
- `driverInfo` set to the following:
323+
324+
| Field | Value |
325+
| -------- | ---------------- |
326+
| name | library |
327+
| version | 1.2 |
328+
| platform | Library Platform |
329+
330+
2. Send a `ping` command to the server and verify that the command succeeds.
331+
332+
3. Save intercepted `client` document as `clientMetadata`.
333+
334+
4. Wait 5ms for the connection to become idle.
335+
336+
5. Append the following `DriverInfoOptions` to the `MongoClient` metadata:
337+
338+
| Field | Value |
339+
| -------- | ---------------- |
340+
| name | library |
341+
| version | 1.2 |
342+
| platform | Library Platform |
343+
344+
6. Send a `ping` command to the server and verify that the command succeeds.
345+
346+
7. Save intercepted `client` document as `updatedClientMetadata`.
347+
348+
8. Assert that `clientMetadata` is identical to `updatedClientMetadata`.
349+
350+
### Test 6: Metadata is not appended if identical to initial metadata (separated by non-identical metadata)
351+
352+
1. Create a `MongoClient` instance with:
353+
354+
- `maxIdleTimeMS` set to `1ms`
355+
- `driverInfo` set to the following:
356+
357+
| Field | Value |
358+
| -------- | ---------------- |
359+
| name | library |
360+
| version | 1.2 |
361+
| platform | Library Platform |
362+
363+
2. Send a `ping` command to the server and verify that the command succeeds.
364+
365+
3. Wait 5ms for the connection to become idle.
366+
367+
4. Append the following `DriverInfoOptions` to the `MongoClient` metadata:
368+
369+
| Field | Value |
370+
| -------- | ---------------- |
371+
| name | framework |
372+
| version | 1.2 |
373+
| platform | Library Platform |
374+
375+
5. Send a `ping` command to the server and verify that the command succeeds.
376+
377+
6. Save intercepted `client` document as `clientMetadata`.
378+
379+
7. Wait 5ms for the connection to become idle.
380+
381+
8. Append the following `DriverInfoOptions` to the `MongoClient` metadata:
382+
383+
| Field | Value |
384+
| -------- | ---------------- |
385+
| name | library |
386+
| version | 1.2 |
387+
| platform | Library Platform |
388+
389+
9. Send a `ping` command to the server and verify that the command succeeds.
390+
391+
10. Save intercepted `client` document as `updatedClientMetadata`.
392+
393+
11. Assert that `clientMetadata` is identical to `updatedClientMetadata`.
394+
395+
### Test 7: Empty strings are considered unset when appending duplicate metadata
396+
397+
Drivers should verify that after `MongoClient` initialization, metadata can be updated multiple times, not replaced, and
398+
is visible in the `hello` command of new connections.
399+
400+
There are multiple test cases parameterized with `DriverInfoOptions` to be appended after a previous metadata update.
401+
Before each test case, perform the setup.
402+
403+
##### Parameterized test cases
404+
405+
###### Initial metadata
406+
407+
| Case | Name | Version | Platform |
408+
| ---- | ------- | ------- | ---------------- |
409+
| 1 | null | 1.2 | Library Platform |
410+
| 2 | library | null | Library Platform |
411+
| 3 | library | 1.2 | null |
412+
413+
###### Appended Metadata
414+
415+
| Case | Name | Version | Platform |
416+
| ---- | ------- | ------- | ---------------- |
417+
| 1 | "" | 1.2 | Library Platform |
418+
| 2 | library | "" | Library Platform |
419+
| 3 | library | 1.2 | "" |
420+
421+
##### Running a test case
422+
423+
1. Create a `MongoClient` instance with:
424+
425+
- `maxIdleTimeMS` set to `1ms`
426+
427+
2. Append the `DriverInfoOptions` from the selected test case from the initial metadata section.
428+
429+
3. Send a `ping` command to the server and verify that the command succeeds.
430+
431+
4. Save intercepted `client` document as `initialClientMetadata`.
432+
433+
5. Wait 5ms for the connection to become idle.
434+
435+
6. Append the `DriverInfoOptions` from the selected test case from the appended metadata section.
436+
437+
7. Send a `ping` command to the server and verify the command succeeds.
438+
439+
8. Store the response as `updatedClientMetadata`.
440+
441+
9. Assert that `initialClientMetadata` is identical to `updatedClientMetadata`.
442+
443+
### Test 8: Empty strings are considered unset when appending metadata identical to initial metadata
444+
445+
Drivers should verify that after `MongoClient` initialization, metadata can be updated multiple times, not replaced, and
446+
is visible in the `hello` command of new connections.
447+
448+
There are multiple test cases parameterized with `DriverInfoOptions` to be appended after a previous metadata update.
449+
Before each test case, perform the setup.
450+
451+
##### Parameterized test cases
452+
453+
###### Initial metadata
454+
455+
| Case | Name | Version | Platform |
456+
| ---- | ------- | ------- | ---------------- |
457+
| 1 | null | 1.2 | Library Platform |
458+
| 2 | library | null | Library Platform |
459+
| 3 | library | 1.2 | null |
460+
461+
###### Appended Metadata
462+
463+
| Case | Name | Version | Platform |
464+
| ---- | ------- | ------- | ---------------- |
465+
| 1 | "" | 1.2 | Library Platform |
466+
| 2 | library | "" | Library Platform |
467+
| 3 | library | 1.2 | "" |
468+
469+
##### Running a test case
470+
471+
1. Create a `MongoClient` instance with:
472+
473+
- `maxIdleTimeMS` set to `1ms`
474+
- `driverInfo` set to the `DriverInfoOptions` from the selected test case from the initial metadata section.
475+
476+
2. Send a `ping` command to the server and verify that the command succeeds.
477+
478+
3. Save intercepted `client` document as `initialClientMetadata`.
479+
480+
4. Wait 5ms for the connection to become idle.
481+
482+
5. Append the `DriverInfoOptions` from the selected test case from the appended metadata section.
483+
484+
6. Send a `ping` command to the server and verify the command succeeds.
485+
486+
7. Store the response as `updatedClientMetadata`.
487+
488+
8. Assert that `initialClientMetadata` is identical to `updatedClientMetadata`.

0 commit comments

Comments
 (0)