Skip to content

Commit 2a05236

Browse files
committed
PYTHON-1332 Only gossip cluster time on >= 3.6.
Do not add readConcern to explain. Test explain with collation.
1 parent 36e585b commit 2a05236

File tree

13 files changed

+114
-110
lines changed

13 files changed

+114
-110
lines changed

pymongo/bulk.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ def _execute_command(self, generator, write_concern, session,
269269
while run.idx_offset < len(run.ops):
270270
if session and retryable:
271271
cmd['txnNumber'] = session._transaction_id()
272-
client._send_cluster_time(cmd, session)
272+
sock_info.send_cluster_time(cmd, session, client)
273273
check_keys = run.op_type == _INSERT
274274
ops = islice(run.ops, run.idx_offset, None)
275275
# Run as many ops as possible.

pymongo/collection.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1945,15 +1945,15 @@ def list_indexes(self, session=None):
19451945
if exc.code != 26:
19461946
raise
19471947
cursor = {'id': 0, 'firstBatch': []}
1948-
return CommandCursor(coll, cursor, sock_info.address, session=s,
1948+
return CommandCursor(coll, cursor, sock_info.address,
1949+
session=s,
19491950
explicit_session=session is not None)
19501951
else:
1951-
namespace = _UJOIN % (self.__database.name, "system.indexes")
19521952
res = message._first_batch(
19531953
sock_info, self.__database.name, "system.indexes",
19541954
{"ns": self.__full_name}, 0, slave_ok, codec_options,
19551955
ReadPreference.PRIMARY, cmd,
1956-
self.database.client._event_listeners, session=None)
1956+
self.database.client._event_listeners)
19571957
cursor = res["cursor"]
19581958
# Note that a collection can only have 64 indexes, so there
19591959
# will never be a getMore call.

pymongo/database.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -739,8 +739,7 @@ def current_op(self, include_all=False, session=None):
739739
return _first_batch(sock_info, "admin", "$cmd.sys.inprog",
740740
spec, -1, True, self.codec_options,
741741
ReadPreference.PRIMARY, cmd,
742-
self.client._event_listeners,
743-
session=None)
742+
self.client._event_listeners)
744743

