Skip to content

Commit 596f80f

Browse files
aherlihybehackett
authored andcommitted
PYTHON-985 - MongoClient properties now block until connected
1 parent e4d3392 commit 596f80f

File tree

5 files changed

+58
-53
lines changed

5 files changed

+58
-53
lines changed

doc/changelog.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
Changelog
22
=========
33

4+
Changes in Version 3.2
5+
----------------------
6+
7+
Version 3.2 implements the new server features introduced in MongoDB 3.2.
8+
9+
Highlights include:
10+
11+
- Certain MongoClient properties block until a connection is established or
12+
raise ServerSelectionTimeoutError if no server is available. See
13+
:class:`~pymongo.MongoClient` for details.
14+
415
Changes in Version 3.1.1
516
------------------------
617

pymongo/mongo_client.py

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -480,22 +480,21 @@ def _purge_index(self, database_name,
480480
if index_name in self.__index_cache[database_name][collection_name]:
481481
del self.__index_cache[database_name][collection_name][index_name]
482482

483-
def _server_property(self, attr_name, default=None):
483+
def _server_property(self, attr_name):
484484
"""An attribute of the current server's description.
485485
486-
Returns "default" while there is no current server, primary, or mongos.
486+
If the client is not connected, this will block until a connection is
487+
established or raise ServerSelectionTimeoutError if no server is
488+
available.
487489
488490
Not threadsafe if used multiple times in a single method, since
489491
the server may change. In such cases, store a local reference to a
490492
ServerDescription first, then use its properties.
491493
"""
492-
try:
493-
server = self._topology.select_server(
494-
writable_server_selector, server_selection_timeout=0)
494+
server = self._topology.select_server(
495+
writable_server_selector)
495496

496-
return getattr(server.description, attr_name)
497-
except ConnectionFailure:
498-
return default
497+
return getattr(server.description, attr_name)
499498

500499
@property
501500
def event_listeners(self):
@@ -513,15 +512,21 @@ def address(self):
513512
the client is load-balancing among mongoses, since there is no single
514513
address. Use :attr:`nodes` instead.
515514
515+
If the client is not connected, this will block until a connection is
516+
established or raise ServerSelectionTimeoutError if no server is
517+
available.
518+
516519
.. versionadded:: 3.0
517520
"""
518-
try:
519-
return self._topology.get_direct_or_primary()
520-
except InvalidOperation:
521-
# Only one case where Topology throws InvalidOperation.
521+
topology_type = self._topology._description.topology_type
522+
if topology_type == TOPOLOGY_TYPE.Sharded:
522523
raise InvalidOperation(
523524
'Cannot use "address" property when load balancing among'
524525
' mongoses, use "nodes" instead.')
526+
if topology_type not in (TOPOLOGY_TYPE.ReplicaSetWithPrimary,
527+
TOPOLOGY_TYPE.Single):
528+
return None
529+
return self._server_property('address')
525530

526531
@property
527532
def primary(self):
@@ -563,16 +568,20 @@ def arbiters(self):
563568

564569
@property
565570
def is_primary(self):
566-
"""If this client if connected to a server that can accept writes.
571+
"""If this client is connected to a server that can accept writes.
567572
568573
True if the current server is a standalone, mongos, or the primary of
569-
a replica set.
574+
a replica set. If the client is not connected, this will block until a
575+
connection is established or raise ServerSelectionTimeoutError if no
576+
server is available.
570577
"""
571-
return self._server_property('is_writable', False)
578+
return self._server_property('is_writable')
572579

573580
@property
574581
def is_mongos(self):
575-
"""If this client is connected to mongos.
582+
"""If this client is connected to mongos. If the client is not
583+
connected, this will block until a connection is established or raise
584+
ServerSelectionTimeoutError if no server is available..
576585
"""
577586
return self._server_property('server_type') == SERVER_TYPE.Mongos
578587

@@ -605,28 +614,34 @@ def nodes(self):
605614
def max_bson_size(self):
606615
"""The largest BSON object the connected server accepts in bytes.
607616
608-
Defaults to 16MB if not connected to a server.
617+
If the client is not connected, this will block until a connection is
618+
established or raise ServerSelectionTimeoutError if no server is
619+
available.
609620
"""
610-
return self._server_property('max_bson_size', common.MAX_BSON_SIZE)
621+
return self._server_property('max_bson_size')
611622

612623
@property
613624
def max_message_size(self):
614625
"""The largest message the connected server accepts in bytes.
615626
616-
Defaults to 32MB if not connected to a server.
627+
If the client is not connected, this will block until a connection is
628+
established or raise ServerSelectionTimeoutError if no server is
629+
available.
617630
"""
618-
return self._server_property(
619-
'max_message_size', common.MAX_MESSAGE_SIZE)
631+
return self._server_property('max_message_size')
620632

621633
@property
622634
def max_write_batch_size(self):
623635
"""The maxWriteBatchSize reported by the server.
624636
637+
If the client is not connected, this will block until a connection is
638+
established or raise ServerSelectionTimeoutError if no server is
639+
available.
640+
625641
Returns a default value when connected to server versions prior to
626642
MongoDB 2.6.
627643
"""
628-
return self._server_property(
629-
'max_write_batch_size', common.MAX_WRITE_BATCH_SIZE)
644+
return self._server_property('max_write_batch_size')
630645

631646
@property
632647
def local_threshold_ms(self):

pymongo/topology.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -218,22 +218,6 @@ def _get_replica_set_members(self, selector):
218218
descriptions = selector(self._description.known_servers)
219219
return set([d.address for d in descriptions])
220220

221-
def get_direct_or_primary(self):
222-
"""Return the address of a connected primary or standalone, or None.
223-
224-
Raise InvalidOperation for Sharded topologies.
225-
"""
226-
# Implemented here in Topology instead of MongoClient, so it can lock.
227-
with self._lock:
228-
topology_type = self._description.topology_type
229-
if topology_type == TOPOLOGY_TYPE.Sharded:
230-
raise InvalidOperation()
231-
if topology_type not in (TOPOLOGY_TYPE.ReplicaSetWithPrimary,
232-
TOPOLOGY_TYPE.Single):
233-
return None
234-
descriptions = writable_server_selector(
235-
self._description.known_servers)
236-
return descriptions[0].address if descriptions else None
237221

238222
def get_secondaries(self):
239223
"""Return set of secondary addresses."""

test/test_client.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -206,19 +206,23 @@ def test_constants(self):
206206

207207
def test_init_disconnected(self):
208208
c = rs_or_single_client(connect=False)
209-
209+
# is_primary causes client to block until connected
210210
self.assertIsInstance(c.is_primary, bool)
211+
212+
c = rs_or_single_client(connect=False)
211213
self.assertIsInstance(c.is_mongos, bool)
214+
c = rs_or_single_client(connect=False)
212215
self.assertIsInstance(c.max_pool_size, int)
213216
self.assertIsInstance(c.nodes, frozenset)
214217

218+
c = rs_or_single_client(connect=False)
215219
self.assertEqual(c.codec_options, CodecOptions())
216220
self.assertIsInstance(c.max_bson_size, int)
217-
self.assertIsInstance(c.max_write_batch_size, int)
221+
c = rs_or_single_client(connect=False)
218222
self.assertFalse(c.primary)
219223
self.assertFalse(c.secondaries)
220-
221-
c.pymongo_test.command('ismaster') # Auto-connect.
224+
c = rs_or_single_client(connect=False)
225+
self.assertIsInstance(c.max_write_batch_size, int)
222226

223227
if client_context.is_rs:
224228
# The primary's host and port are from the replica set config.
@@ -1058,14 +1062,9 @@ def test(collection):
10581062
lazy_client_trial(reset, find_one, test, self._get_client)
10591063

10601064
def test_max_bson_size(self):
1061-
# Client should have sane defaults before connecting, and should update
1062-
# its configuration once connected.
10631065
c = self._get_client()
1064-
self.assertEqual(16 * (1024 ** 2), c.max_bson_size)
1065-
self.assertEqual(2 * c.max_bson_size, c.max_message_size)
10661066

1067-
# Make the client connect, so that it sets its max_bson_size and
1068-
# max_message_size attributes.
1067+
# max_bson_size will cause the client to connect.
10691068
ismaster = c.db.command('ismaster')
10701069
self.assertEqual(ismaster['maxBsonObjectSize'], c.max_bson_size)
10711070
if 'maxMessageSizeBytes' in ismaster:

test/test_replica_set_client.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -368,10 +368,6 @@ def test_max_write_batch_size(self):
368368
c.set_max_write_batch_size('a:1', 1)
369369
c.set_max_write_batch_size('b:2', 2)
370370

371-
# Starts with default max batch size.
372-
self.assertEqual(1000, c.max_write_batch_size)
373-
c.db.command('ismaster') # Connect.
374-
375371
# Uses primary's max batch size.
376372
self.assertEqual(c.max_write_batch_size, 1)
377373

0 commit comments

Comments
 (0)