Skip to content

Commit 99e21c3

Browse files
committed
PYTHON-2777 Raise client side error for snapshot reads on <5.0 (#659)
(cherry picked from commit 4152600)
1 parent ee97eae commit 99e21c3

7 files changed

+213
-19
lines changed

pymongo/bulk.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,8 @@ def _execute_command(self, generator, write_concern, session,
302302
if retryable and not self.started_retryable_write:
303303
session._start_retryable_write()
304304
self.started_retryable_write = True
305-
session._apply_to(cmd, retryable, ReadPreference.PRIMARY)
305+
session._apply_to(cmd, retryable, ReadPreference.PRIMARY,
306+
sock_info)
306307
sock_info.send_cluster_time(cmd, session, client)
307308
ops = islice(run.ops, run.idx_offset, None)
308309
# Run as many ops as possible in one command.

pymongo/client_session.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,7 @@ def _txn_read_preference(self):
866866
return self._transaction.opts.read_preference
867867
return None
868868

869-
def _apply_to(self, command, is_retryable, read_preference):
869+
def _apply_to(self, command, is_retryable, read_preference, sock_info):
870870
self._check_ended()
871871

872872
self._server_session.last_use = monotonic.time()
@@ -891,7 +891,7 @@ def _apply_to(self, command, is_retryable, read_preference):
891891
rc = self._transaction.opts.read_concern.document
892892
if rc:
893893
command['readConcern'] = rc
894-
self._update_read_concern(command)
894+
self._update_read_concern(command, sock_info)
895895

896896
command['txnNumber'] = self._server_session.transaction_id
897897
command['autocommit'] = False
@@ -900,12 +900,15 @@ def _start_retryable_write(self):
900900
self._check_ended()
901901
self._server_session.inc_transaction_id()
902902

903-
def _update_read_concern(self, cmd):
903+
def _update_read_concern(self, cmd, sock_info):
904904
if (self.options.causal_consistency
905905
and self.operation_time is not None):
906906
cmd.setdefault('readConcern', {})[
907907
'afterClusterTime'] = self.operation_time
908908
if self.options.snapshot:
909+
if sock_info.max_wire_version < 13:
910+
raise ConfigurationError(
911+
'Snapshot reads require MongoDB 5.0 or later')
909912
rc = cmd.setdefault('readConcern', {})
910913
rc['level'] = 'snapshot'
911914
if self._snapshot_time is not None:

pymongo/message.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -315,10 +315,10 @@ def as_command(self, sock_info):
315315
session = self.session
316316
sock_info.add_server_api(cmd)
317317
if session:
318-
session._apply_to(cmd, False, self.read_preference)
318+
session._apply_to(cmd, False, self.read_preference, sock_info)
319319
# Explain does not support readConcern.
320320
if not explain and not session.in_transaction:
321-
session._update_read_concern(cmd)
321+
session._update_read_concern(cmd, sock_info)
322322
sock_info.send_cluster_time(cmd, session, self.client)
323323
# Support auto encryption
324324
client = self.client
@@ -420,7 +420,7 @@ def as_command(self, sock_info):
420420
self.max_await_time_ms)
421421

422422
if self.session:
423-
self.session._apply_to(cmd, False, self.read_preference)
423+
self.session._apply_to(cmd, False, self.read_preference, sock_info)
424424
sock_info.add_server_api(cmd)
425425
sock_info.send_cluster_time(cmd, self.session, self.client)
426426
# Support auto encryption

pymongo/network.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def command(sock_info, dbname, spec, slave_ok, is_mongos,
9494
if read_concern.level:
9595
spec['readConcern'] = read_concern.document
9696
if session:
97-
session._update_read_concern(spec)
97+
session._update_read_concern(spec, sock_info)
9898
if collation is not None:
9999
spec['collation'] = collation
100100

pymongo/pool.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,8 @@ def command(self, dbname, spec, slave_ok=False,
736736

737737
self.add_server_api(spec)
738738
if session:
739-
session._apply_to(spec, retryable_write, read_preference)
739+
session._apply_to(spec, retryable_write, read_preference,
740+
self)
740741
self.send_cluster_time(spec, session, client)
741742
listeners = self.listeners if publish_events else None
742743
unacknowledged = write_concern and not write_concern.acknowledged
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
{
2+
"description": "snapshot-sessions-not-supported-client-error",
3+
"schemaVersion": "1.0",
4+
"runOnRequirements": [
5+
{
6+
"minServerVersion": "3.6",
7+
"maxServerVersion": "4.4.99"
8+
}
9+
],
10+
"createEntities": [
11+
{
12+
"client": {
13+
"id": "client0",
14+
"observeEvents": [
15+
"commandStartedEvent",
16+
"commandFailedEvent"
17+
]
18+
}
19+
},
20+
{
21+
"database": {
22+
"id": "database0",
23+
"client": "client0",
24+
"databaseName": "database0"
25+
}
26+
},
27+
{
28+
"collection": {
29+
"id": "collection0",
30+
"database": "database0",
31+
"collectionName": "collection0"
32+
}
33+
},
34+
{
35+
"session": {
36+
"id": "session0",
37+
"client": "client0",
38+
"sessionOptions": {
39+
"snapshot": true
40+
}
41+
}
42+
}
43+
],
44+
"initialData": [
45+
{
46+
"collectionName": "collection0",
47+
"databaseName": "database0",
48+
"documents": [
49+
{
50+
"_id": 1,
51+
"x": 11
52+
}
53+
]
54+
}
55+
],
56+
"tests": [
57+
{
58+
"description": "Client error on find with snapshot",
59+
"operations": [
60+
{
61+
"name": "find",
62+
"object": "collection0",
63+
"arguments": {
64+
"session": "session0",
65+
"filter": {}
66+
},
67+
"expectError": {
68+
"isClientError": true,
69+
"errorContains": "Snapshot reads require MongoDB 5.0 or later"
70+
}
71+
}
72+
],
73+
"expectEvents": []
74+
},
75+
{
76+
"description": "Client error on aggregate with snapshot",
77+
"operations": [
78+
{
79+
"name": "aggregate",
80+
"object": "collection0",
81+
"arguments": {
82+
"session": "session0",
83+
"pipeline": []
84+
},
85+
"expectError": {
86+
"isClientError": true,
87+
"errorContains": "Snapshot reads require MongoDB 5.0 or later"
88+
}
89+
}
90+
],
91+
"expectEvents": []
92+
},
93+
{
94+
"description": "Client error on distinct with snapshot",
95+
"operations": [
96+
{
97+
"name": "distinct",
98+
"object": "collection0",
99+
"arguments": {
100+
"fieldName": "x",
101+
"filter": {},
102+
"session": "session0"
103+
},
104+
"expectError": {
105+
"isClientError": true,
106+
"errorContains": "Snapshot reads require MongoDB 5.0 or later"
107+
}
108+
}
109+
],
110+
"expectEvents": []
111+
}
112+
]
113+
}

test/sessions/unified/snapshot-sessions-not-supported-server-error.json

+86-10
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@
33
"schemaVersion": "1.0",
44
"runOnRequirements": [
55
{
6-
"minServerVersion": "3.6",
7-
"maxServerVersion": "4.4.99"
8-
},
9-
{
10-
"minServerVersion": "3.6",
6+
"minServerVersion": "5.0",
117
"topologies": [
128
"single"
139
]
@@ -20,11 +16,6 @@
2016
"observeEvents": [
2117
"commandStartedEvent",
2218
"commandFailedEvent"
23-
],
24-
"ignoreCommandMonitoringEvents": [
25-
"findAndModify",
26-
"insert",
27-
"update"
2819
]
2920
}
3021
},
@@ -106,6 +97,91 @@
10697
]
10798
}
10899
]
100+
},
101+
{
102+
"description": "Server returns an error on aggregate with snapshot",
103+
"operations": [
104+
{
105+
"name": "aggregate",
106+
"object": "collection0",
107+
"arguments": {
108+
"session": "session0",
109+
"pipeline": []
110+
},
111+
"expectError": {
112+
"isError": true,
113+
"isClientError": false
114+
}
115+
}
116+
],
117+
"expectEvents": [
118+
{
119+
"client": "client0",
120+
"events": [
121+
{
122+
"commandStartedEvent": {
123+
"command": {
124+
"aggregate": "collection0",
125+
"readConcern": {
126+
"level": "snapshot",
127+
"atClusterTime": {
128+
"$$exists": false
129+
}
130+
}
131+
}
132+
}
133+
},
134+
{
135+
"commandFailedEvent": {
136+
"commandName": "aggregate"
137+
}
138+
}
139+
]
140+
}
141+
]
142+
},
143+
{
144+
"description": "Server returns an error on distinct with snapshot",
145+
"operations": [
146+
{
147+
"name": "distinct",
148+
"object": "collection0",
149+
"arguments": {
150+
"fieldName": "x",
151+
"filter": {},
152+
"session": "session0"
153+
},
154+
"expectError": {
155+
"isError": true,
156+
"isClientError": false
157+
}
158+
}
159+
],
160+
"expectEvents": [
161+
{
162+
"client": "client0",
163+
"events": [
164+
{
165+
"commandStartedEvent": {
166+
"command": {
167+
"distinct": "collection0",
168+
"readConcern": {
169+
"level": "snapshot",
170+
"atClusterTime": {
171+
"$$exists": false
172+
}
173+
}
174+
}
175+
}
176+
},
177+
{
178+
"commandFailedEvent": {
179+
"commandName": "distinct"
180+
}
181+
}
182+
]
183+
}
184+
]
109185
}
110186
]
111187
}

0 commit comments

Comments
 (0)