745744
def profiling_level(self, session=None):
746745
"""Get the database's current profiling level.

pymongo/message.py

Lines changed: 45 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -170,31 +170,8 @@ def _convert_write_result(operation, command, result):
170170
('$snapshot', 'snapshot')])
171171

172172

173-
def _gen_explain_command(
174-
coll, spec, projection, skip, limit, batch_size,
175-
options, read_concern, session, client):
176-
"""Generate an explain command document."""
177-
cmd = _gen_find_command(coll, spec, projection, skip, limit, batch_size,
178-
options, session=None, client=None)
179-
if read_concern.level:
180-
explain = SON([('explain', cmd), ('readConcern', read_concern.document)])
181-
else:
182-
explain = SON([('explain', cmd)])
183-
184-
if session:
185-
explain['lsid'] = session._use_lsid()
186-
if (session.options.causal_consistency
187-
and session.operation_time is not None):
188-
explain.setdefault(
189-
'readConcern', {})['afterClusterTime'] = session.operation_time
190-
191-
client._send_cluster_time(explain, session)
192-
return explain
193-
194-
195173
def _gen_find_command(coll, spec, projection, skip, limit, batch_size, options,
196-
session, client, read_concern=DEFAULT_READ_CONCERN,
197-
collation=None):
174+
read_concern, collation=None):
198175
"""Generate a find command document."""
199176
cmd = SON([('find', coll)])
200177
if '$query' in spec:
@@ -225,29 +202,17 @@ def _gen_find_command(coll, spec, projection, skip, limit, batch_size, options,
225202
cmd.update([(opt, True)
226203
for opt, val in _OPTIONS.items()
227204
if options & val])
228-
if session:
229-
cmd['lsid'] = session._use_lsid()
230-
if (session.options.causal_consistency
231-
and session.operation_time is not None):
232-
cmd.setdefault(
233-
'readConcern', {})['afterClusterTime'] = session.operation_time
234-
if client:
235-
client._send_cluster_time(cmd, session)
236205
return cmd
237206

238207

239-
def _gen_get_more_command(cursor_id, coll, batch_size, max_await_time_ms,
240-
session, client):
208+
def _gen_get_more_command(cursor_id, coll, batch_size, max_await_time_ms):
241209
"""Generate a getMore command document."""
242210
cmd = SON([('getMore', cursor_id),
243211
('collection', coll)])
244212
if batch_size:
245213
cmd['batchSize'] = batch_size
246214
if max_await_time_ms is not None:
247215
cmd['maxTimeMS'] = max_await_time_ms
248-
if session:
249-
cmd['lsid'] = session._use_lsid()
250-
client._send_cluster_time(cmd, session)
251216
return cmd
252217

253218

@@ -299,23 +264,32 @@ def use_command(self, sock_info, exhaust):
299264

300265
return use_find_cmd
301266

302-
def as_command(self):
267+
def as_command(self, sock_info):
303268
"""Return a find command document for this query.
304269
305270
Should be called *after* get_message.
306271
"""
307-
if '$explain' in self.spec:
272+
explain = '$explain' in self.spec
273+
cmd = _gen_find_command(
274+
self.coll, self.spec, self.fields, self.ntoskip,
275+
self.limit, self.batch_size, self.flags, self.read_concern,
276+
self.collation)
277+
if explain:
308278
self.name = 'explain'
309-
return _gen_explain_command(
310-
self.coll, self.spec, self.fields, self.ntoskip,
311-
self.limit, self.batch_size, self.flags,
312-
self.read_concern, self.session, self.client), self.db
313-
return _gen_find_command(self.coll, self.spec, self.fields,
314-
self.ntoskip, self.limit, self.batch_size,
315-
self.flags, self.session, self.client,
316-
self.read_concern, self.collation), self.db
317-
318-
def get_message(self, set_slave_ok, is_mongos, use_cmd=False):
279+
cmd = SON([('explain', cmd)])
280+
session = self.session
281+
if session:
282+
cmd['lsid'] = session._use_lsid()
283+
# Explain does not support readConcern.
284+
if (not explain and session.options.causal_consistency
285+
and session.operation_time is not None):
286+
cmd.setdefault(
287+
'readConcern', {})[
288+
'afterClusterTime'] = session.operation_time
289+
sock_info.send_cluster_time(cmd, session, self.client)
290+
return cmd, self.db
291+
292+
def get_message(self, set_slave_ok, sock_info, use_cmd=False):
319293
"""Get a query message, possibly setting the slaveOk bit."""
320294
if set_slave_ok:
321295
# Set the slaveOk bit.
@@ -328,7 +302,7 @@ def get_message(self, set_slave_ok, is_mongos, use_cmd=False):
328302

329303
if use_cmd:
330304
ns = _UJOIN % (self.db, "$cmd")
331-
spec = self.as_command()[0]
305+
spec = self.as_command(sock_info)[0]
332306
ntoreturn = -1 # All DB commands return 1 document
333307
else:
334308
# OP_QUERY treats ntoreturn of -1 and 1 the same, return
@@ -341,7 +315,7 @@ def get_message(self, set_slave_ok, is_mongos, use_cmd=False):
341315
else:
342316
ntoreturn = self.limit
343317

344-
if is_mongos:
318+
if sock_info.is_mongos:
345319
spec = _maybe_add_read_preference(spec,
346320
self.read_preference)
347321

@@ -372,22 +346,25 @@ def use_command(self, sock_info, exhaust):
372346
sock_info.validate_session(self.client, self.session)
373347
return sock_info.max_wire_version >= 4 and not exhaust
374348

375-
def as_command(self):
349+
def as_command(self, sock_info):
376350
"""Return a getMore command document for this query."""
377-
return _gen_get_more_command(self.cursor_id, self.coll,
378-
self.ntoreturn,
379-
self.max_await_time_ms,
380-
self.session,
381-
self.client), self.db
351+
cmd = _gen_get_more_command(self.cursor_id, self.coll,
352+
self.ntoreturn,
353+
self.max_await_time_ms)
354+
355+
if self.session:
356+
cmd['lsid'] = self.session._use_lsid()
357+
sock_info.send_cluster_time(cmd, self.session, self.client)
358+
return cmd, self.db
382359

383-
def get_message(self, dummy0, dummy1, use_cmd=False):
360+
def get_message(self, dummy0, sock_info, use_cmd=False):
384361
"""Get a getmore message."""
385362

386363
ns = _UJOIN % (self.db, self.coll)
387364

388365
if use_cmd:
389366
ns = _UJOIN % (self.db, "$cmd")
390-
spec = self.as_command()[0]
367+
spec = self.as_command(sock_info)[0]
391368

392369
return query(0, ns, 0, -1, spec, None, self.codec_options)
393370

@@ -401,20 +378,20 @@ def use_command(self, socket_info, exhaust):
401378

402379
return False
403380

404-
def get_message(self, set_slave_ok, is_mongos, use_cmd=False):
381+
def get_message(self, set_slave_ok, sock_info, use_cmd=False):
405382
# Always pass False for use_cmd.
406-
return super(_RawBatchQuery, self).get_message(set_slave_ok, is_mongos,
407-
False)
383+
return super(_RawBatchQuery, self).get_message(
384+
set_slave_ok, sock_info, False)
408385

409386

410387
class _RawBatchGetMore(_GetMore):
411388
def use_command(self, socket_info, exhaust):
412389
return False
413390

414-
def get_message(self, set_slave_ok, is_mongos, use_cmd=False):
391+
def get_message(self, set_slave_ok, sock_info, use_cmd=False):
415392
# Always pass False for use_cmd.
416-
return super(_RawBatchGetMore, self).get_message(set_slave_ok, is_mongos,
417-
False)
393+
return super(_RawBatchGetMore, self).get_message(
394+
set_slave_ok, sock_info, False)
418395

419396

420397
class _CursorAddress(tuple):
@@ -982,21 +959,19 @@ def unpack(cls, msg):
982959

983960

984961
def _first_batch(sock_info, db, coll, query, ntoreturn,
985-
slave_ok, codec_options, read_preference, cmd, listeners,
986-
session):
962+
slave_ok, codec_options, read_preference, cmd, listeners):
987963
"""Simple query helper for retrieving a first (and possibly only) batch."""
988964
query = _Query(
989965
0, db, coll, 0, query, None, codec_options,
990-
read_preference, ntoreturn, 0, DEFAULT_READ_CONCERN, None, session,
966+
read_preference, ntoreturn, 0, DEFAULT_READ_CONCERN, None, None,
991967
None)
992968

993969
name = next(iter(cmd))
994970
publish = listeners.enabled_for_commands
995971
if publish:
996972
start = datetime.datetime.now()
997973

998-
request_id, msg, max_doc_size = query.get_message(slave_ok,
999-
sock_info.is_mongos)
974+
request_id, msg, max_doc_size = query.get_message(slave_ok, sock_info)
1000975

1001976
if publish:
1002977
encoding_duration = datetime.datetime.now() - start

pymongo/mongo_client.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,8 +1679,7 @@ def unlock(self, session=None):
16791679
message._first_batch(sock_info, "admin", "$cmd.sys.unlock",
16801680
{}, -1, True, self.codec_options,
16811681
ReadPreference.PRIMARY, cmd,
1682-
self._event_listeners,
1683-
session=None)
1682+
self._event_listeners)
16841683

16851684
def __enter__(self):
16861685
return self

pymongo/monitor.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,8 @@ def _check_with_retry(self):
120120

121121
start = _time()
122122
try:
123-
cluster_time = self._topology.max_cluster_time()
124123
# If the server type is unknown, send metadata with first check.
125-
return self._check_once(metadata=metadata,
126-
cluster_time=cluster_time)
124+
return self._check_once(metadata=metadata)
127125
except ReferenceError:
128126
raise
129127
except Exception as error:
@@ -142,9 +140,7 @@ def _check_with_retry(self):
142140
# Always send metadata: this is a new connection.
143141
start = _time()
144142
try:
145-
cluster_time = self._topology.max_cluster_time()
146-
return self._check_once(metadata=self._pool.opts.metadata,
147-
cluster_time=cluster_time)
143+
return self._check_once(metadata=self._pool.opts.metadata)
148144
except ReferenceError:
149145
raise
150146
except Exception as error:
@@ -155,7 +151,7 @@ def _check_with_retry(self):
155151
self._avg_round_trip_time.reset()
156152
return default
157153

158-
def _check_once(self, metadata=None, cluster_time=None):
154+
def _check_once(self, metadata=None):
159155
"""A single attempt to call ismaster.
160156
161157
Returns a ServerDescription, or raises an exception.
@@ -165,7 +161,7 @@ def _check_once(self, metadata=None, cluster_time=None):
165161
self._listeners.publish_server_heartbeat_started(address)
166162
with self._pool.get_socket({}) as sock_info:
167163
response, round_trip_time = self._check_with_socket(
168-
sock_info, metadata=metadata, cluster_time=cluster_time)
164+
sock_info, metadata=metadata)
169165
self._avg_round_trip_time.add_sample(round_trip_time)
170166
sd = ServerDescription(
171167
address=address,
@@ -177,16 +173,18 @@ def _check_once(self, metadata=None, cluster_time=None):
177173

178174
return sd
179175

180-
def _check_with_socket(self, sock_info, metadata=None, cluster_time=None):
176+
def _check_with_socket(self, sock_info, metadata=None):
181177
"""Return (IsMaster, round_trip_time).
182178
183179
Can raise ConnectionFailure or OperationFailure.
184180
"""
185181
cmd = SON([('ismaster', 1)])
186182
if metadata is not None:
187183
cmd['client'] = metadata
188-
if cluster_time is not None:
189-
cmd['$clusterTime'] = cluster_time
184+
if self._server_description.max_wire_version >= 6:
185+
cluster_time = self._topology.max_cluster_time()
186+
if cluster_time is not None:
187+
cmd['$clusterTime'] = cluster_time
190188
start = _time()
191189
request_id, msg, max_doc_size = message.query(
192190
0, 'admin.$cmd', 0, -1, cmd,

pymongo/network.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,8 @@
3434
except ImportError:
3535
_SELECT_ERROR = OSError
3636

37-
from bson import SON
3837
from pymongo import helpers, message
39-
from pymongo.common import MAX_MESSAGE_SIZE, ORDERED_TYPES
38+
from pymongo.common import MAX_MESSAGE_SIZE
4039
from pymongo.errors import (AutoReconnect,
4140
NotMasterError,
4241
OperationFailure,
@@ -53,7 +52,7 @@ def command(sock, dbname, spec, slave_ok, is_mongos,
5352
check_keys=False, listeners=None, max_bson_size=None,
5453
read_concern=None,
5554
parse_write_concern_error=False,
56-
collation=None, retryable_write=False):
55+
collation=None):
5756
"""Execute a command over the socket, or raise socket.error.
5857
5958
:Parameters:
@@ -76,20 +75,10 @@ def command(sock, dbname, spec, slave_ok, is_mongos,
7675
- `parse_write_concern_error`: Whether to parse the ``writeConcernError``
7776
field in the command response.
7877
- `collation`: The collation for this command.
79-
- `retryable_write`: True if this command is a retryable write.
8078
"""
8179
name = next(iter(spec))
8280
ns = dbname + '.$cmd'
8381
flags = 4 if slave_ok else 0
84-
if (client or session) and not isinstance(spec, ORDERED_TYPES):
85-
# Ensure command name remains in first place.
86-
spec = SON(spec)
87-
if session:
88-
spec['lsid'] = session._use_lsid()
89-
if retryable_write:
90-
spec['txnNumber'] = session._transaction_id()
91-
if client:
92-
client._send_cluster_time(spec, session)
9382

9483
# Publish the original command document, perhaps with lsid and $clusterTime.
9584
orig = spec

0 commit comments

Comments
 (0)