From f282babff02f07d77a18944349ac9ca16a8d079d Mon Sep 17 00:00:00 2001 From: Bernie Hackett Date: Thu, 7 May 2015 14:50:38 -0700 Subject: [PATCH] PYTHON-926 - ReadPreference.NEAREST shouldn't pick arbiters. --- pymongo/server_selectors.py | 22 +++++++++++++++++++++- test/__init__.py | 9 +++++++-- test/test_read_preferences.py | 7 ++++--- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/pymongo/server_selectors.py b/pymongo/server_selectors.py index 920eee84e..767999737 100644 --- a/pymongo/server_selectors.py +++ b/pymongo/server_selectors.py @@ -21,6 +21,10 @@ def any_server_selector(server_descriptions): return server_descriptions +def readable_server_selector(server_descriptions): + return [s for s in server_descriptions if s.is_readable] + + def writable_server_selector(server_descriptions): return [s for s in server_descriptions if s.is_writable] @@ -49,6 +53,11 @@ def single_tag_set_server_selector(tag_set, server_descriptions): A server tagged {'a': '1', 'b': '2'} matches the tag set {'a': '1'}. The empty tag set {} matches any server. + + The `server_descriptions` passed to this function should have + non-readable servers (e.g. RSGhost, RSArbiter, Unknown) filtered + out (e.g. by readable_server_selector or secondary_server_selector) + first. """ def tags_match(server_tags): for key, value in tag_set.items(): @@ -68,6 +77,11 @@ def tag_sets_server_selector(tag_sets, server_descriptions): [{'a': 'value'}, {}] expresses a preference for servers tagged {'a': 'value'}, but accepts any server if none matches the first preference. + + The `server_descriptions` passed to this function should have + non-readable servers (e.g. RSGhost, RSArbiter, Unknown) filtered + out (e.g. by readable_server_selector or secondary_server_selector) + first. """ for tag_set in tag_sets: selected = single_tag_set_server_selector(tag_set, server_descriptions) @@ -81,6 +95,11 @@ def apply_local_threshold(latency_ms, server_descriptions): """All servers with round trip times within latency_ms of the fastest one. No ServerDescription's round_trip_time can be None. + + The `server_descriptions` passed to this function should have + non-readable servers (e.g. RSGhost, RSArbiter, Unknown) filtered + out (e.g. by readable_server_selector or secondary_server_selector) + first. """ if not server_descriptions: # Avoid ValueError from min() with empty sequence. @@ -104,4 +123,5 @@ def secondary_with_tags_server_selector(tag_sets, server_descriptions): def member_with_tags_server_selector(tag_sets, server_descriptions): """All near-enough members matching the tag sets.""" - return tag_sets_server_selector(tag_sets, server_descriptions) + return tag_sets_server_selector( + tag_sets, readable_server_selector(server_descriptions)) diff --git a/test/__init__.py b/test/__init__.py index 34bb6cf2f..62e6bdf4d 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -127,8 +127,13 @@ class ClientContext(object): self.rs_client = pymongo.MongoClient( pair, replicaSet=self.replica_set_name) - self.nodes = set([partition_node(node) - for node in self.ismaster.get('hosts', [])]) + nodes = [partition_node(node) + for node in self.ismaster.get('hosts', [])] + nodes.extend([partition_node(node) + for node in self.ismaster.get('passives', [])]) + nodes.extend([partition_node(node) + for node in self.ismaster.get('arbiters', [])]) + self.nodes = set(nodes) self.rs_or_standalone_client = self.rs_client or self.client diff --git a/test/test_read_preferences.py b/test/test_read_preferences.py index fd54242bc..22a903495 100644 --- a/test/test_read_preferences.py +++ b/test/test_read_preferences.py @@ -31,7 +31,7 @@ from pymongo.read_preferences import (ReadPreference, MovingAverage, Primary, PrimaryPreferred, Secondary, SecondaryPreferred, Nearest, _ServerMode) -from pymongo.server_selectors import any_server_selector +from pymongo.server_selectors import readable_server_selector from pymongo.server_type import SERVER_TYPE from pymongo.write_concern import WriteConcern @@ -97,7 +97,7 @@ class TestReadPreferencesBase(TestReplicaSetClientBase): def assertReadsFrom(self, expected, **kwargs): c = rs_client(**kwargs) wait_until( - lambda: len(c.nodes) == self.w, + lambda: len(c.nodes - c.arbiters) == self.w, "discovered all nodes") used = self.read_from_which_kind(c) @@ -249,7 +249,8 @@ class TestReadPreferences(TestReadPreferencesBase): latencies = ', '.join( '%s: %dms' % (server.description.address, server.description.round_trip_time) - for server in c._get_topology().select_servers(any_server_selector)) + for server in c._get_topology().select_servers( + readable_server_selector)) self.assertFalse( not_used,