From d4f558acbbc2c7f89738a2fc053438240c2a122b Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Mon, 8 Dec 2025 09:29:05 -0500 Subject: [PATCH 01/11] PYTHON-5662 - Add support for server selection's deprioritized servers to all topologies --- pymongo/asynchronous/mongo_client.py | 3 +- pymongo/asynchronous/topology.py | 23 ++++- pymongo/server_selectors.py | 6 +- pymongo/synchronous/mongo_client.py | 3 +- pymongo/synchronous/topology.py | 23 ++++- pymongo/topology_description.py | 22 ++++- test/asynchronous/test_retryable_reads.py | 71 ++++++++++++++++ test/asynchronous/utils_selection_tests.py | 39 +++++++-- .../read/DeprioritizedNearest.json | 63 ++++++++++++++ .../read/DeprioritizedPrimary.json | 40 +++++++++ .../read/DeprioritizedPrimaryPreferred.json | 63 ++++++++++++++ .../read/DeprioritizedSecondary.json | 63 ++++++++++++++ .../read/DeprioritizedSecondaryPreferred.json | 63 ++++++++++++++ .../read/DeprioritizedNearest.json | 78 +++++++++++++++++ .../read/DeprioritizedPrimary.json | 49 +++++++++++ .../read/DeprioritizedPrimaryPreferred.json | 84 +++++++++++++++++++ .../read/DeprioritizedSecondary.json | 62 ++++++++++++++ .../read/DeprioritizedSecondaryPreferred.json | 78 +++++++++++++++++ .../DeprioritizedSecondaryPreferred.json | 54 ++++++++++++ .../Sharded/read/DeprioritizedNearest.json | 47 +++++++++++ .../Sharded/read/DeprioritizedPrimary.json | 43 ++++++++++ .../read/DeprioritizedPrimaryPreferred.json | 48 +++++++++++ .../Sharded/read/DeprioritizedSecondary.json | 48 +++++++++++ .../read/DeprioritizedSecondaryPreferred.json | 48 +++++++++++ .../Sharded/write/DeprioritizedNearest.json | 47 +++++++++++ .../Sharded/write/DeprioritizedPrimary.json | 43 ++++++++++ .../write/DeprioritizedPrimaryPreferred.json | 48 +++++++++++ .../Sharded/write/DeprioritizedSecondary.json | 48 +++++++++++ .../DeprioritizedSecondaryPreferred.json | 48 +++++++++++ .../Single/read/Deprioritized.json | 54 ++++++++++++ .../Single/write/Deprioritized.json | 54 ++++++++++++ test/test_retryable_reads.py | 69 +++++++++++++++ test/utils_selection_tests.py | 43 ++++++++-- 33 files changed, 1547 insertions(+), 28 deletions(-) create mode 100644 test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearest.json create mode 100644 test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimary.json create mode 100644 test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferred.json create mode 100644 test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondary.json create mode 100644 test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferred.json create mode 100644 test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearest.json create mode 100644 test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimary.json create mode 100644 test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferred.json create mode 100644 test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json create mode 100644 test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferred.json create mode 100644 test/server_selection/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json create mode 100644 test/server_selection/server_selection/Sharded/read/DeprioritizedNearest.json create mode 100644 test/server_selection/server_selection/Sharded/read/DeprioritizedPrimary.json create mode 100644 test/server_selection/server_selection/Sharded/read/DeprioritizedPrimaryPreferred.json create mode 100644 test/server_selection/server_selection/Sharded/read/DeprioritizedSecondary.json create mode 100644 test/server_selection/server_selection/Sharded/read/DeprioritizedSecondaryPreferred.json create mode 100644 test/server_selection/server_selection/Sharded/write/DeprioritizedNearest.json create mode 100644 test/server_selection/server_selection/Sharded/write/DeprioritizedPrimary.json create mode 100644 test/server_selection/server_selection/Sharded/write/DeprioritizedPrimaryPreferred.json create mode 100644 test/server_selection/server_selection/Sharded/write/DeprioritizedSecondary.json create mode 100644 test/server_selection/server_selection/Sharded/write/DeprioritizedSecondaryPreferred.json create mode 100644 test/server_selection/server_selection/Single/read/Deprioritized.json create mode 100644 test/server_selection/server_selection/Single/write/Deprioritized.json diff --git a/pymongo/asynchronous/mongo_client.py b/pymongo/asynchronous/mongo_client.py index 2a8ff43392..76550b8684 100644 --- a/pymongo/asynchronous/mongo_client.py +++ b/pymongo/asynchronous/mongo_client.py @@ -2825,8 +2825,7 @@ async def run(self) -> T: if self._last_error is None: self._last_error = exc - if self._client.topology_description.topology_type == TOPOLOGY_TYPE.Sharded: - self._deprioritized_servers.append(self._server) + self._deprioritized_servers.append(self._server) def _is_not_eligible_for_retry(self) -> bool: """Checks if the exchange is not eligible for retry""" diff --git a/pymongo/asynchronous/topology.py b/pymongo/asynchronous/topology.py index 283aabc690..716d3d56c1 100644 --- a/pymongo/asynchronous/topology.py +++ b/pymongo/asynchronous/topology.py @@ -265,6 +265,7 @@ async def select_servers( server_selection_timeout: Optional[float] = None, address: Optional[_Address] = None, operation_id: Optional[int] = None, + deprioritized_servers: Optional[list[Server]] = None, ) -> list[Server]: """Return a list of Servers matching selector, or time out. @@ -292,7 +293,12 @@ async def select_servers( async with self._lock: server_descriptions = await self._select_servers_loop( - selector, server_timeout, operation, operation_id, address + selector, + server_timeout, + operation, + operation_id, + address, + deprioritized_servers=deprioritized_servers, ) return [ @@ -306,6 +312,7 @@ async def _select_servers_loop( operation: str, operation_id: Optional[int], address: Optional[_Address], + deprioritized_servers: Optional[list[Server]] = None, ) -> list[ServerDescription]: """select_servers() guts. Hold the lock when calling this.""" now = time.monotonic() @@ -324,7 +331,12 @@ async def _select_servers_loop( ) server_descriptions = self._description.apply_selector( - selector, address, custom_selector=self._settings.server_selector + selector, + address, + custom_selector=self._settings.server_selector, + deprioritized_servers=[server.description for server in deprioritized_servers] + if deprioritized_servers + else None, ) while not server_descriptions: @@ -385,7 +397,12 @@ async def _select_server( operation_id: Optional[int] = None, ) -> Server: servers = await self.select_servers( - selector, operation, server_selection_timeout, address, operation_id + selector, + operation, + server_selection_timeout, + address, + operation_id, + deprioritized_servers, ) servers = _filter_servers(servers, deprioritized_servers) if len(servers) == 1: diff --git a/pymongo/server_selectors.py b/pymongo/server_selectors.py index 0d1425ab31..ee116b84ed 100644 --- a/pymongo/server_selectors.py +++ b/pymongo/server_selectors.py @@ -34,16 +34,16 @@ class Selection: @classmethod def from_topology_description(cls, topology_description: TopologyDescription) -> Selection: - known_servers = topology_description.known_servers + candidate_servers = topology_description.candidate_servers primary = None - for sd in known_servers: + for sd in candidate_servers: if sd.server_type == SERVER_TYPE.RSPrimary: primary = sd break return Selection( topology_description, - topology_description.known_servers, + topology_description.candidate_servers, topology_description.common_wire_version, primary, ) diff --git a/pymongo/synchronous/mongo_client.py b/pymongo/synchronous/mongo_client.py index fea2d6daef..60bda31834 100644 --- a/pymongo/synchronous/mongo_client.py +++ b/pymongo/synchronous/mongo_client.py @@ -2815,8 +2815,7 @@ def run(self) -> T: if self._last_error is None: self._last_error = exc - if self._client.topology_description.topology_type == TOPOLOGY_TYPE.Sharded: - self._deprioritized_servers.append(self._server) + self._deprioritized_servers.append(self._server) def _is_not_eligible_for_retry(self) -> bool: """Checks if the exchange is not eligible for retry""" diff --git a/pymongo/synchronous/topology.py b/pymongo/synchronous/topology.py index a4ca0e6e0f..42b62fb4df 100644 --- a/pymongo/synchronous/topology.py +++ b/pymongo/synchronous/topology.py @@ -265,6 +265,7 @@ def select_servers( server_selection_timeout: Optional[float] = None, address: Optional[_Address] = None, operation_id: Optional[int] = None, + deprioritized_servers: Optional[list[Server]] = None, ) -> list[Server]: """Return a list of Servers matching selector, or time out. @@ -292,7 +293,12 @@ def select_servers( with self._lock: server_descriptions = self._select_servers_loop( - selector, server_timeout, operation, operation_id, address + selector, + server_timeout, + operation, + operation_id, + address, + deprioritized_servers=deprioritized_servers, ) return [ @@ -306,6 +312,7 @@ def _select_servers_loop( operation: str, operation_id: Optional[int], address: Optional[_Address], + deprioritized_servers: Optional[list[Server]] = None, ) -> list[ServerDescription]: """select_servers() guts. Hold the lock when calling this.""" now = time.monotonic() @@ -324,7 +331,12 @@ def _select_servers_loop( ) server_descriptions = self._description.apply_selector( - selector, address, custom_selector=self._settings.server_selector + selector, + address, + custom_selector=self._settings.server_selector, + deprioritized_servers=[server.description for server in deprioritized_servers] + if deprioritized_servers + else None, ) while not server_descriptions: @@ -385,7 +397,12 @@ def _select_server( operation_id: Optional[int] = None, ) -> Server: servers = self.select_servers( - selector, operation, server_selection_timeout, address, operation_id + selector, + operation, + server_selection_timeout, + address, + operation_id, + deprioritized_servers, ) servers = _filter_servers(servers, deprioritized_servers) if len(servers) == 1: diff --git a/pymongo/topology_description.py b/pymongo/topology_description.py index a315c1b885..5c400ff0b1 100644 --- a/pymongo/topology_description.py +++ b/pymongo/topology_description.py @@ -85,6 +85,7 @@ def __init__( self._server_descriptions = server_descriptions self._max_set_version = max_set_version self._max_election_id = max_election_id + self._candidate_servers = list(self._server_descriptions.values()) # The heartbeat_frequency is used in staleness estimates. self._topology_settings = topology_settings @@ -248,6 +249,11 @@ def readable_servers(self) -> list[ServerDescription]: """List of readable Servers.""" return [s for s in self._server_descriptions.values() if s.is_readable] + @property + def candidate_servers(self) -> list[ServerDescription]: + """List of Servers excluding deprioritized servers.""" + return self._candidate_servers + @property def common_wire_version(self) -> Optional[int]: """Minimum of all servers' max wire versions, or None.""" @@ -283,11 +289,24 @@ def _apply_local_threshold(self, selection: Optional[Selection]) -> list[ServerD if (cast(float, s.round_trip_time) - fastest) <= threshold ] + def _filter_servers( + self, deprioritized_servers: Optional[list[ServerDescription]] = None + ) -> None: + """Filter out deprioritized servers from a list of server candidates.""" + if not deprioritized_servers: + self._candidate_servers = self.known_servers + else: + filtered = [ + server for server in self.known_servers if server not in deprioritized_servers + ] + self._candidate_servers = filtered or self.known_servers + def apply_selector( self, selector: Any, address: Optional[_Address] = None, custom_selector: Optional[_ServerSelector] = None, + deprioritized_servers: Optional[list[ServerDescription]] = None, ) -> list[ServerDescription]: """List of servers matching the provided selector(s). @@ -324,9 +343,10 @@ def apply_selector( description = self.server_descriptions().get(address) return [description] if description and description.is_server_type_known else [] + self._filter_servers(deprioritized_servers) # Primary selection fast path. if self.topology_type == TOPOLOGY_TYPE.ReplicaSetWithPrimary and type(selector) is Primary: - for sd in self._server_descriptions.values(): + for sd in self._candidate_servers: if sd.server_type == SERVER_TYPE.RSPrimary: sds = [sd] if custom_selector: diff --git a/test/asynchronous/test_retryable_reads.py b/test/asynchronous/test_retryable_reads.py index 47ac91b0f5..b8f42f3834 100644 --- a/test/asynchronous/test_retryable_reads.py +++ b/test/asynchronous/test_retryable_reads.py @@ -21,6 +21,7 @@ import threading from test.asynchronous.utils import async_set_fail_point +from pymongo import ReadPreference from pymongo.errors import OperationFailure sys.path[0:0] = [""] @@ -182,6 +183,44 @@ async def test_retryable_reads_are_retried_on_a_different_mongos_when_one_is_ava # Assert that both events occurred on different mongos. assert listener.failed_events[0].connection_id != listener.failed_events[1].connection_id + @async_client_context.require_replica_set + @async_client_context.require_failCommand_fail_point + async def test_retryable_reads_are_retried_on_a_different_replica_when_one_is_available(self): + fail_command = { + "configureFailPoint": "failCommand", + "mode": {"times": 1}, + "data": {"failCommands": ["find"], "errorCode": 6}, + } + + replica_clients = [] + + for node in async_client_context.nodes: + client = await self.async_rs_or_single_client(*node, directConnection=True) + await async_set_fail_point(client, fail_command) + replica_clients.append(client) + + listener = OvertCommandListener() + client = await self.async_rs_or_single_client( + event_listeners=[listener], + retryReads=True, + directConnection=False, + readPreference="secondaryPreferred", + ) + + with self.assertRaises(OperationFailure): + await client.t.t.find_one({}) + + # Disable failpoints on each node + for client in replica_clients: + fail_command["mode"] = "off" + await async_set_fail_point(client, fail_command) + + self.assertEqual(len(listener.failed_events), 2) + self.assertEqual(len(listener.succeeded_events), 0) + + # Assert that both events occurred on different nodes. + assert listener.failed_events[0].connection_id != listener.failed_events[1].connection_id + @async_client_context.require_multiple_mongoses @async_client_context.require_failCommand_fail_point async def test_retryable_reads_are_retried_on_the_same_mongos_when_no_others_are_available( @@ -218,6 +257,38 @@ async def test_retryable_reads_are_retried_on_the_same_mongos_when_no_others_are # Assert that both events occurred on the same mongos. assert listener.succeeded_events[0].connection_id == listener.failed_events[0].connection_id + @async_client_context.require_replica_set + @async_client_context.require_failCommand_fail_point + async def test_retryable_reads_are_retried_on_the_same_replica_when_no_others_are_available( + self + ): + fail_command = { + "configureFailPoint": "failCommand", + "mode": {"times": 1}, + "data": {"failCommands": ["find"], "errorCode": 6}, + } + + node_client = await self.async_rs_or_single_client(*list(async_client_context.nodes)[0]) + await async_set_fail_point(node_client, fail_command) + + listener = OvertCommandListener() + client = await self.async_rs_or_single_client( + event_listeners=[listener], + retryReads=True, + ) + + await client.t.t.find_one({}) + + # Disable failpoints + fail_command["mode"] = "off" + await async_set_fail_point(node_client, fail_command) + + self.assertEqual(len(listener.failed_events), 1) + self.assertEqual(len(listener.succeeded_events), 1) + + # Assert that both events occurred on the same node. + assert listener.succeeded_events[0].connection_id == listener.failed_events[0].connection_id + @async_client_context.require_failCommand_fail_point async def test_retryable_reads_are_retried_on_the_same_implicit_session(self): listener = OvertCommandListener() diff --git a/test/asynchronous/utils_selection_tests.py b/test/asynchronous/utils_selection_tests.py index d6b92fadb4..9d4e6a3894 100644 --- a/test/asynchronous/utils_selection_tests.py +++ b/test/asynchronous/utils_selection_tests.py @@ -35,7 +35,7 @@ from bson import json_util from pymongo.asynchronous.settings import TopologySettings from pymongo.asynchronous.topology import Topology -from pymongo.common import HEARTBEAT_FREQUENCY +from pymongo.common import HEARTBEAT_FREQUENCY, clean_node from pymongo.errors import AutoReconnect, ConfigurationError from pymongo.operations import _Op from pymongo.server_selectors import writable_server_selector @@ -95,12 +95,21 @@ async def run_scenario(self): # "Eligible servers" is defined in the server selection spec as # the set of servers matching both the ReadPreference's mode # and tag sets. - top_latency = await create_topology(scenario_def) + top_suitable = await create_topology(scenario_def, local_threshold_ms=1000000) # "In latency window" is defined in the server selection # spec as the subset of suitable_servers that falls within the # allowable latency window. - top_suitable = await create_topology(scenario_def, local_threshold_ms=1000000) + top_latency = await create_topology(scenario_def) + + top_suitable_deprioritized_servers = [ + top_suitable.get_server_by_address(clean_node(server["address"])) + for server in scenario_def.get("deprioritized_servers", []) + ] + top_latency_deprioritized_servers = [ + top_latency.get_server_by_address(clean_node(server["address"])) + for server in scenario_def.get("deprioritized_servers", []) + ] # Create server selector. if scenario_def.get("operation") == "write": @@ -120,21 +129,37 @@ async def run_scenario(self): # Select servers. if not scenario_def.get("suitable_servers"): with self.assertRaises(AutoReconnect): - await top_suitable.select_server(pref, _Op.TEST, server_selection_timeout=0) + await top_suitable.select_server( + pref, + _Op.TEST, + server_selection_timeout=0, + deprioritized_servers=top_suitable_deprioritized_servers, + ) return if not scenario_def["in_latency_window"]: with self.assertRaises(AutoReconnect): - await top_latency.select_server(pref, _Op.TEST, server_selection_timeout=0) + await top_latency.select_server( + pref, + _Op.TEST, + server_selection_timeout=0, + deprioritized_servers=top_latency_deprioritized_servers, + ) return actual_suitable_s = await top_suitable.select_servers( - pref, _Op.TEST, server_selection_timeout=0 + pref, + _Op.TEST, + server_selection_timeout=0, + deprioritized_servers=top_suitable_deprioritized_servers, ) actual_latency_s = await top_latency.select_servers( - pref, _Op.TEST, server_selection_timeout=0 + pref, + _Op.TEST, + server_selection_timeout=0, + deprioritized_servers=top_latency_deprioritized_servers, ) expected_suitable_servers = {} diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearest.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearest.json new file mode 100644 index 0000000000..464e542c54 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearest.json @@ -0,0 +1,63 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} + diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimary.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimary.json new file mode 100644 index 0000000000..4726045de8 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimary.json @@ -0,0 +1,40 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Primary" + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [], + "in_latency_window": [] +} + diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferred.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferred.json new file mode 100644 index 0000000000..ef9444e595 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferred.json @@ -0,0 +1,63 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} + diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondary.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondary.json new file mode 100644 index 0000000000..fbd5456ad6 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondary.json @@ -0,0 +1,63 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} + diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferred.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferred.json new file mode 100644 index 0000000000..d8d92261e1 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferred.json @@ -0,0 +1,63 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} + diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearest.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearest.json new file mode 100644 index 0000000000..5b9d0fa538 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearest.json @@ -0,0 +1,78 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimary.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimary.json new file mode 100644 index 0000000000..5e679d51b0 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimary.json @@ -0,0 +1,49 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Primary" + }, + "deprioritized_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + ], + "in_latency_window": [ + ] +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferred.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferred.json new file mode 100644 index 0000000000..367d19e66d --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferred.json @@ -0,0 +1,84 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + {} + ] + }, + "deprioritized_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json new file mode 100644 index 0000000000..675c9539fe --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json @@ -0,0 +1,62 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + ], + "in_latency_window": [ + ] +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferred.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferred.json new file mode 100644 index 0000000000..1fd6c654f0 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferred.json @@ -0,0 +1,78 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json new file mode 100644 index 0000000000..aead0a689e --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json @@ -0,0 +1,54 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + ], + "in_latency_window": [ + ] +} diff --git a/test/server_selection/server_selection/Sharded/read/DeprioritizedNearest.json b/test/server_selection/server_selection/Sharded/read/DeprioritizedNearest.json new file mode 100644 index 0000000000..1aa730cd21 --- /dev/null +++ b/test/server_selection/server_selection/Sharded/read/DeprioritizedNearest.json @@ -0,0 +1,47 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 10, + "type": "Mongos" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 10, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 10, + "type": "Mongos" + } + ] +} diff --git a/test/server_selection/server_selection/Sharded/read/DeprioritizedPrimary.json b/test/server_selection/server_selection/Sharded/read/DeprioritizedPrimary.json new file mode 100644 index 0000000000..0017e28a46 --- /dev/null +++ b/test/server_selection/server_selection/Sharded/read/DeprioritizedPrimary.json @@ -0,0 +1,43 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Primary" + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} + diff --git a/test/server_selection/server_selection/Sharded/read/DeprioritizedPrimaryPreferred.json b/test/server_selection/server_selection/Sharded/read/DeprioritizedPrimaryPreferred.json new file mode 100644 index 0000000000..7540492b4d --- /dev/null +++ b/test/server_selection/server_selection/Sharded/read/DeprioritizedPrimaryPreferred.json @@ -0,0 +1,48 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} + diff --git a/test/server_selection/server_selection/Sharded/read/DeprioritizedSecondary.json b/test/server_selection/server_selection/Sharded/read/DeprioritizedSecondary.json new file mode 100644 index 0000000000..a476695d06 --- /dev/null +++ b/test/server_selection/server_selection/Sharded/read/DeprioritizedSecondary.json @@ -0,0 +1,48 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} + diff --git a/test/server_selection/server_selection/Sharded/read/DeprioritizedSecondaryPreferred.json b/test/server_selection/server_selection/Sharded/read/DeprioritizedSecondaryPreferred.json new file mode 100644 index 0000000000..effca47d2d --- /dev/null +++ b/test/server_selection/server_selection/Sharded/read/DeprioritizedSecondaryPreferred.json @@ -0,0 +1,48 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} + diff --git a/test/server_selection/server_selection/Sharded/write/DeprioritizedNearest.json b/test/server_selection/server_selection/Sharded/write/DeprioritizedNearest.json new file mode 100644 index 0000000000..5cc2cc5477 --- /dev/null +++ b/test/server_selection/server_selection/Sharded/write/DeprioritizedNearest.json @@ -0,0 +1,47 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 10, + "type": "Mongos" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 10, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 10, + "type": "Mongos" + } + ] +} diff --git a/test/server_selection/server_selection/Sharded/write/DeprioritizedPrimary.json b/test/server_selection/server_selection/Sharded/write/DeprioritizedPrimary.json new file mode 100644 index 0000000000..faae97e51f --- /dev/null +++ b/test/server_selection/server_selection/Sharded/write/DeprioritizedPrimary.json @@ -0,0 +1,43 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "Primary" + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} + diff --git a/test/server_selection/server_selection/Sharded/write/DeprioritizedPrimaryPreferred.json b/test/server_selection/server_selection/Sharded/write/DeprioritizedPrimaryPreferred.json new file mode 100644 index 0000000000..3754630246 --- /dev/null +++ b/test/server_selection/server_selection/Sharded/write/DeprioritizedPrimaryPreferred.json @@ -0,0 +1,48 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} + diff --git a/test/server_selection/server_selection/Sharded/write/DeprioritizedSecondary.json b/test/server_selection/server_selection/Sharded/write/DeprioritizedSecondary.json new file mode 100644 index 0000000000..905c8df90a --- /dev/null +++ b/test/server_selection/server_selection/Sharded/write/DeprioritizedSecondary.json @@ -0,0 +1,48 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} + diff --git a/test/server_selection/server_selection/Sharded/write/DeprioritizedSecondaryPreferred.json b/test/server_selection/server_selection/Sharded/write/DeprioritizedSecondaryPreferred.json new file mode 100644 index 0000000000..47e55ca20d --- /dev/null +++ b/test/server_selection/server_selection/Sharded/write/DeprioritizedSecondaryPreferred.json @@ -0,0 +1,48 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} + diff --git a/test/server_selection/server_selection/Single/read/Deprioritized.json b/test/server_selection/server_selection/Single/read/Deprioritized.json new file mode 100644 index 0000000000..c76fc6c771 --- /dev/null +++ b/test/server_selection/server_selection/Single/read/Deprioritized.json @@ -0,0 +1,54 @@ +{ + "topology_description": { + "type": "Single", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ], + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ] +} diff --git a/test/server_selection/server_selection/Single/write/Deprioritized.json b/test/server_selection/server_selection/Single/write/Deprioritized.json new file mode 100644 index 0000000000..099076f2da --- /dev/null +++ b/test/server_selection/server_selection/Single/write/Deprioritized.json @@ -0,0 +1,54 @@ +{ + "topology_description": { + "type": "Single", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ], + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ] +} diff --git a/test/test_retryable_reads.py b/test/test_retryable_reads.py index c9f72ae547..32d2a2fa68 100644 --- a/test/test_retryable_reads.py +++ b/test/test_retryable_reads.py @@ -21,6 +21,7 @@ import threading from test.utils import set_fail_point +from pymongo import ReadPreference from pymongo.errors import OperationFailure sys.path[0:0] = [""] @@ -182,6 +183,44 @@ def test_retryable_reads_are_retried_on_a_different_mongos_when_one_is_available # Assert that both events occurred on different mongos. assert listener.failed_events[0].connection_id != listener.failed_events[1].connection_id + @client_context.require_replica_set + @client_context.require_failCommand_fail_point + def test_retryable_reads_are_retried_on_a_different_replica_when_one_is_available(self): + fail_command = { + "configureFailPoint": "failCommand", + "mode": {"times": 1}, + "data": {"failCommands": ["find"], "errorCode": 6}, + } + + replica_clients = [] + + for node in client_context.nodes: + client = self.rs_or_single_client(*node, directConnection=True) + set_fail_point(client, fail_command) + replica_clients.append(client) + + listener = OvertCommandListener() + client = self.rs_or_single_client( + event_listeners=[listener], + retryReads=True, + directConnection=False, + readPreference="secondaryPreferred", + ) + + with self.assertRaises(OperationFailure): + client.t.t.find_one({}) + + # Disable failpoints on each node + for client in replica_clients: + fail_command["mode"] = "off" + set_fail_point(client, fail_command) + + self.assertEqual(len(listener.failed_events), 2) + self.assertEqual(len(listener.succeeded_events), 0) + + # Assert that both events occurred on different nodes. + assert listener.failed_events[0].connection_id != listener.failed_events[1].connection_id + @client_context.require_multiple_mongoses @client_context.require_failCommand_fail_point def test_retryable_reads_are_retried_on_the_same_mongos_when_no_others_are_available(self): @@ -216,6 +255,36 @@ def test_retryable_reads_are_retried_on_the_same_mongos_when_no_others_are_avail # Assert that both events occurred on the same mongos. assert listener.succeeded_events[0].connection_id == listener.failed_events[0].connection_id + @client_context.require_replica_set + @client_context.require_failCommand_fail_point + def test_retryable_reads_are_retried_on_the_same_replica_when_no_others_are_available(self): + fail_command = { + "configureFailPoint": "failCommand", + "mode": {"times": 1}, + "data": {"failCommands": ["find"], "errorCode": 6}, + } + + node_client = self.rs_or_single_client(*list(client_context.nodes)[0]) + set_fail_point(node_client, fail_command) + + listener = OvertCommandListener() + client = self.rs_or_single_client( + event_listeners=[listener], + retryReads=True, + ) + + client.t.t.find_one({}) + + # Disable failpoints + fail_command["mode"] = "off" + set_fail_point(node_client, fail_command) + + self.assertEqual(len(listener.failed_events), 1) + self.assertEqual(len(listener.succeeded_events), 1) + + # Assert that both events occurred on the same node. + assert listener.succeeded_events[0].connection_id == listener.failed_events[0].connection_id + @client_context.require_failCommand_fail_point def test_retryable_reads_are_retried_on_the_same_implicit_session(self): listener = OvertCommandListener() diff --git a/test/utils_selection_tests.py b/test/utils_selection_tests.py index 2772f06070..2798e49eb0 100644 --- a/test/utils_selection_tests.py +++ b/test/utils_selection_tests.py @@ -33,7 +33,7 @@ from test.utils_shared import parse_read_preference from bson import json_util -from pymongo.common import HEARTBEAT_FREQUENCY +from pymongo.common import HEARTBEAT_FREQUENCY, clean_node from pymongo.errors import AutoReconnect, ConfigurationError from pymongo.operations import _Op from pymongo.server_selectors import writable_server_selector @@ -95,12 +95,21 @@ def run_scenario(self): # "Eligible servers" is defined in the server selection spec as # the set of servers matching both the ReadPreference's mode # and tag sets. - top_latency = create_topology(scenario_def) + top_suitable = create_topology(scenario_def, local_threshold_ms=1000000) # "In latency window" is defined in the server selection # spec as the subset of suitable_servers that falls within the # allowable latency window. - top_suitable = create_topology(scenario_def, local_threshold_ms=1000000) + top_latency = create_topology(scenario_def) + + top_suitable_deprioritized_servers = [ + top_suitable.get_server_by_address(clean_node(server["address"])) + for server in scenario_def.get("deprioritized_servers", []) + ] + top_latency_deprioritized_servers = [ + top_latency.get_server_by_address(clean_node(server["address"])) + for server in scenario_def.get("deprioritized_servers", []) + ] # Create server selector. if scenario_def.get("operation") == "write": @@ -120,18 +129,38 @@ def run_scenario(self): # Select servers. if not scenario_def.get("suitable_servers"): with self.assertRaises(AutoReconnect): - top_suitable.select_server(pref, _Op.TEST, server_selection_timeout=0) + top_suitable.select_server( + pref, + _Op.TEST, + server_selection_timeout=0, + deprioritized_servers=top_suitable_deprioritized_servers, + ) return if not scenario_def["in_latency_window"]: with self.assertRaises(AutoReconnect): - top_latency.select_server(pref, _Op.TEST, server_selection_timeout=0) + top_latency.select_server( + pref, + _Op.TEST, + server_selection_timeout=0, + deprioritized_servers=top_latency_deprioritized_servers, + ) return - actual_suitable_s = top_suitable.select_servers(pref, _Op.TEST, server_selection_timeout=0) - actual_latency_s = top_latency.select_servers(pref, _Op.TEST, server_selection_timeout=0) + actual_suitable_s = top_suitable.select_servers( + pref, + _Op.TEST, + server_selection_timeout=0, + deprioritized_servers=top_suitable_deprioritized_servers, + ) + actual_latency_s = top_latency.select_servers( + pref, + _Op.TEST, + server_selection_timeout=0, + deprioritized_servers=top_latency_deprioritized_servers, + ) expected_suitable_servers = {} for server in scenario_def["suitable_servers"]: From 948410afa95814ae7162dade83ddc4700f1b72b3 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Mon, 8 Dec 2025 14:32:06 -0500 Subject: [PATCH 02/11] Remove unneeded _filter_servers and test --- pymongo/asynchronous/topology.py | 14 -------------- pymongo/synchronous/topology.py | 14 -------------- test/test_topology.py | 17 ----------------- 3 files changed, 45 deletions(-) diff --git a/pymongo/asynchronous/topology.py b/pymongo/asynchronous/topology.py index 716d3d56c1..fef237f99a 100644 --- a/pymongo/asynchronous/topology.py +++ b/pymongo/asynchronous/topology.py @@ -404,7 +404,6 @@ async def _select_server( operation_id, deprioritized_servers, ) - servers = _filter_servers(servers, deprioritized_servers) if len(servers) == 1: return servers[0] server1, server2 = random.sample(servers, 2) @@ -1129,16 +1128,3 @@ def _is_stale_server_description(current_sd: ServerDescription, new_sd: ServerDe if current_tv["processId"] != new_tv["processId"]: return False return current_tv["counter"] > new_tv["counter"] - - -def _filter_servers( - candidates: list[Server], deprioritized_servers: Optional[list[Server]] = None -) -> list[Server]: - """Filter out deprioritized servers from a list of server candidates.""" - if not deprioritized_servers: - return candidates - - filtered = [server for server in candidates if server not in deprioritized_servers] - - # If not possible to pick a prioritized server, return the original list - return filtered or candidates diff --git a/pymongo/synchronous/topology.py b/pymongo/synchronous/topology.py index 42b62fb4df..62c9da1c82 100644 --- a/pymongo/synchronous/topology.py +++ b/pymongo/synchronous/topology.py @@ -404,7 +404,6 @@ def _select_server( operation_id, deprioritized_servers, ) - servers = _filter_servers(servers, deprioritized_servers) if len(servers) == 1: return servers[0] server1, server2 = random.sample(servers, 2) @@ -1127,16 +1126,3 @@ def _is_stale_server_description(current_sd: ServerDescription, new_sd: ServerDe if current_tv["processId"] != new_tv["processId"]: return False return current_tv["counter"] > new_tv["counter"] - - -def _filter_servers( - candidates: list[Server], deprioritized_servers: Optional[list[Server]] = None -) -> list[Server]: - """Filter out deprioritized servers from a list of server candidates.""" - if not deprioritized_servers: - return candidates - - filtered = [server for server in candidates if server not in deprioritized_servers] - - # If not possible to pick a prioritized server, return the original list - return filtered or candidates diff --git a/test/test_topology.py b/test/test_topology.py index d3bbcd9060..2ac00d31d3 100644 --- a/test/test_topology.py +++ b/test/test_topology.py @@ -733,23 +733,6 @@ def test_unexpected_load_balancer(self): self.assertNotIn(("a", 27017), t.description.server_descriptions()) self.assertEqual(t.description.topology_type_name, "Unknown") - def test_filtered_server_selection(self): - s1 = Server(ServerDescription(("localhost", 27017)), pool=object(), monitor=object()) # type: ignore[arg-type] - s2 = Server(ServerDescription(("localhost2", 27017)), pool=object(), monitor=object()) # type: ignore[arg-type] - servers = [s1, s2] - - result = _filter_servers(servers, deprioritized_servers=[s2]) - self.assertEqual(result, [s1]) - - result = _filter_servers(servers, deprioritized_servers=[s1, s2]) - self.assertEqual(result, servers) - - result = _filter_servers(servers, deprioritized_servers=[]) - self.assertEqual(result, servers) - - result = _filter_servers(servers) - self.assertEqual(result, servers) - def wait_for_primary(topology): """Wait for a Topology to discover a writable server. From e0465670ca8427ba59108470ba20941f146be8be Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Mon, 8 Dec 2025 14:45:12 -0500 Subject: [PATCH 03/11] Fix testing imports --- test/test_topology.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_topology.py b/test/test_topology.py index 2ac00d31d3..f2e7a91cda 100644 --- a/test/test_topology.py +++ b/test/test_topology.py @@ -38,7 +38,7 @@ from pymongo.synchronous.pool import PoolOptions from pymongo.synchronous.server import Server from pymongo.synchronous.settings import TopologySettings -from pymongo.synchronous.topology import Topology, _ErrorContext, _filter_servers +from pymongo.synchronous.topology import Topology, _ErrorContext from pymongo.topology_description import TOPOLOGY_TYPE From 2d3a0e335357ab90ede28d7a616f4c40cbc0eeea Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Mon, 8 Dec 2025 15:23:40 -0500 Subject: [PATCH 04/11] Fix --- pymongo/asynchronous/mongo_client.py | 4 +++- pymongo/synchronous/mongo_client.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pymongo/asynchronous/mongo_client.py b/pymongo/asynchronous/mongo_client.py index 76550b8684..537968db59 100644 --- a/pymongo/asynchronous/mongo_client.py +++ b/pymongo/asynchronous/mongo_client.py @@ -2867,7 +2867,9 @@ async def _get_server(self) -> Server: self._session, self._operation, address=self._address, - deprioritized_servers=self._deprioritized_servers, + deprioritized_servers=self._deprioritized_servers + if self._deprioritized_servers + else None, operation_id=self._operation_id, ) diff --git a/pymongo/synchronous/mongo_client.py b/pymongo/synchronous/mongo_client.py index 60bda31834..5b83342214 100644 --- a/pymongo/synchronous/mongo_client.py +++ b/pymongo/synchronous/mongo_client.py @@ -2857,7 +2857,9 @@ def _get_server(self) -> Server: self._session, self._operation, address=self._address, - deprioritized_servers=self._deprioritized_servers, + deprioritized_servers=self._deprioritized_servers + if self._deprioritized_servers + else None, operation_id=self._operation_id, ) From 7a7acca3bb640511c284f7d777d91033a8d14537 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Mon, 8 Dec 2025 16:08:11 -0500 Subject: [PATCH 05/11] Don't add None to deprioritized servers --- pymongo/asynchronous/mongo_client.py | 7 +++---- pymongo/synchronous/mongo_client.py | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/pymongo/asynchronous/mongo_client.py b/pymongo/asynchronous/mongo_client.py index 537968db59..bc51b7d8cc 100644 --- a/pymongo/asynchronous/mongo_client.py +++ b/pymongo/asynchronous/mongo_client.py @@ -2825,7 +2825,8 @@ async def run(self) -> T: if self._last_error is None: self._last_error = exc - self._deprioritized_servers.append(self._server) + if self._server is not None: + self._deprioritized_servers.append(self._server) def _is_not_eligible_for_retry(self) -> bool: """Checks if the exchange is not eligible for retry""" @@ -2867,9 +2868,7 @@ async def _get_server(self) -> Server: self._session, self._operation, address=self._address, - deprioritized_servers=self._deprioritized_servers - if self._deprioritized_servers - else None, + deprioritized_servers=self._deprioritized_servers, operation_id=self._operation_id, ) diff --git a/pymongo/synchronous/mongo_client.py b/pymongo/synchronous/mongo_client.py index 5b83342214..139fe9c50d 100644 --- a/pymongo/synchronous/mongo_client.py +++ b/pymongo/synchronous/mongo_client.py @@ -2815,7 +2815,8 @@ def run(self) -> T: if self._last_error is None: self._last_error = exc - self._deprioritized_servers.append(self._server) + if self._server is not None: + self._deprioritized_servers.append(self._server) def _is_not_eligible_for_retry(self) -> bool: """Checks if the exchange is not eligible for retry""" @@ -2857,9 +2858,7 @@ def _get_server(self) -> Server: self._session, self._operation, address=self._address, - deprioritized_servers=self._deprioritized_servers - if self._deprioritized_servers - else None, + deprioritized_servers=self._deprioritized_servers, operation_id=self._operation_id, ) From fb7fef190f62797a55752f13423b694c19560c5e Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Tue, 9 Dec 2025 13:19:30 -0500 Subject: [PATCH 06/11] Sync tests --- .../DeprioritizedAllPrimaryPreferred.json | 84 +++++++++++++++ .../DeprioritizedAllSecondaryPreferred.json | 100 ++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllPrimaryPreferred.json create mode 100644 test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllSecondaryPreferred.json diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllPrimaryPreferred.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllPrimaryPreferred.json new file mode 100644 index 0000000000..095537ba1a --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllPrimaryPreferred.json @@ -0,0 +1,84 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + {} + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllSecondaryPreferred.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllSecondaryPreferred.json new file mode 100644 index 0000000000..b29ba2bc80 --- /dev/null +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllSecondaryPreferred.json @@ -0,0 +1,100 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + {} + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} From 322940a1e4e895445481b35c5db4d96940c42cff Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Tue, 9 Dec 2025 16:19:31 -0500 Subject: [PATCH 07/11] Sync to spec --- pymongo/topology_description.py | 13 ++++++++++ .../read/DeprioritizedPrimary.json | 16 +++++++++++++ .../read/DeprioritizedSecondary.json | 24 +++++++++++++++++++ .../DeprioritizedSecondaryPreferred.json | 16 +++++++++++++ 4 files changed, 69 insertions(+) diff --git a/pymongo/topology_description.py b/pymongo/topology_description.py index 5c400ff0b1..96dfef7fac 100644 --- a/pymongo/topology_description.py +++ b/pymongo/topology_description.py @@ -352,6 +352,14 @@ def apply_selector( if custom_selector: sds = custom_selector(sds) return sds + # All primaries are deprioritized + if deprioritized_servers: + for sd in deprioritized_servers: + if sd.server_type == SERVER_TYPE.RSPrimary: + sds = [sd] + if custom_selector: + sds = custom_selector(sds) + return sds # No primary found, return an empty list. return [] @@ -359,6 +367,11 @@ def apply_selector( # Ignore read preference for sharded clusters. if self.topology_type != TOPOLOGY_TYPE.Sharded: selection = selector(selection) + # No suitable servers found, apply preference again but include deprioritized servers. + if not selection and deprioritized_servers: + self._filter_servers(None) + selection = Selection.from_topology_description(self) + selection = selector(selection) # Apply custom selector followed by localThresholdMS. if custom_selector is not None and selection: diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimary.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimary.json index 5e679d51b0..a1ef5b200f 100644 --- a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimary.json +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimary.json @@ -43,7 +43,23 @@ } ], "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } ], "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } ] } diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json index 675c9539fe..32080807a1 100644 --- a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json @@ -56,7 +56,31 @@ } ], "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } ], "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } ] } diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json index aead0a689e..0f3642aa20 100644 --- a/test/server_selection/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json @@ -48,7 +48,23 @@ } ], "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } ], "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } ] } From f8cced2513d8d9c5e1a11b52ebdd1083d2a9faea Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Wed, 10 Dec 2025 10:59:06 -0500 Subject: [PATCH 08/11] Use addresses for checking deprioritized servers --- pymongo/topology_description.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pymongo/topology_description.py b/pymongo/topology_description.py index 96dfef7fac..074bbc7fca 100644 --- a/pymongo/topology_description.py +++ b/pymongo/topology_description.py @@ -296,8 +296,11 @@ def _filter_servers( if not deprioritized_servers: self._candidate_servers = self.known_servers else: + deprioritized_addresses = {sd.address for sd in deprioritized_servers} filtered = [ - server for server in self.known_servers if server not in deprioritized_servers + server + for server in self.known_servers + if server.address not in deprioritized_addresses ] self._candidate_servers = filtered or self.known_servers From 1d92805723f2f1a647a76ede3b2c40c59157880a Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 11 Dec 2025 11:11:37 -0500 Subject: [PATCH 09/11] Remove unneeded prose tests --- test/asynchronous/test_retryable_reads.py | 70 ----------------------- test/test_retryable_reads.py | 68 ---------------------- 2 files changed, 138 deletions(-) diff --git a/test/asynchronous/test_retryable_reads.py b/test/asynchronous/test_retryable_reads.py index b8f42f3834..39b29f44ba 100644 --- a/test/asynchronous/test_retryable_reads.py +++ b/test/asynchronous/test_retryable_reads.py @@ -183,44 +183,6 @@ async def test_retryable_reads_are_retried_on_a_different_mongos_when_one_is_ava # Assert that both events occurred on different mongos. assert listener.failed_events[0].connection_id != listener.failed_events[1].connection_id - @async_client_context.require_replica_set - @async_client_context.require_failCommand_fail_point - async def test_retryable_reads_are_retried_on_a_different_replica_when_one_is_available(self): - fail_command = { - "configureFailPoint": "failCommand", - "mode": {"times": 1}, - "data": {"failCommands": ["find"], "errorCode": 6}, - } - - replica_clients = [] - - for node in async_client_context.nodes: - client = await self.async_rs_or_single_client(*node, directConnection=True) - await async_set_fail_point(client, fail_command) - replica_clients.append(client) - - listener = OvertCommandListener() - client = await self.async_rs_or_single_client( - event_listeners=[listener], - retryReads=True, - directConnection=False, - readPreference="secondaryPreferred", - ) - - with self.assertRaises(OperationFailure): - await client.t.t.find_one({}) - - # Disable failpoints on each node - for client in replica_clients: - fail_command["mode"] = "off" - await async_set_fail_point(client, fail_command) - - self.assertEqual(len(listener.failed_events), 2) - self.assertEqual(len(listener.succeeded_events), 0) - - # Assert that both events occurred on different nodes. - assert listener.failed_events[0].connection_id != listener.failed_events[1].connection_id - @async_client_context.require_multiple_mongoses @async_client_context.require_failCommand_fail_point async def test_retryable_reads_are_retried_on_the_same_mongos_when_no_others_are_available( @@ -257,38 +219,6 @@ async def test_retryable_reads_are_retried_on_the_same_mongos_when_no_others_are # Assert that both events occurred on the same mongos. assert listener.succeeded_events[0].connection_id == listener.failed_events[0].connection_id - @async_client_context.require_replica_set - @async_client_context.require_failCommand_fail_point - async def test_retryable_reads_are_retried_on_the_same_replica_when_no_others_are_available( - self - ): - fail_command = { - "configureFailPoint": "failCommand", - "mode": {"times": 1}, - "data": {"failCommands": ["find"], "errorCode": 6}, - } - - node_client = await self.async_rs_or_single_client(*list(async_client_context.nodes)[0]) - await async_set_fail_point(node_client, fail_command) - - listener = OvertCommandListener() - client = await self.async_rs_or_single_client( - event_listeners=[listener], - retryReads=True, - ) - - await client.t.t.find_one({}) - - # Disable failpoints - fail_command["mode"] = "off" - await async_set_fail_point(node_client, fail_command) - - self.assertEqual(len(listener.failed_events), 1) - self.assertEqual(len(listener.succeeded_events), 1) - - # Assert that both events occurred on the same node. - assert listener.succeeded_events[0].connection_id == listener.failed_events[0].connection_id - @async_client_context.require_failCommand_fail_point async def test_retryable_reads_are_retried_on_the_same_implicit_session(self): listener = OvertCommandListener() diff --git a/test/test_retryable_reads.py b/test/test_retryable_reads.py index 32d2a2fa68..dc73877c1e 100644 --- a/test/test_retryable_reads.py +++ b/test/test_retryable_reads.py @@ -183,44 +183,6 @@ def test_retryable_reads_are_retried_on_a_different_mongos_when_one_is_available # Assert that both events occurred on different mongos. assert listener.failed_events[0].connection_id != listener.failed_events[1].connection_id - @client_context.require_replica_set - @client_context.require_failCommand_fail_point - def test_retryable_reads_are_retried_on_a_different_replica_when_one_is_available(self): - fail_command = { - "configureFailPoint": "failCommand", - "mode": {"times": 1}, - "data": {"failCommands": ["find"], "errorCode": 6}, - } - - replica_clients = [] - - for node in client_context.nodes: - client = self.rs_or_single_client(*node, directConnection=True) - set_fail_point(client, fail_command) - replica_clients.append(client) - - listener = OvertCommandListener() - client = self.rs_or_single_client( - event_listeners=[listener], - retryReads=True, - directConnection=False, - readPreference="secondaryPreferred", - ) - - with self.assertRaises(OperationFailure): - client.t.t.find_one({}) - - # Disable failpoints on each node - for client in replica_clients: - fail_command["mode"] = "off" - set_fail_point(client, fail_command) - - self.assertEqual(len(listener.failed_events), 2) - self.assertEqual(len(listener.succeeded_events), 0) - - # Assert that both events occurred on different nodes. - assert listener.failed_events[0].connection_id != listener.failed_events[1].connection_id - @client_context.require_multiple_mongoses @client_context.require_failCommand_fail_point def test_retryable_reads_are_retried_on_the_same_mongos_when_no_others_are_available(self): @@ -255,36 +217,6 @@ def test_retryable_reads_are_retried_on_the_same_mongos_when_no_others_are_avail # Assert that both events occurred on the same mongos. assert listener.succeeded_events[0].connection_id == listener.failed_events[0].connection_id - @client_context.require_replica_set - @client_context.require_failCommand_fail_point - def test_retryable_reads_are_retried_on_the_same_replica_when_no_others_are_available(self): - fail_command = { - "configureFailPoint": "failCommand", - "mode": {"times": 1}, - "data": {"failCommands": ["find"], "errorCode": 6}, - } - - node_client = self.rs_or_single_client(*list(client_context.nodes)[0]) - set_fail_point(node_client, fail_command) - - listener = OvertCommandListener() - client = self.rs_or_single_client( - event_listeners=[listener], - retryReads=True, - ) - - client.t.t.find_one({}) - - # Disable failpoints - fail_command["mode"] = "off" - set_fail_point(node_client, fail_command) - - self.assertEqual(len(listener.failed_events), 1) - self.assertEqual(len(listener.succeeded_events), 1) - - # Assert that both events occurred on the same node. - assert listener.succeeded_events[0].connection_id == listener.failed_events[0].connection_id - @client_context.require_failCommand_fail_point def test_retryable_reads_are_retried_on_the_same_implicit_session(self): listener = OvertCommandListener() From 4a526085dfe434161d26fdfa934a13993fcd9385 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 11 Dec 2025 11:13:15 -0500 Subject: [PATCH 10/11] Remove unneeded import --- test/asynchronous/test_retryable_reads.py | 1 - test/test_retryable_reads.py | 1 - 2 files changed, 2 deletions(-) diff --git a/test/asynchronous/test_retryable_reads.py b/test/asynchronous/test_retryable_reads.py index 39b29f44ba..47ac91b0f5 100644 --- a/test/asynchronous/test_retryable_reads.py +++ b/test/asynchronous/test_retryable_reads.py @@ -21,7 +21,6 @@ import threading from test.asynchronous.utils import async_set_fail_point -from pymongo import ReadPreference from pymongo.errors import OperationFailure sys.path[0:0] = [""] diff --git a/test/test_retryable_reads.py b/test/test_retryable_reads.py index dc73877c1e..c9f72ae547 100644 --- a/test/test_retryable_reads.py +++ b/test/test_retryable_reads.py @@ -21,7 +21,6 @@ import threading from test.utils import set_fail_point -from pymongo import ReadPreference from pymongo.errors import OperationFailure sys.path[0:0] = [""] From c8ac4dce343c9140856447233fef84764f1bda5a Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Thu, 11 Dec 2025 14:00:56 -0500 Subject: [PATCH 11/11] resync --- .../read/DeprioritizedNearest.json | 1 - .../read/DeprioritizedPrimary.json | 1 - .../read/DeprioritizedPrimaryPreferred.json | 1 - .../read/DeprioritizedSecondary.json | 1 - .../read/DeprioritizedSecondaryPreferred.json | 1 - .../read/DeprioritizedAllPrimaryPreferred.json | 2 +- .../read/DeprioritizedAllSecondaryPreferred.json | 2 +- .../read/DeprioritizedPrimaryPreferred.json | 2 +- .../read/DeprioritizedSecondary.json | 2 +- .../write/DeprioritizedSecondaryPreferred.json | 16 ++++++++-------- .../Sharded/read/DeprioritizedPrimary.json | 1 - .../read/DeprioritizedPrimaryPreferred.json | 1 - .../Sharded/read/DeprioritizedSecondary.json | 1 - .../read/DeprioritizedSecondaryPreferred.json | 1 - .../Sharded/write/DeprioritizedPrimary.json | 1 - .../write/DeprioritizedPrimaryPreferred.json | 1 - .../Sharded/write/DeprioritizedSecondary.json | 1 - .../write/DeprioritizedSecondaryPreferred.json | 1 - 18 files changed, 12 insertions(+), 25 deletions(-) diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearest.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearest.json index 464e542c54..f569c39d6c 100644 --- a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearest.json +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearest.json @@ -60,4 +60,3 @@ } ] } - diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimary.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimary.json index 4726045de8..8ec76964e9 100644 --- a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimary.json +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimary.json @@ -37,4 +37,3 @@ "suitable_servers": [], "in_latency_window": [] } - diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferred.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferred.json index ef9444e595..4ab48d0b66 100644 --- a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferred.json +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferred.json @@ -60,4 +60,3 @@ } ] } - diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondary.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondary.json index fbd5456ad6..202a277487 100644 --- a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondary.json +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondary.json @@ -60,4 +60,3 @@ } ] } - diff --git a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferred.json b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferred.json index d8d92261e1..d37e5545da 100644 --- a/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferred.json +++ b/test/server_selection/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferred.json @@ -60,4 +60,3 @@ } ] } - diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllPrimaryPreferred.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllPrimaryPreferred.json index 095537ba1a..9f415203b3 100644 --- a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllPrimaryPreferred.json +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllPrimaryPreferred.json @@ -31,7 +31,7 @@ "operation": "read", "read_preference": { "mode": "PrimaryPreferred", - "tag_sets": [ + "tag_sets": [ {} ] }, diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllSecondaryPreferred.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllSecondaryPreferred.json index b29ba2bc80..f8d321b067 100644 --- a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllSecondaryPreferred.json +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllSecondaryPreferred.json @@ -31,7 +31,7 @@ "operation": "read", "read_preference": { "mode": "SecondaryPreferred", - "tag_sets": [ + "tag_sets": [ {} ] }, diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferred.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferred.json index 367d19e66d..7b6da395c5 100644 --- a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferred.json +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferred.json @@ -31,7 +31,7 @@ "operation": "read", "read_preference": { "mode": "PrimaryPreferred", - "tag_sets": [ + "tag_sets": [ {} ] }, diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json index 32080807a1..f3dc03db09 100644 --- a/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json @@ -37,7 +37,7 @@ } ] }, - "deprioritized_servers": [ + "deprioritized_servers": [ { "address": "b:27017", "avg_rtt_ms": 5, diff --git a/test/server_selection/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json b/test/server_selection/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json index 0f3642aa20..4669ee57d5 100644 --- a/test/server_selection/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json +++ b/test/server_selection/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json @@ -3,25 +3,25 @@ "type": "ReplicaSetWithPrimary", "servers": [ { - "address": "b:27017", - "avg_rtt_ms": 5, - "type": "RSSecondary", + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", "tags": { "data_center": "nyc" } }, { - "address": "c:27017", - "avg_rtt_ms": 100, + "address": "b:27017", + "avg_rtt_ms": 5, "type": "RSSecondary", "tags": { "data_center": "nyc" } }, { - "address": "a:27017", - "avg_rtt_ms": 26, - "type": "RSPrimary", + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", "tags": { "data_center": "nyc" } diff --git a/test/server_selection/server_selection/Sharded/read/DeprioritizedPrimary.json b/test/server_selection/server_selection/Sharded/read/DeprioritizedPrimary.json index 0017e28a46..8629c84a2b 100644 --- a/test/server_selection/server_selection/Sharded/read/DeprioritizedPrimary.json +++ b/test/server_selection/server_selection/Sharded/read/DeprioritizedPrimary.json @@ -40,4 +40,3 @@ } ] } - diff --git a/test/server_selection/server_selection/Sharded/read/DeprioritizedPrimaryPreferred.json b/test/server_selection/server_selection/Sharded/read/DeprioritizedPrimaryPreferred.json index 7540492b4d..800264cfde 100644 --- a/test/server_selection/server_selection/Sharded/read/DeprioritizedPrimaryPreferred.json +++ b/test/server_selection/server_selection/Sharded/read/DeprioritizedPrimaryPreferred.json @@ -45,4 +45,3 @@ } ] } - diff --git a/test/server_selection/server_selection/Sharded/read/DeprioritizedSecondary.json b/test/server_selection/server_selection/Sharded/read/DeprioritizedSecondary.json index a476695d06..42d1e227f1 100644 --- a/test/server_selection/server_selection/Sharded/read/DeprioritizedSecondary.json +++ b/test/server_selection/server_selection/Sharded/read/DeprioritizedSecondary.json @@ -45,4 +45,3 @@ } ] } - diff --git a/test/server_selection/server_selection/Sharded/read/DeprioritizedSecondaryPreferred.json b/test/server_selection/server_selection/Sharded/read/DeprioritizedSecondaryPreferred.json index effca47d2d..eaab0b3af5 100644 --- a/test/server_selection/server_selection/Sharded/read/DeprioritizedSecondaryPreferred.json +++ b/test/server_selection/server_selection/Sharded/read/DeprioritizedSecondaryPreferred.json @@ -45,4 +45,3 @@ } ] } - diff --git a/test/server_selection/server_selection/Sharded/write/DeprioritizedPrimary.json b/test/server_selection/server_selection/Sharded/write/DeprioritizedPrimary.json index faae97e51f..a936658939 100644 --- a/test/server_selection/server_selection/Sharded/write/DeprioritizedPrimary.json +++ b/test/server_selection/server_selection/Sharded/write/DeprioritizedPrimary.json @@ -40,4 +40,3 @@ } ] } - diff --git a/test/server_selection/server_selection/Sharded/write/DeprioritizedPrimaryPreferred.json b/test/server_selection/server_selection/Sharded/write/DeprioritizedPrimaryPreferred.json index 3754630246..5dc3fa292d 100644 --- a/test/server_selection/server_selection/Sharded/write/DeprioritizedPrimaryPreferred.json +++ b/test/server_selection/server_selection/Sharded/write/DeprioritizedPrimaryPreferred.json @@ -45,4 +45,3 @@ } ] } - diff --git a/test/server_selection/server_selection/Sharded/write/DeprioritizedSecondary.json b/test/server_selection/server_selection/Sharded/write/DeprioritizedSecondary.json index 905c8df90a..8b5cc5251c 100644 --- a/test/server_selection/server_selection/Sharded/write/DeprioritizedSecondary.json +++ b/test/server_selection/server_selection/Sharded/write/DeprioritizedSecondary.json @@ -45,4 +45,3 @@ } ] } - diff --git a/test/server_selection/server_selection/Sharded/write/DeprioritizedSecondaryPreferred.json b/test/server_selection/server_selection/Sharded/write/DeprioritizedSecondaryPreferred.json index 47e55ca20d..dcff289cc2 100644 --- a/test/server_selection/server_selection/Sharded/write/DeprioritizedSecondaryPreferred.json +++ b/test/server_selection/server_selection/Sharded/write/DeprioritizedSecondaryPreferred.json @@ -45,4 +45,3 @@ } ] } -