From 529aad2429d92a9af4670be1d239c15b535e188c Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 19 Feb 2026 17:00:58 -0500 Subject: [PATCH 1/4] DRIVERS-3404 - Server selection deprioritization only for overload errors on replica sets --- source/retryable-reads/retryable-reads.md | 15 ++++- source/retryable-reads/tests/README.md | 64 +++++++++++++++++++++ source/retryable-writes/retryable-writes.md | 13 ++++- 3 files changed, 86 insertions(+), 6 deletions(-) diff --git a/source/retryable-reads/retryable-reads.md b/source/retryable-reads/retryable-reads.md index 2d2a311321..3626b379f8 100644 --- a/source/retryable-reads/retryable-reads.md +++ b/source/retryable-reads/retryable-reads.md @@ -207,8 +207,13 @@ capture this original retryable error. Drivers should then proceed with selectin ###### 3a. Selecting the server for retry -The server address on which the operation failed MUST be provided to the server selection mechanism as a member of the -deprioritized server address list. +For sharded clusters, the server address on which the operation failed MUST be provided to the server selection +mechanism as a member of the deprioritized server address list. + +For all other topologies, the server address on which the operation failed MUST be provided to the server selection +mechanism as a member of the deprioritized server address list only if the error is labelled with +`SystemOverloadedError`. All other retryable errors MUST NOT cause the server address to be added to the deprioritized +server address list. If the driver cannot select a server for a retry attempt or the newly selected server does not support retryable reads, retrying is not possible and drivers MUST raise the previous retryable error. In both cases, the caller is able to infer @@ -295,7 +300,11 @@ function executeRetryableRead(command, session) { } else { // If a previous attempt was made, deprioritize the previous server address // where the command failed. - deprioritizedServers.push(previousServer.address); + // Sharded clusters deprioritize on all retryable errors. + // Other topologies only deprioritize on overload errors. + if previousServer.isSharded || previousError.hasLabel("SystemOverloadedError") { + deprioritizedServers.push(previousServer.address); + } server = selectServer(deprioritizedServers); } } catch (ServerSelectionException exception) { diff --git a/source/retryable-reads/tests/README.md b/source/retryable-reads/tests/README.md index 1ae4dedc9f..5ea90ec624 100644 --- a/source/retryable-reads/tests/README.md +++ b/source/retryable-reads/tests/README.md @@ -123,6 +123,70 @@ This test MUST be executed against a sharded cluster that supports `retryReads=t 7. Disable the fail point on `s0`. +### 3. Retrying Reads in a Replica Set + +These tests will be used to ensure drivers properly retry reads against a replica set. + +#### 3.1 Retryable Reads Caused by Overload Errors Are Retried on a Different Replicaset Server When One is Available + +This test MUST be executed against a replica set that has at least one secondary, supports `retryReads=true`, and has +enabled the `configureFailPoint` command (MongoDB 4.2+). + +1. Create a client `client` with `retryReads=true`, `readPreference=primaryPreferred`, and command event monitoring + enabled. + +2. Configure the following fail point for `client`: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["find"], + errorLabels: ["RetryableError", "SystemOverloadedError"] + errorCode: 6 + } + } + ``` + +3. Reset the command event monitor to clear the failpoint command from its stored events. + +4. Execute a `find` command with `client`. + +5. Assert that one failed command event and one successful command event occurred. + +6. Assert that both events occurred on different servers. + +#### 3.2 Retryable Reads Caused by Non-Overload Errors Are Retried on the Same Replicaset Server + +This test MUST be executed against a replica set that has at least one secondary, supports `retryReads=true`, and has +enabled the `configureFailPoint` command (MongoDB 4.2+). + +1. Create a client `client` with `retryReads=true`, `readPreference=primaryPreferred`, and command event monitoring + enabled. + +2. Configure the following fail point for `client`: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["find"], + errorLabels: ["RetryableError"] + errorCode: 6 + } + } + ``` + +3. Reset the command event monitor to clear the failpoint command from its stored events. + +4. Execute a `find` command with `client`. + +5. Assert that one failed command event and one successful command event occurred. + +6. Assert that both events occurred on the same server. + ## Changelog - 2024-04-30: Migrated from reStructuredText to Markdown. diff --git a/source/retryable-writes/retryable-writes.md b/source/retryable-writes/retryable-writes.md index 80d52721fb..454da8f3b6 100644 --- a/source/retryable-writes/retryable-writes.md +++ b/source/retryable-writes/retryable-writes.md @@ -317,8 +317,11 @@ Drivers MUST then retry the operation as many times as necessary until any one o - CSOT is not enabled and one retry was attempted. -For each retry attempt, drivers MUST select a writable server. The server address on which the operation failed MUST be -provided to the server selection mechanism as a member of the deprioritized server address list. +For each retry attempt, drivers MUST select a writable server. For sharded clusters, the server address on which the +operation failed MUST be provided to the server selection mechanism as a member of the deprioritized server address +list. For all other topologies, the server address on which the operation failed MUST be provided to the server +selection mechanism as a member of the deprioritized server address list only if the error is labelled with +`SystemOverloadedError`. If the driver cannot select a server for a retry attempt or the selected server does not support retryable writes, retrying is not possible and drivers MUST raise the retryable error from the previous attempt. In both cases, the caller @@ -436,8 +439,12 @@ function executeRetryableWrite(command, session) { * If we cannot select a writable server, do not proceed with retrying and * throw the previous error. The caller can then infer that an attempt was * made and failed. */ + // Sharded clusters deprioritize on all retryable errors. + // Other topologies only deprioritize on overload errors. try { - deprioritizedServers.push(server.address); + if server.isSharded || previousError.hasLabel("SystemOverloadedError") { + deprioritizedServers.push(server.address); + } server = selectServer("writable", deprioritizedServers); } catch (Exception ignoredError) { throw previousError; From 5b17b22964d6329cfbde145d59b83ab0fe1ac282 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 19 Feb 2026 17:06:24 -0500 Subject: [PATCH 2/4] Update changelogs --- source/retryable-reads/retryable-reads.md | 2 ++ source/retryable-reads/tests/README.md | 2 ++ source/retryable-writes/retryable-writes.md | 2 ++ 3 files changed, 6 insertions(+) diff --git a/source/retryable-reads/retryable-reads.md b/source/retryable-reads/retryable-reads.md index 3626b379f8..fe174d9cd7 100644 --- a/source/retryable-reads/retryable-reads.md +++ b/source/retryable-reads/retryable-reads.md @@ -557,6 +557,8 @@ any customers experiencing degraded performance can simply disable `retryableRea ## Changelog +- 2026-02-19: Clarified that server deprioritization on replica sets only occurs for `SystemOverloadedError` errors. + - 2025-12-08: Clarified that server deprioritization during retries must use a list of server addresses. - 2024-04-30: Migrated from reStructuredText to Markdown. diff --git a/source/retryable-reads/tests/README.md b/source/retryable-reads/tests/README.md index 5ea90ec624..1206591cde 100644 --- a/source/retryable-reads/tests/README.md +++ b/source/retryable-reads/tests/README.md @@ -189,6 +189,8 @@ enabled the `configureFailPoint` command (MongoDB 4.2+). ## Changelog +- 2026-02-19: Add prose tests for retrying against a replica set. + - 2024-04-30: Migrated from reStructuredText to Markdown. - 2024-03-06: Convert legacy retryable reads tests to unified format. diff --git a/source/retryable-writes/retryable-writes.md b/source/retryable-writes/retryable-writes.md index 454da8f3b6..952ca0e629 100644 --- a/source/retryable-writes/retryable-writes.md +++ b/source/retryable-writes/retryable-writes.md @@ -700,6 +700,8 @@ retryWrites is not true would be inconsistent with the server and potentially co ## Changelog +- 2026-02-19: Clarified that server deprioritization on replica sets only occurs for `SystemOverloadedError` errors. + - 2026-01-14: Clarify which error to return when more than one error with the `NoWritesPerformed` label is encountered. - 2025-12-08: Clarified that server deprioritization during retries must use a list of server addresses. From 8eacf1f8ddd29ea7e12849cd3f0915a9d3624878 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Fri, 20 Feb 2026 10:30:49 -0500 Subject: [PATCH 3/4] Require MongoDB 4.4+ --- source/retryable-reads/tests/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/retryable-reads/tests/README.md b/source/retryable-reads/tests/README.md index 1206591cde..694eaae0ad 100644 --- a/source/retryable-reads/tests/README.md +++ b/source/retryable-reads/tests/README.md @@ -129,8 +129,8 @@ These tests will be used to ensure drivers properly retry reads against a replic #### 3.1 Retryable Reads Caused by Overload Errors Are Retried on a Different Replicaset Server When One is Available -This test MUST be executed against a replica set that has at least one secondary, supports `retryReads=true`, and has -enabled the `configureFailPoint` command (MongoDB 4.2+). +This test MUST be executed against a MongoDB 4.4+ replica set that has at least one secondary, supports +`retryReads=true`, and has enabled the `configureFailPoint` command with the `errorLabels` option. 1. Create a client `client` with `retryReads=true`, `readPreference=primaryPreferred`, and command event monitoring enabled. @@ -159,8 +159,8 @@ enabled the `configureFailPoint` command (MongoDB 4.2+). #### 3.2 Retryable Reads Caused by Non-Overload Errors Are Retried on the Same Replicaset Server -This test MUST be executed against a replica set that has at least one secondary, supports `retryReads=true`, and has -enabled the `configureFailPoint` command (MongoDB 4.2+). +This test MUST be executed against a MongoDB 4.4+ replica set that has at least one secondary, supports +`retryReads=true`, and has enabled the `configureFailPoint` command with the `errorLabels` option. 1. Create a client `client` with `retryReads=true`, `readPreference=primaryPreferred`, and command event monitoring enabled. From 677a94a20a881f40d5e56c2480f1eece83d13228 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Mon, 23 Feb 2026 08:57:50 -0500 Subject: [PATCH 4/4] Address SH review --- source/retryable-reads/retryable-reads.md | 3 ++- source/retryable-writes/retryable-writes.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/source/retryable-reads/retryable-reads.md b/source/retryable-reads/retryable-reads.md index fe174d9cd7..5831a11bf8 100644 --- a/source/retryable-reads/retryable-reads.md +++ b/source/retryable-reads/retryable-reads.md @@ -213,7 +213,8 @@ mechanism as a member of the deprioritized server address list. For all other topologies, the server address on which the operation failed MUST be provided to the server selection mechanism as a member of the deprioritized server address list only if the error is labelled with `SystemOverloadedError`. All other retryable errors MUST NOT cause the server address to be added to the deprioritized -server address list. +server address list. This requirement preserves the existing behavior of retryable reads for non-overload errors and +avoids unintended consequences for operations utilizing primaryPreferred and secondaryPreferred read preferences. If the driver cannot select a server for a retry attempt or the newly selected server does not support retryable reads, retrying is not possible and drivers MUST raise the previous retryable error. In both cases, the caller is able to infer diff --git a/source/retryable-writes/retryable-writes.md b/source/retryable-writes/retryable-writes.md index 952ca0e629..f7744f770e 100644 --- a/source/retryable-writes/retryable-writes.md +++ b/source/retryable-writes/retryable-writes.md @@ -321,7 +321,7 @@ For each retry attempt, drivers MUST select a writable server. For sharded clust operation failed MUST be provided to the server selection mechanism as a member of the deprioritized server address list. For all other topologies, the server address on which the operation failed MUST be provided to the server selection mechanism as a member of the deprioritized server address list only if the error is labelled with -`SystemOverloadedError`. +`SystemOverloadedError`. This requirement preserves the existing behavior of retryable writes for non-overload errors. If the driver cannot select a server for a retry attempt or the selected server does not support retryable writes, retrying is not possible and drivers MUST raise the retryable error from the previous attempt. In both cases, the caller