Skip to content

Commit 8b2eb24

Browse files
authored
PYTHON-2164 Remove client max_bson_size/max_message_size/max_write_batch_size (#766)
Use the hello command instead: doc = client.admin.command('hello') max_bson_size = doc['maxBsonObjectSize'] max_message_size = doc['maxMessageSizeBytes'] max_write_batch_size = doc['maxWriteBatchSize'] Also add documentation for TopologyDescription.apply_selector.
1 parent 695a90e commit 8b2eb24

File tree

9 files changed

+75
-74
lines changed

9 files changed

+75
-74
lines changed

doc/api/pymongo/mongo_client.rst

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@
2626
.. autoattribute:: min_pool_size
2727
.. autoattribute:: max_idle_time_ms
2828
.. autoattribute:: nodes
29-
.. autoattribute:: max_bson_size
30-
.. autoattribute:: max_message_size
31-
.. autoattribute:: max_write_batch_size
3229
.. autoattribute:: local_threshold_ms
3330
.. autoattribute:: server_selection_timeout
3431
.. autoattribute:: codec_options

doc/changelog.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ Breaking Changes in 4.0
3333
:meth:`pymongo.mongo_client.MongoClient.unlock`, and
3434
:attr:`pymongo.mongo_client.MongoClient.is_locked`.
3535
- Removed :meth:`pymongo.mongo_client.MongoClient.database_names`.
36+
- Removed :attr:`pymongo.mongo_client.MongoClient.max_bson_size`.
37+
- Removed :attr:`pymongo.mongo_client.MongoClient.max_message_size`.
38+
- Removed :attr:`pymongo.mongo_client.MongoClient.max_write_batch_size`.
3639
- Removed :meth:`pymongo.database.Database.eval`,
3740
:data:`pymongo.database.Database.system_js` and
3841
:class:`pymongo.database.SystemJS`.

doc/migrate-to-pymongo4.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,29 @@ can be changed to this::
173173

174174
names = client.list_database_names()
175175

176+
MongoClient.max_bson_size/max_message_size/max_write_batch_size are removed
177+
...........................................................................
178+
179+
Removed :attr:`pymongo.mongo_client.MongoClient.max_bson_size`,
180+
:attr:`pymongo.mongo_client.MongoClient.max_message_size`, and
181+
:attr:`pymongo.mongo_client.MongoClient.max_write_batch_size`. These helpers
182+
were incorrect when in ``loadBalanced=true mode`` and ambiguous in clusters
183+
with mixed versions. Use the `hello command`_ to get the authoritative
184+
value from the remote server instead. Code like this::
185+
186+
max_bson_size = client.max_bson_size
187+
max_message_size = client.max_message_size
188+
max_write_batch_size = client.max_write_batch_size
189+
190+
can be changed to this::
191+
192+
doc = client.admin.command('hello')
193+
max_bson_size = doc['maxBsonObjectSize']
194+
max_message_size = doc['maxMessageSizeBytes']
195+
max_write_batch_size = doc['maxWriteBatchSize']
196+
197+
.. _hello command: https://docs.mongodb.com/manual/reference/command/hello/
198+
176199
``tz_aware`` defaults to ``False``
177200
..................................
178201

pymongo/mongo_client.py

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,39 +1053,6 @@ def nodes(self):
10531053
description = self._topology.description
10541054
return frozenset(s.address for s in description.known_servers)
10551055

1056-
@property
1057-
def max_bson_size(self):
1058-
"""The largest BSON object the connected server accepts in bytes.
1059-
1060-
If the client is not connected, this will block until a connection is
1061-
established or raise ServerSelectionTimeoutError if no server is
1062-
available.
1063-
"""
1064-
return self._server_property('max_bson_size')
1065-
1066-
@property
1067-
def max_message_size(self):
1068-
"""The largest message the connected server accepts in bytes.
1069-
1070-
If the client is not connected, this will block until a connection is
1071-
established or raise ServerSelectionTimeoutError if no server is
1072-
available.
1073-
"""
1074-
return self._server_property('max_message_size')
1075-
1076-
@property
1077-
def max_write_batch_size(self):
1078-
"""The maxWriteBatchSize reported by the server.
1079-
1080-
If the client is not connected, this will block until a connection is
1081-
established or raise ServerSelectionTimeoutError if no server is
1082-
available.
1083-
1084-
Returns a default value when connected to server versions prior to
1085-
MongoDB 2.6.
1086-
"""
1087-
return self._server_property('max_write_batch_size')
1088-
10891056
@property
10901057
def local_threshold_ms(self):
10911058
"""The local threshold for this instance."""

pymongo/topology_description.py

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -228,21 +228,32 @@ def heartbeat_frequency(self):
228228
def srv_max_hosts(self):
229229
return self._topology_settings._srv_max_hosts
230230

231-
def apply_selector(self, selector, address, custom_selector=None):
231+
def _apply_local_threshold(self, selection):
232+
if not selection:
233+
return []
234+
# Round trip time in seconds.
235+
fastest = min(
236+
s.round_trip_time for s in selection.server_descriptions)
237+
threshold = self._topology_settings.local_threshold_ms / 1000.0
238+
return [s for s in selection.server_descriptions
239+
if (s.round_trip_time - fastest) <= threshold]
240+
241+
def apply_selector(self, selector, address=None, custom_selector=None):
242+
"""List of servers matching the provided selector(s).
232243
233-
def apply_local_threshold(selection):
234-
if not selection:
235-
return []
236-
237-
settings = self._topology_settings
238-
239-
# Round trip time in seconds.
240-
fastest = min(
241-
s.round_trip_time for s in selection.server_descriptions)
242-
threshold = settings.local_threshold_ms / 1000.0
243-
return [s for s in selection.server_descriptions
244-
if (s.round_trip_time - fastest) <= threshold]
244+
:Parameters:
245+
- `selector`: a callable that takes a Selection as input and returns
246+
a Selection as output. For example, an instance of a read
247+
preference from :mod:`~pymongo.read_preferences`.
248+
- `address` (optional): A server address to select.
249+
- `custom_selector` (optional): A callable that augments server
250+
selection rules. Accepts a list of
251+
:class:`~pymongo.server_description.ServerDescription` objects and
252+
return a list of server descriptions that should be considered
253+
suitable for the desired operation.
245254
255+
.. versionadded:: 3.4
256+
"""
246257
if getattr(selector, 'min_wire_version', 0):
247258
common_wv = self.common_wire_version
248259
if common_wv and common_wv < selector.min_wire_version:
@@ -271,7 +282,7 @@ def apply_local_threshold(selection):
271282
if custom_selector is not None and selection:
272283
selection = selection.with_server_descriptions(
273284
custom_selector(selection.server_descriptions))
274-
return apply_local_threshold(selection)
285+
return self._apply_local_threshold(selection)
275286

276287
def has_readable_server(self, read_preference=ReadPreference.PRIMARY):
277288
"""Does this topology have any readable servers available matching the
@@ -288,7 +299,7 @@ def has_readable_server(self, read_preference=ReadPreference.PRIMARY):
288299
.. versionadded:: 3.4
289300
"""
290301
common.validate_read_preference("read_preference", read_preference)
291-
return any(self.apply_selector(read_preference, None))
302+
return any(self.apply_selector(read_preference))
292303

293304
def has_writable_server(self):
294305
"""Does this topology have a writable server available?

test/__init__.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ def __init__(self):
239239
self.auth_enabled = False
240240
self.test_commands_enabled = False
241241
self.server_parameters = {}
242+
self._hello = None
242243
self.is_mongos = False
243244
self.mongoses = []
244245
self.is_rs = False
@@ -274,7 +275,9 @@ def client_options(self):
274275

275276
@property
276277
def hello(self):
277-
return self.client.admin.command(HelloCompat.LEGACY_CMD)
278+
if not self._hello:
279+
self._hello = self.client.admin.command(HelloCompat.LEGACY_CMD)
280+
return self._hello
278281

279282
def _connect(self, host, port, **kwargs):
280283
# Jython takes a long time to connect.
@@ -391,6 +394,7 @@ def _init_client(self):
391394
**self.default_client_options)
392395

393396
# Get the authoritative hello result from the primary.
397+
self._hello = None
394398
hello = self.hello
395399
nodes = [partition_node(node.lower())
396400
for node in hello.get('hosts', [])]
@@ -866,6 +870,14 @@ def requires_hint_with_min_max_queries(self):
866870
# Changed in SERVER-39567.
867871
return self.version.at_least(4, 1, 10)
868872

873+
@property
874+
def max_bson_size(self):
875+
return self.hello['maxBsonObjectSize']
876+
877+
@property
878+
def max_write_batch_size(self):
879+
return self.hello['maxWriteBatchSize']
880+
869881

870882
# Reusable client context
871883
client_context = ClientContext()

test/test_bulk.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ def test_upsert(self):
283283

284284
def test_numerous_inserts(self):
285285
# Ensure we don't exceed server's maxWriteBatchSize size limit.
286-
n_docs = self.client.max_write_batch_size + 100
286+
n_docs = client_context.max_write_batch_size + 100
287287
requests = [InsertOne({}) for _ in range(n_docs)]
288288
result = self.coll.bulk_write(requests, ordered=False)
289289
self.assertEqual(n_docs, result.inserted_count)
@@ -344,7 +344,7 @@ def test_bulk_write_invalid_arguments(self):
344344
self.coll.bulk_write([{}])
345345

346346
def test_upsert_large(self):
347-
big = 'a' * (client_context.client.max_bson_size - 37)
347+
big = 'a' * (client_context.max_bson_size - 37)
348348
result = self.coll.bulk_write([
349349
UpdateOne({'x': 1}, {'$set': {'s': big}}, upsert=True)])
350350
self.assertEqualResponse(
@@ -566,7 +566,7 @@ def test_multiple_error_unordered_batch(self):
566566
result)
567567

568568
def test_large_inserts_ordered(self):
569-
big = 'x' * self.coll.database.client.max_bson_size
569+
big = 'x' * client_context.max_bson_size
570570
requests = [
571571
InsertOne({'b': 1, 'a': 1}),
572572
InsertOne({'big': big}),
@@ -599,7 +599,7 @@ def test_large_inserts_ordered(self):
599599
self.assertEqual(6, self.coll.count_documents({}))
600600

601601
def test_large_inserts_unordered(self):
602-
big = 'x' * self.coll.database.client.max_bson_size
602+
big = 'x' * client_context.max_bson_size
603603
requests = [
604604
InsertOne({'b': 1, 'a': 1}),
605605
InsertOne({'big': big}),

test/test_client.py

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -640,15 +640,14 @@ def test_init_disconnected(self):
640640

641641
c = rs_or_single_client(connect=False)
642642
self.assertEqual(c.codec_options, CodecOptions())
643-
self.assertIsInstance(c.max_bson_size, int)
644643
c = rs_or_single_client(connect=False)
645644
self.assertFalse(c.primary)
646645
self.assertFalse(c.secondaries)
647646
c = rs_or_single_client(connect=False)
648-
self.assertIsInstance(c.max_write_batch_size, int)
649647
self.assertIsInstance(c.topology_description, TopologyDescription)
650648
self.assertEqual(c.topology_description, c._topology._description)
651-
649+
self.assertIsNone(c.address) # PYTHON-2981
650+
c.admin.command('ping') # connect
652651
if client_context.is_rs:
653652
# The primary's host and port are from the replica set config.
654653
self.assertIsNotNone(c.address)
@@ -1837,17 +1836,6 @@ def test(collection):
18371836

18381837
lazy_client_trial(reset, find_one, test, self._get_client)
18391838

1840-
def test_max_bson_size(self):
1841-
c = self._get_client()
1842-
1843-
# max_bson_size will cause the client to connect.
1844-
hello = c.db.command(HelloCompat.LEGACY_CMD)
1845-
self.assertEqual(hello['maxBsonObjectSize'], c.max_bson_size)
1846-
if 'maxMessageSizeBytes' in hello:
1847-
self.assertEqual(
1848-
hello['maxMessageSizeBytes'],
1849-
c.max_message_size)
1850-
18511839

18521840
class TestMongoClientFailover(MockClientTest):
18531841

test/test_collection.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,7 @@ def test_delete_many(self):
851851
lambda: 0 == db.test.count_documents({}), 'delete 2 documents')
852852

853853
def test_command_document_too_large(self):
854-
large = '*' * (self.client.max_bson_size + _COMMAND_OVERHEAD)
854+
large = '*' * (client_context.max_bson_size + _COMMAND_OVERHEAD)
855855
coll = self.db.test
856856
self.assertRaises(
857857
DocumentTooLarge, coll.insert_one, {'data': large})
@@ -862,7 +862,7 @@ def test_command_document_too_large(self):
862862
DocumentTooLarge, coll.delete_one, {'data': large})
863863

864864
def test_write_large_document(self):
865-
max_size = self.db.client.max_bson_size
865+
max_size = client_context.max_bson_size
866866
half_size = int(max_size / 2)
867867
max_str = "x" * max_size
868868
half_str = "x" * half_size
@@ -1879,7 +1879,7 @@ def test_min_query(self):
18791879
def test_numerous_inserts(self):
18801880
# Ensure we don't exceed server's maxWriteBatchSize size limit.
18811881
self.db.test.drop()
1882-
n_docs = self.client.max_write_batch_size + 100
1882+
n_docs = client_context.max_write_batch_size + 100
18831883
self.db.test.insert_many([{} for _ in range(n_docs)])
18841884
self.assertEqual(n_docs, self.db.test.count_documents({}))
18851885
self.db.test.drop()
@@ -1888,7 +1888,7 @@ def test_insert_many_large_batch(self):
18881888
# Tests legacy insert.
18891889
db = self.client.test_insert_large_batch
18901890
self.addCleanup(self.client.drop_database, 'test_insert_large_batch')
1891-
max_bson_size = self.client.max_bson_size
1891+
max_bson_size = client_context.max_bson_size
18921892
# Write commands are limited to 16MB + 16k per batch
18931893
big_string = 'x' * int(max_bson_size / 2)
18941894

0 commit comments

Comments
 (0)