@@ -85,6 +85,7 @@ def __init__(
8585 self ._server_descriptions = server_descriptions
8686 self ._max_set_version = max_set_version
8787 self ._max_election_id = max_election_id
88+ self ._candidate_servers = list (self ._server_descriptions .values ())
8889
8990 # The heartbeat_frequency is used in staleness estimates.
9091 self ._topology_settings = topology_settings
@@ -248,6 +249,11 @@ def readable_servers(self) -> list[ServerDescription]:
248249 """List of readable Servers."""
249250 return [s for s in self ._server_descriptions .values () if s .is_readable ]
250251
252+ @property
253+ def candidate_servers (self ) -> list [ServerDescription ]:
254+ """List of Servers excluding deprioritized servers."""
255+ return self ._candidate_servers
256+
251257 @property
252258 def common_wire_version (self ) -> Optional [int ]:
253259 """Minimum of all servers' max wire versions, or None."""
@@ -283,11 +289,27 @@ def _apply_local_threshold(self, selection: Optional[Selection]) -> list[ServerD
283289 if (cast (float , s .round_trip_time ) - fastest ) <= threshold
284290 ]
285291
292+ def _filter_servers (
293+ self , deprioritized_servers : Optional [list [ServerDescription ]] = None
294+ ) -> None :
295+ """Filter out deprioritized servers from a list of server candidates."""
296+ if not deprioritized_servers :
297+ self ._candidate_servers = self .known_servers
298+ else :
299+ deprioritized_addresses = {sd .address for sd in deprioritized_servers }
300+ filtered = [
301+ server
302+ for server in self .known_servers
303+ if server .address not in deprioritized_addresses
304+ ]
305+ self ._candidate_servers = filtered or self .known_servers
306+
286307 def apply_selector (
287308 self ,
288309 selector : Any ,
289310 address : Optional [_Address ] = None ,
290311 custom_selector : Optional [_ServerSelector ] = None ,
312+ deprioritized_servers : Optional [list [ServerDescription ]] = None ,
291313 ) -> list [ServerDescription ]:
292314 """List of servers matching the provided selector(s).
293315
@@ -324,21 +346,35 @@ def apply_selector(
324346 description = self .server_descriptions ().get (address )
325347 return [description ] if description and description .is_server_type_known else []
326348
349+ self ._filter_servers (deprioritized_servers )
327350 # Primary selection fast path.
328351 if self .topology_type == TOPOLOGY_TYPE .ReplicaSetWithPrimary and type (selector ) is Primary :
329- for sd in self ._server_descriptions . values () :
352+ for sd in self ._candidate_servers :
330353 if sd .server_type == SERVER_TYPE .RSPrimary :
331354 sds = [sd ]
332355 if custom_selector :
333356 sds = custom_selector (sds )
334357 return sds
358+ # All primaries are deprioritized
359+ if deprioritized_servers :
360+ for sd in deprioritized_servers :
361+ if sd .server_type == SERVER_TYPE .RSPrimary :
362+ sds = [sd ]
363+ if custom_selector :
364+ sds = custom_selector (sds )
365+ return sds
335366 # No primary found, return an empty list.
336367 return []
337368
338369 selection = Selection .from_topology_description (self )
339370 # Ignore read preference for sharded clusters.
340371 if self .topology_type != TOPOLOGY_TYPE .Sharded :
341372 selection = selector (selection )
373+ # No suitable servers found, apply preference again but include deprioritized servers.
374+ if not selection and deprioritized_servers :
375+ self ._filter_servers (None )
376+ selection = Selection .from_topology_description (self )
377+ selection = selector (selection )
342378
343379 # Apply custom selector followed by localThresholdMS.
344380 if custom_selector is not None and selection :
0 commit comments