From 9b52910e9a57bfd5cdc8380c84d15878b912453d Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Tue, 16 Jun 2026 12:44:07 -0400 Subject: [PATCH 01/16] DRIVERS-3535 - Client Backpressure with retryAfterMS --- .../client-backpressure.md | 31 +++++++--- source/client-backpressure/tests/README.md | 62 +++++++++++++++++++ 2 files changed, 84 insertions(+), 9 deletions(-) diff --git a/source/client-backpressure/client-backpressure.md b/source/client-backpressure/client-backpressure.md index 6aea3d2aaa..90f48f7dbc 100644 --- a/source/client-backpressure/client-backpressure.md +++ b/source/client-backpressure/client-backpressure.md @@ -129,12 +129,18 @@ rules: - To retry `runCommand`, both [retryWrites](../retryable-writes/retryable-writes.md#retrywrites) and [retryReads](../retryable-reads/retryable-reads.md#retryreads) MUST be enabled. See [Why must both `retryWrites` and `retryReads` be enabled to retry runCommand?](client-backpressure.md#why-must-both-retrywrites-and-retryreads-be-enabled-to-retry-runcommand) -3. If the request is eligible for retry (as outlined in step 2 above), the client MUST apply exponential backoff - according to the following formula: `backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2^(attempt - 1))` - - `jitter` is a random jitter value between 0 and 1. - - `BASE_BACKOFF` is constant 100ms. - - `MAX_BACKOFF` is 10000ms. - - This results in delays of 100ms and 200ms before accounting for jitter. +3. If the request is eligible for retry (as outlined in step 2 above), the client MUST apply backoff according to the + following rules: + 1. If `retryAfterMS` is present on the error and has a positive value, apply backoff according to the following + formula: `backoff = retryAfterMS.value + (jitter * retryAfterMS)` + - `jitter` is a random jitter value between -0.5 and 0.5. + - `retryAfterMS.value` is the value of the error's `retryAfterMS` field. + 2. If `retryAfterMS` is not present, apply exponential backoff according to the following formula: + `backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2^(attempt - 1))` + - `jitter` is a random jitter value between 0 and 1. + - `BASE_BACKOFF` is constant 100ms. + - `MAX_BACKOFF` is 10000ms. + - This results in delays of 100ms and 200ms before accounting for jitter. 4. If the request is eligible for retry (as outlined in step 2 above) and `enableOverloadRetargeting` is enabled, the client MUST add the previously used server's address to the list of deprioritized server addresses for [server selection](../server-selection/server-selection.md). Drivers MUST expose `enableOverloadRetargeting` as a @@ -213,9 +219,16 @@ def execute_command_retryable(command, ...): deprioritized_servers.append(server.address) if is_overload: - jitter = random.random() # Random float between [0.0, 1.0). - backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2 ** (attempt - 1)) - + # If present on the error, retryAfterMS sets the backoff + retry_after_ms = exc.retry_after_ms + if retry_after_ms: + retry_after /= 1000 # Convert from milliseconds to seconds + jitter = random.uniform(-0.5, 0.5) # Random float between [-0.5, 0.5]. + backoff = (jitter * retry_after) + retry_after + # Otherwise fall back to exponential + else: + jitter = random.random() # Random float between [0.0, 1.0). + backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2 ** (attempt - 1)) # If the delay exceeds the deadline, bail early. if _csot.get_timeout(): if time.monotonic() + backoff > _csot.get_deadline(): diff --git a/source/client-backpressure/tests/README.md b/source/client-backpressure/tests/README.md index 17becefd0a..8f25f21f0f 100644 --- a/source/client-backpressure/tests/README.md +++ b/source/client-backpressure/tests/README.md @@ -119,3 +119,65 @@ option. 5. Assert that the raised error contains both the `RetryableError` and `SystemOverloadedError` error labels. 6. Assert that the total number of started commands is `maxAdaptiveRetries` + 1 (2). + +#### Test 5: Overload Errors with retryAfterMS override exponential backoff + +Drivers should test that overload errors with `retryAfterMS` override the default exponential backoff policy. This test +MUST be executed against a MongoDB 9.0+ server that has enabled the `configureFailPoint` command with the `errorLabels` +option. + +1. Let `client` be a `MongoClient`. + +2. Let `coll` be a collection. + +3. Configure the random number generator used for exponential backoff jitter to always return a number as close as + possible to `1`. + +4. Configure the following failPoint: + + ```javascript + { + configureFailPoint: 'failCommand', + mode: 'alwaysOn', + data: { + failCommands: ['insert'], + errorCode: 462, + errorLabels: ['SystemOverloadedError', 'RetryableError'] + } + } + ``` + +5. Insert the document `{ a: 1 }`. Expect that the command errors. Measure the duration of the command execution. + + ```javascript + const start = performance.now(); + expect( + await coll.insertOne({ a: 1 }).catch(e => e) + ).to.be.an.instanceof(MongoServerError); + const end = performance.now(); + ``` + +6. Configure the random number generator used for `retryAfterMS` jitter to always return `0`. + +7. Run the following command to set up `retryAfterMS` on overload errors. + + ```python + client.admin.command("setParameter", 1, overloadRetryAfterMS=50) + ``` + +8. Execute step 5 again. + +9. Run the following command to disable `retryAfterMS` on overload errors. + + ```python + client.admin.command("setParameter", 1, overloadRetryAfterMS=0) + ``` + +10. Compare the time between the two runs. + + ```python + assertTrue(absolute_value(exponential_backoff_time - (with_retry_after_ms_time + 0.2 seconds)) < 0.2 seconds) + ``` + + The difference in the backoffs is 0.2 seconds. There is a 0.2-second window to account for potential variance + between the two runs. From 5af946f0c27ad7180d0a1eda60a877fcd0eef918 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Tue, 16 Jun 2026 12:50:48 -0400 Subject: [PATCH 02/16] Clarity --- source/client-backpressure/client-backpressure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/client-backpressure/client-backpressure.md b/source/client-backpressure/client-backpressure.md index 90f48f7dbc..49fbb6cebd 100644 --- a/source/client-backpressure/client-backpressure.md +++ b/source/client-backpressure/client-backpressure.md @@ -135,7 +135,7 @@ rules: formula: `backoff = retryAfterMS.value + (jitter * retryAfterMS)` - `jitter` is a random jitter value between -0.5 and 0.5. - `retryAfterMS.value` is the value of the error's `retryAfterMS` field. - 2. If `retryAfterMS` is not present, apply exponential backoff according to the following formula: + 2. Otherwise, apply exponential backoff according to the following formula: `backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2^(attempt - 1))` - `jitter` is a random jitter value between 0 and 1. - `BASE_BACKOFF` is constant 100ms. From 90e37d956fd82cf32dae3d057a751aca24b45484 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Tue, 16 Jun 2026 12:52:06 -0400 Subject: [PATCH 03/16] Update changelog --- source/client-backpressure/client-backpressure.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/client-backpressure/client-backpressure.md b/source/client-backpressure/client-backpressure.md index 49fbb6cebd..b8c5b02f8d 100644 --- a/source/client-backpressure/client-backpressure.md +++ b/source/client-backpressure/client-backpressure.md @@ -446,6 +446,8 @@ to understand and configure. ## Changelog +- 2026-06-16: Add support for retryAfterMS backoff calculation. + - 2026-04-14: Clarify correct retry behavior when a mix of overload and non-overload errors are encountered. - 2026-03-30: Introduce phase 1 support without token buckets. From d57a0a4702542538e9a8cf6e5ca63612d6a397a4 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 18 Jun 2026 09:31:15 -0400 Subject: [PATCH 04/16] Update source/client-backpressure/client-backpressure.md Co-authored-by: Sergey Zelenov --- source/client-backpressure/client-backpressure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/client-backpressure/client-backpressure.md b/source/client-backpressure/client-backpressure.md index b8c5b02f8d..a9a9cc379f 100644 --- a/source/client-backpressure/client-backpressure.md +++ b/source/client-backpressure/client-backpressure.md @@ -132,7 +132,7 @@ rules: 3. If the request is eligible for retry (as outlined in step 2 above), the client MUST apply backoff according to the following rules: 1. If `retryAfterMS` is present on the error and has a positive value, apply backoff according to the following - formula: `backoff = retryAfterMS.value + (jitter * retryAfterMS)` + formula: `backoff = retryAfterMS.value + (jitter * retryAfterMS.value)` - `jitter` is a random jitter value between -0.5 and 0.5. - `retryAfterMS.value` is the value of the error's `retryAfterMS` field. 2. Otherwise, apply exponential backoff according to the following formula: From 1dfcedb91458a5486c315501ed94b2c231288fc2 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 18 Jun 2026 09:34:05 -0400 Subject: [PATCH 05/16] SZ review --- source/client-backpressure/client-backpressure.md | 2 +- source/mongodb-handshake/handshake.md | 2 +- source/mongodb-handshake/tests/README.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/client-backpressure/client-backpressure.md b/source/client-backpressure/client-backpressure.md index a9a9cc379f..ee5589eaf0 100644 --- a/source/client-backpressure/client-backpressure.md +++ b/source/client-backpressure/client-backpressure.md @@ -222,7 +222,7 @@ def execute_command_retryable(command, ...): # If present on the error, retryAfterMS sets the backoff retry_after_ms = exc.retry_after_ms if retry_after_ms: - retry_after /= 1000 # Convert from milliseconds to seconds + retry_after = retry_after / 1000 # Convert from milliseconds to seconds jitter = random.uniform(-0.5, 0.5) # Random float between [-0.5, 0.5]. backoff = (jitter * retry_after) + retry_after # Otherwise fall back to exponential diff --git a/source/mongodb-handshake/handshake.md b/source/mongodb-handshake/handshake.md index e8d39bf6b9..5521f7e9de 100644 --- a/source/mongodb-handshake/handshake.md +++ b/source/mongodb-handshake/handshake.md @@ -85,7 +85,7 @@ if stable_api_configured or client_options.load_balanced: cmd = {"hello": 1} else: cmd = {"legacy hello": 1, "helloOk": 1} -cmd["backpressure"] = True +cmd["backpressure"] = "2" cmd["client"] = client_metadata if client_options.compressors: cmd["compression"] = client_options.compressors diff --git a/source/mongodb-handshake/tests/README.md b/source/mongodb-handshake/tests/README.md index 296c87ee02..3ee8369fa7 100644 --- a/source/mongodb-handshake/tests/README.md +++ b/source/mongodb-handshake/tests/README.md @@ -499,7 +499,7 @@ Before each test case, perform the setup. 8. Assert that `initialClientMetadata` is identical to `updatedClientMetadata`. -### Test 9: Handshake documents include `backpressure: true` +### Test 9: Handshake documents include `backpressure: 2` These tests require a mechanism for observing handshake documents sent to the server. @@ -511,4 +511,4 @@ These tests require a mechanism for observing handshake documents sent to the se 3. Assert that for every handshake document intercepted: - 1. The document has a field `backpressure` whose value is `true`. + 1. The document has a field `backpressure` whose value is `"2"`. From 07581aa79a7b36e1820afc4269bc62c5b76550ad Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Tue, 23 Jun 2026 08:55:11 -0400 Subject: [PATCH 06/16] Update source/client-backpressure/tests/README.md Co-authored-by: Steven Silvester --- source/client-backpressure/tests/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/client-backpressure/tests/README.md b/source/client-backpressure/tests/README.md index 8f25f21f0f..09125e1f20 100644 --- a/source/client-backpressure/tests/README.md +++ b/source/client-backpressure/tests/README.md @@ -122,7 +122,7 @@ option. #### Test 5: Overload Errors with retryAfterMS override exponential backoff -Drivers should test that overload errors with `retryAfterMS` override the default exponential backoff policy. This test +Drivers SHOULD test that overload errors with `retryAfterMS` override the default exponential backoff policy. This test MUST be executed against a MongoDB 9.0+ server that has enabled the `configureFailPoint` command with the `errorLabels` option. From c66215642c323f33f619de5078f46cd20a607a5b Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Tue, 23 Jun 2026 10:47:00 -0400 Subject: [PATCH 07/16] _MAX_BACKOFF applies to retryAfterMS --- source/client-backpressure/client-backpressure.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/client-backpressure/client-backpressure.md b/source/client-backpressure/client-backpressure.md index ee5589eaf0..93c808e561 100644 --- a/source/client-backpressure/client-backpressure.md +++ b/source/client-backpressure/client-backpressure.md @@ -132,9 +132,10 @@ rules: 3. If the request is eligible for retry (as outlined in step 2 above), the client MUST apply backoff according to the following rules: 1. If `retryAfterMS` is present on the error and has a positive value, apply backoff according to the following - formula: `backoff = retryAfterMS.value + (jitter * retryAfterMS.value)` + formula: `backoff = min(MAX_BACKOFF, retryAfterMS.value + (jitter * retryAfterMS.value))` - `jitter` is a random jitter value between -0.5 and 0.5. - `retryAfterMS.value` is the value of the error's `retryAfterMS` field. + - `MAX_BACKOFF` is 10000ms. 2. Otherwise, apply exponential backoff according to the following formula: `backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2^(attempt - 1))` - `jitter` is a random jitter value between 0 and 1. From e4abae5fe6610f40a0c1cf45b8a11866d62f66e7 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Tue, 23 Jun 2026 12:05:30 -0400 Subject: [PATCH 08/16] Simplify backoff formula --- source/client-backpressure/client-backpressure.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/source/client-backpressure/client-backpressure.md b/source/client-backpressure/client-backpressure.md index 93c808e561..e34612908b 100644 --- a/source/client-backpressure/client-backpressure.md +++ b/source/client-backpressure/client-backpressure.md @@ -131,16 +131,13 @@ rules: [Why must both `retryWrites` and `retryReads` be enabled to retry runCommand?](client-backpressure.md#why-must-both-retrywrites-and-retryreads-be-enabled-to-retry-runcommand) 3. If the request is eligible for retry (as outlined in step 2 above), the client MUST apply backoff according to the following rules: - 1. If `retryAfterMS` is present on the error and has a positive value, apply backoff according to the following - formula: `backoff = min(MAX_BACKOFF, retryAfterMS.value + (jitter * retryAfterMS.value))` - - `jitter` is a random jitter value between -0.5 and 0.5. - - `retryAfterMS.value` is the value of the error's `retryAfterMS` field. - - `MAX_BACKOFF` is 10000ms. - 2. Otherwise, apply exponential backoff according to the following formula: - `backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2^(attempt - 1))` + 1. If `retryAfterMS` is present on the error and has a positive value, use the following formula: + `backoff = jitter * min(MAX_BACKOFF, retryAfterMS.value * 2^(attempt - 1))` - `jitter` is a random jitter value between 0 and 1. - `BASE_BACKOFF` is constant 100ms. - `MAX_BACKOFF` is 10000ms. + - `retryAfterMS.value` is the value of the error's `retryAfterMS` field. + 2. Otherwise, use `BASE_BACKOFF` instead of `retryAfterMS.value` in the same formula. - This results in delays of 100ms and 200ms before accounting for jitter. 4. If the request is eligible for retry (as outlined in step 2 above) and `enableOverloadRetargeting` is enabled, the client MUST add the previously used server's address to the list of deprioritized server addresses for From 63426b9622531e87afecb7c9136aa7b3e68d0ee6 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Tue, 23 Jun 2026 12:07:44 -0400 Subject: [PATCH 09/16] Update retryAfterMS prose test --- source/client-backpressure/tests/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/client-backpressure/tests/README.md b/source/client-backpressure/tests/README.md index 09125e1f20..4de02c3b5c 100644 --- a/source/client-backpressure/tests/README.md +++ b/source/client-backpressure/tests/README.md @@ -157,7 +157,8 @@ option. const end = performance.now(); ``` -6. Configure the random number generator used for `retryAfterMS` jitter to always return `0`. +6. Configure the random number generator used for exponential backoff jitter to always return a number as close as + possible to `1`. 7. Run the following command to set up `retryAfterMS` on overload errors. From 6d6a071bd836b15b91d3cf4522fa1fcaf2785133 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Tue, 23 Jun 2026 12:09:15 -0400 Subject: [PATCH 10/16] Cleanup test --- source/client-backpressure/tests/README.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/source/client-backpressure/tests/README.md b/source/client-backpressure/tests/README.md index 4de02c3b5c..ce816f9ce7 100644 --- a/source/client-backpressure/tests/README.md +++ b/source/client-backpressure/tests/README.md @@ -157,28 +157,25 @@ option. const end = performance.now(); ``` -6. Configure the random number generator used for exponential backoff jitter to always return a number as close as - possible to `1`. - -7. Run the following command to set up `retryAfterMS` on overload errors. +6. Run the following command to set up `retryAfterMS` on overload errors. ```python client.admin.command("setParameter", 1, overloadRetryAfterMS=50) ``` -8. Execute step 5 again. +7. Execute step 5 again. -9. Run the following command to disable `retryAfterMS` on overload errors. +8. Run the following command to disable `retryAfterMS` on overload errors. ```python client.admin.command("setParameter", 1, overloadRetryAfterMS=0) ``` -10. Compare the time between the two runs. +9. Compare the time between the two runs. ```python assertTrue(absolute_value(exponential_backoff_time - (with_retry_after_ms_time + 0.2 seconds)) < 0.2 seconds) ``` - The difference in the backoffs is 0.2 seconds. There is a 0.2-second window to account for potential variance - between the two runs. + The difference in the backoffs is 0.2 seconds. There is a 0.2-second window to account for potential variance between + the two runs. From 2ad0956869c8f2b22f425157c54a08b4197312f0 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Tue, 23 Jun 2026 12:11:04 -0400 Subject: [PATCH 11/16] Update pseudocode --- source/client-backpressure/client-backpressure.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/client-backpressure/client-backpressure.md b/source/client-backpressure/client-backpressure.md index e34612908b..e5f3495326 100644 --- a/source/client-backpressure/client-backpressure.md +++ b/source/client-backpressure/client-backpressure.md @@ -217,13 +217,12 @@ def execute_command_retryable(command, ...): deprioritized_servers.append(server.address) if is_overload: - # If present on the error, retryAfterMS sets the backoff + # If present on the error, retryAfterMS sets the base backoff retry_after_ms = exc.retry_after_ms if retry_after_ms: retry_after = retry_after / 1000 # Convert from milliseconds to seconds - jitter = random.uniform(-0.5, 0.5) # Random float between [-0.5, 0.5]. - backoff = (jitter * retry_after) + retry_after - # Otherwise fall back to exponential + backoff = jitter * min(MAX_BACKOFF, retry_after * 2 ** (attempt - 1)) + # Otherwise fall back to BASE_BACKOFF else: jitter = random.random() # Random float between [0.0, 1.0). backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2 ** (attempt - 1)) From 132c52b96b502479c5a2582f4fcfaa40110ffd2d Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Wed, 24 Jun 2026 12:10:34 -0400 Subject: [PATCH 12/16] SS review --- source/client-backpressure/client-backpressure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/client-backpressure/client-backpressure.md b/source/client-backpressure/client-backpressure.md index e5f3495326..e5c1b3c1fe 100644 --- a/source/client-backpressure/client-backpressure.md +++ b/source/client-backpressure/client-backpressure.md @@ -217,6 +217,7 @@ def execute_command_retryable(command, ...): deprioritized_servers.append(server.address) if is_overload: + jitter = random.random() # Random float between [0.0, 1.0). # If present on the error, retryAfterMS sets the base backoff retry_after_ms = exc.retry_after_ms if retry_after_ms: @@ -224,7 +225,6 @@ def execute_command_retryable(command, ...): backoff = jitter * min(MAX_BACKOFF, retry_after * 2 ** (attempt - 1)) # Otherwise fall back to BASE_BACKOFF else: - jitter = random.random() # Random float between [0.0, 1.0). backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2 ** (attempt - 1)) # If the delay exceeds the deadline, bail early. if _csot.get_timeout(): From 540e4772bec0cbb1de0e31b2cec1a7e2725b6d45 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Wed, 24 Jun 2026 12:19:37 -0400 Subject: [PATCH 13/16] SS review --- source/client-backpressure/client-backpressure.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/source/client-backpressure/client-backpressure.md b/source/client-backpressure/client-backpressure.md index e5c1b3c1fe..51043df749 100644 --- a/source/client-backpressure/client-backpressure.md +++ b/source/client-backpressure/client-backpressure.md @@ -218,14 +218,11 @@ def execute_command_retryable(command, ...): if is_overload: jitter = random.random() # Random float between [0.0, 1.0). - # If present on the error, retryAfterMS sets the base backoff - retry_after_ms = exc.retry_after_ms - if retry_after_ms: - retry_after = retry_after / 1000 # Convert from milliseconds to seconds - backoff = jitter * min(MAX_BACKOFF, retry_after * 2 ** (attempt - 1)) - # Otherwise fall back to BASE_BACKOFF - else: - backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2 ** (attempt - 1)) + retry_after = BASE_BACKOFF + # If present on the error, retryAfterMS overrides the base backoff + if exc.retry_after_ms: + retry_after = exc.retry_after_ms / 1000 # Convert from milliseconds to seconds + backoff = jitter * min(MAX_BACKOFF, retry_after * 2 ** (attempt - 1)) # If the delay exceeds the deadline, bail early. if _csot.get_timeout(): if time.monotonic() + backoff > _csot.get_deadline(): From 3618b6b706c1756b6fb2fed6b6e3578eaeb82f0d Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Wed, 24 Jun 2026 12:23:57 -0400 Subject: [PATCH 14/16] Conciseness cleanup --- source/client-backpressure/client-backpressure.md | 13 ++++--------- source/client-backpressure/tests/README.md | 7 +++---- source/mongodb-handshake/tests/README.md | 2 +- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/source/client-backpressure/client-backpressure.md b/source/client-backpressure/client-backpressure.md index 51043df749..3c1f5bd3dd 100644 --- a/source/client-backpressure/client-backpressure.md +++ b/source/client-backpressure/client-backpressure.md @@ -130,15 +130,10 @@ rules: [retryReads](../retryable-reads/retryable-reads.md#retryreads) MUST be enabled. See [Why must both `retryWrites` and `retryReads` be enabled to retry runCommand?](client-backpressure.md#why-must-both-retrywrites-and-retryreads-be-enabled-to-retry-runcommand) 3. If the request is eligible for retry (as outlined in step 2 above), the client MUST apply backoff according to the - following rules: - 1. If `retryAfterMS` is present on the error and has a positive value, use the following formula: - `backoff = jitter * min(MAX_BACKOFF, retryAfterMS.value * 2^(attempt - 1))` - - `jitter` is a random jitter value between 0 and 1. - - `BASE_BACKOFF` is constant 100ms. - - `MAX_BACKOFF` is 10000ms. - - `retryAfterMS.value` is the value of the error's `retryAfterMS` field. - 2. Otherwise, use `BASE_BACKOFF` instead of `retryAfterMS.value` in the same formula. - - This results in delays of 100ms and 200ms before accounting for jitter. + following formula: `backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2^(attempt - 1))` - `jitter` is a random + jitter value between 0 and 1. - `MAX_BACKOFF` is 10000ms. - `BASE_BACKOFF` is constant 100ms. - This results in + delays of 100ms and 200ms before accounting for jitter. + 1. If `retryAfterMS` is present on the error and has a positive value, use that value instead of `BASE_BACKOFF`. 4. If the request is eligible for retry (as outlined in step 2 above) and `enableOverloadRetargeting` is enabled, the client MUST add the previously used server's address to the list of deprioritized server addresses for [server selection](../server-selection/server-selection.md). Drivers MUST expose `enableOverloadRetargeting` as a diff --git a/source/client-backpressure/tests/README.md b/source/client-backpressure/tests/README.md index ce816f9ce7..e1c47f2d3d 100644 --- a/source/client-backpressure/tests/README.md +++ b/source/client-backpressure/tests/README.md @@ -120,11 +120,10 @@ option. 6. Assert that the total number of started commands is `maxAdaptiveRetries` + 1 (2). -#### Test 5: Overload Errors with retryAfterMS override exponential backoff +#### Test 5: Overload Errors with retryAfterMS override base backoff -Drivers SHOULD test that overload errors with `retryAfterMS` override the default exponential backoff policy. This test -MUST be executed against a MongoDB 9.0+ server that has enabled the `configureFailPoint` command with the `errorLabels` -option. +Drivers SHOULD test that overload errors with `retryAfterMS` override the default backoff duration. This test MUST be +executed against a MongoDB 9.0+ server that has enabled the `configureFailPoint` command with the `errorLabels` option. 1. Let `client` be a `MongoClient`. diff --git a/source/mongodb-handshake/tests/README.md b/source/mongodb-handshake/tests/README.md index 3ee8369fa7..ed56e5dc46 100644 --- a/source/mongodb-handshake/tests/README.md +++ b/source/mongodb-handshake/tests/README.md @@ -499,7 +499,7 @@ Before each test case, perform the setup. 8. Assert that `initialClientMetadata` is identical to `updatedClientMetadata`. -### Test 9: Handshake documents include `backpressure: 2` +### Test 9: Handshake documents include `backpressure: "2"` These tests require a mechanism for observing handshake documents sent to the server. From 81e9c1b7035adde933bda3d50a0d38f5a3b3e7f9 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Wed, 24 Jun 2026 13:31:39 -0400 Subject: [PATCH 15/16] Formatting --- source/client-backpressure/client-backpressure.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/client-backpressure/client-backpressure.md b/source/client-backpressure/client-backpressure.md index 3c1f5bd3dd..a2b4c42877 100644 --- a/source/client-backpressure/client-backpressure.md +++ b/source/client-backpressure/client-backpressure.md @@ -130,10 +130,13 @@ rules: [retryReads](../retryable-reads/retryable-reads.md#retryreads) MUST be enabled. See [Why must both `retryWrites` and `retryReads` be enabled to retry runCommand?](client-backpressure.md#why-must-both-retrywrites-and-retryreads-be-enabled-to-retry-runcommand) 3. If the request is eligible for retry (as outlined in step 2 above), the client MUST apply backoff according to the - following formula: `backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2^(attempt - 1))` - `jitter` is a random - jitter value between 0 and 1. - `MAX_BACKOFF` is 10000ms. - `BASE_BACKOFF` is constant 100ms. - This results in - delays of 100ms and 200ms before accounting for jitter. - 1. If `retryAfterMS` is present on the error and has a positive value, use that value instead of `BASE_BACKOFF`. + following formula: `backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2^(attempt - 1))` + 1. `jitter` is a random jitter value between 0 and 1. + 2. `MAX_BACKOFF` is 10000ms. + 3. `BASE_BACKOFF` is constant 100ms. + 4. This results in delays of 100ms and 200ms before accounting for jitter. + 5. If `retryAfterMS` is present on the error and has a positive value, the client MUST use that value instead of + `BASE_BACKOFF`. 4. If the request is eligible for retry (as outlined in step 2 above) and `enableOverloadRetargeting` is enabled, the client MUST add the previously used server's address to the list of deprioritized server addresses for [server selection](../server-selection/server-selection.md). Drivers MUST expose `enableOverloadRetargeting` as a From 45e90e6a0799aac8bb8f696374ded57735692d4c Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 25 Jun 2026 14:17:51 -0400 Subject: [PATCH 16/16] JY review --- source/client-backpressure/client-backpressure.md | 8 +++++--- source/mongodb-handshake/handshake.md | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/source/client-backpressure/client-backpressure.md b/source/client-backpressure/client-backpressure.md index a2b4c42877..610279123a 100644 --- a/source/client-backpressure/client-backpressure.md +++ b/source/client-backpressure/client-backpressure.md @@ -134,9 +134,11 @@ rules: 1. `jitter` is a random jitter value between 0 and 1. 2. `MAX_BACKOFF` is 10000ms. 3. `BASE_BACKOFF` is constant 100ms. - 4. This results in delays of 100ms and 200ms before accounting for jitter. - 5. If `retryAfterMS` is present on the error and has a positive value, the client MUST use that value instead of - `BASE_BACKOFF`. + 4. `attempt` is the retry number. The first retry is `attempt = 1`, the second is `attempt = 2`, and so on. + 5. This results in delays of 100ms and 200ms before accounting for jitter. + 6. If `retryAfterMS` is present on the error and has a positive value, the client MUST use that value instead of + `BASE_BACKOFF`. `retryAfterMS` represents a server-supplied base backoff to use in place of the driver's + default. 4. If the request is eligible for retry (as outlined in step 2 above) and `enableOverloadRetargeting` is enabled, the client MUST add the previously used server's address to the list of deprioritized server addresses for [server selection](../server-selection/server-selection.md). Drivers MUST expose `enableOverloadRetargeting` as a diff --git a/source/mongodb-handshake/handshake.md b/source/mongodb-handshake/handshake.md index 5521f7e9de..07c326b5d4 100644 --- a/source/mongodb-handshake/handshake.md +++ b/source/mongodb-handshake/handshake.md @@ -55,6 +55,10 @@ Drivers MUST use the `OP_MSG` protocol for all handshakes if their minWireVersio MUST use legacy hello for the first message of the initial handshake, and include `helloOk:true` in the handshake request. +Drivers MUST include `backpressure: "2"` in their handshake request in order to explicitly version their supported +version of the client backpressure specification. The value of `backpressure` MUST be the string `"2"` and not a literal +number `2`. + If the legacy handshake response includes `helloOk: true`, then subsequent topology monitoring commands MUST use the `hello` command. If the legacy handshake response does not include `helloOk: true`, then subsequent topology monitoring commands MUST use the legacy hello command. Additionally, note that if the server does not understand `OP_MSG`, the @@ -561,6 +565,7 @@ support the `hello` command, the `helloOk: true` argument is ignored and the leg ## Changelog +- 2026-06-25: Clarify the client backpressure component of the handshake. - 2026-06-11: Clarify that there is no new behavior as a result of only using OP_MSG for all handshakes. - 2026-06-05: Use OP_MSG for all handshakes. - 2025-09-04: Clarify that drivers do not append the same metadata multiple times.