Skip to content

Commit 85b0c0e

Browse files
authored
PYTHON-4018 Clarify exactly what code/label fields drivers should inspect to determine retryability (#1489)
1 parent 5877be9 commit 85b0c0e

File tree

2 files changed

+320
-14
lines changed

2 files changed

+320
-14
lines changed

pymongo/mongo_client.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
PyMongoError,
8484
ServerSelectionTimeoutError,
8585
WaitQueueTimeoutError,
86+
WriteConcernError,
8687
)
8788
from pymongo.lock import _HAS_REGISTER_AT_FORK, _create_lock, _release_locks
8889
from pymongo.monitoring import ConnectionClosedReason
@@ -2141,7 +2142,7 @@ def _retryable_error_doc(exc: PyMongoError) -> Optional[Mapping[str, Any]]:
21412142
return None
21422143

21432144

2144-
def _add_retryable_write_error(exc: PyMongoError, max_wire_version: int) -> None:
2145+
def _add_retryable_write_error(exc: PyMongoError, max_wire_version: int, is_mongos: bool) -> None:
21452146
doc = _retryable_error_doc(exc)
21462147
if doc:
21472148
code = doc.get("code", 0)
@@ -2158,7 +2159,10 @@ def _add_retryable_write_error(exc: PyMongoError, max_wire_version: int) -> None
21582159
for label in doc.get("errorLabels", []):
21592160
exc._add_error_label(label)
21602161
else:
2161-
if code in helpers._RETRYABLE_ERROR_CODES:
2162+
# Do not consult writeConcernError for pre-4.4 mongos.
2163+
if isinstance(exc, WriteConcernError) and is_mongos:
2164+
pass
2165+
elif code in helpers._RETRYABLE_ERROR_CODES:
21622166
exc._add_error_label("RetryableWriteError")
21632167

21642168
# Connection errors are always retryable except NotPrimaryError and WaitQueueTimeoutError which is
@@ -2392,12 +2396,14 @@ def _write(self) -> T:
23922396
"""
23932397
try:
23942398
max_wire_version = 0
2399+
is_mongos = False
23952400
self._server = self._get_server()
23962401
supports_session = (
23972402
self._session is not None and self._server.description.retryable_writes_supported
23982403
)
23992404
with self._client._checkout(self._server, self._session) as conn:
24002405
max_wire_version = conn.max_wire_version
2406+
is_mongos = conn.is_mongos
24012407
if self._retryable and not supports_session:
24022408
# A retry is not possible because this server does
24032409
# not support sessions raise the last error.
@@ -2408,7 +2414,7 @@ def _write(self) -> T:
24082414
if not self._retryable:
24092415
raise
24102416
# Add the RetryableWriteError label, if applicable.
2411-
_add_retryable_write_error(exc, max_wire_version)
2417+
_add_retryable_write_error(exc, max_wire_version, is_mongos)
24122418
raise
24132419

24142420
def _read(self) -> T:

test/retryable_writes/unified/insertOne-serverErrors.json

Lines changed: 311 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33
"schemaVersion": "1.0",
44
"runOnRequirements": [
55
{
6-
"minServerVersion": "3.6",
6+
"minServerVersion": "4.0",
77
"topologies": [
88
"replicaset"
99
]
10+
},
11+
{
12+
"minServerVersion": "4.1.7",
13+
"topologies": [
14+
"sharded"
15+
]
1016
}
1117
],
1218
"createEntities": [
@@ -55,16 +61,7 @@
5561
"description": "InsertOne succeeds after retryable writeConcernError",
5662
"runOnRequirements": [
5763
{
58-
"minServerVersion": "4.0",
59-
"topologies": [
60-
"replicaset"
61-
]
62-
},
63-
{
64-
"minServerVersion": "4.1.7",
65-
"topologies": [
66-
"sharded-replicaset"
67-
]
64+
"minServerVersion": "4.3.1"
6865
}
6966
],
7067
"operations": [
@@ -168,6 +165,309 @@
168165
]
169166
}
170167
]
168+
},
169+
{
170+
"description": "RetryableWriteError label is added based on top-level code in pre-4.4 server response",
171+
"runOnRequirements": [
172+
{
173+
"minServerVersion": "4.2",
174+
"maxServerVersion": "4.2.99",
175+
"topologies": [
176+
"replicaset",
177+
"sharded"
178+
]
179+
}
180+
],
181+
"operations": [
182+
{
183+
"name": "failPoint",
184+
"object": "testRunner",
185+
"arguments": {
186+
"client": "client0",
187+
"failPoint": {
188+
"configureFailPoint": "failCommand",
189+
"mode": {
190+
"times": 2
191+
},
192+
"data": {
193+
"failCommands": [
194+
"insert"
195+
],
196+
"errorCode": 189
197+
}
198+
}
199+
}
200+
},
201+
{
202+
"name": "insertOne",
203+
"object": "collection0",
204+
"arguments": {
205+
"document": {
206+
"_id": 3,
207+
"x": 33
208+
}
209+
},
210+
"expectError": {
211+
"errorLabelsContain": [
212+
"RetryableWriteError"
213+
]
214+
}
215+
}
216+
],
217+
"expectEvents": [
218+
{
219+
"client": "client0",
220+
"events": [
221+
{
222+
"commandStartedEvent": {
223+
"command": {
224+
"insert": "coll",
225+
"documents": [
226+
{
227+
"_id": 3,
228+
"x": 33
229+
}
230+
]
231+
},
232+
"commandName": "insert",
233+
"databaseName": "retryable-writes-tests"
234+
}
235+
},
236+
{
237+
"commandStartedEvent": {
238+
"command": {
239+
"insert": "coll",
240+
"documents": [
241+
{
242+
"_id": 3,
243+
"x": 33
244+
}
245+
]
246+
},
247+
"commandName": "insert",
248+
"databaseName": "retryable-writes-tests"
249+
}
250+
}
251+
]
252+
}
253+
],
254+
"outcome": [
255+
{
256+
"collectionName": "coll",
257+
"databaseName": "retryable-writes-tests",
258+
"documents": [
259+
{
260+
"_id": 1,
261+
"x": 11
262+
},
263+
{
264+
"_id": 2,
265+
"x": 22
266+
}
267+
]
268+
}
269+
]
270+
},
271+
{
272+
"description": "RetryableWriteError label is added based on writeConcernError in pre-4.4 mongod response",
273+
"runOnRequirements": [
274+
{
275+
"minServerVersion": "4.2",
276+
"maxServerVersion": "4.2.99",
277+
"topologies": [
278+
"replicaset"
279+
]
280+
}
281+
],
282+
"operations": [
283+
{
284+
"name": "failPoint",
285+
"object": "testRunner",
286+
"arguments": {
287+
"client": "client0",
288+
"failPoint": {
289+
"configureFailPoint": "failCommand",
290+
"mode": {
291+
"times": 2
292+
},
293+
"data": {
294+
"failCommands": [
295+
"insert"
296+
],
297+
"writeConcernError": {
298+
"code": 91,
299+
"errmsg": "Replication is being shut down"
300+
}
301+
}
302+
}
303+
}
304+
},
305+
{
306+
"name": "insertOne",
307+
"object": "collection0",
308+
"arguments": {
309+
"document": {
310+
"_id": 3,
311+
"x": 33
312+
}
313+
},
314+
"expectError": {
315+
"errorLabelsContain": [
316+
"RetryableWriteError"
317+
]
318+
}
319+
}
320+
],
321+
"expectEvents": [
322+
{
323+
"client": "client0",
324+
"events": [
325+
{
326+
"commandStartedEvent": {
327+
"command": {
328+
"insert": "coll",
329+
"documents": [
330+
{
331+
"_id": 3,
332+
"x": 33
333+
}
334+
]
335+
},
336+
"commandName": "insert",
337+
"databaseName": "retryable-writes-tests"
338+
}
339+
},
340+
{
341+
"commandStartedEvent": {
342+
"command": {
343+
"insert": "coll",
344+
"documents": [
345+
{
346+
"_id": 3,
347+
"x": 33
348+
}
349+
]
350+
},
351+
"commandName": "insert",
352+
"databaseName": "retryable-writes-tests"
353+
}
354+
}
355+
]
356+
}
357+
],
358+
"outcome": [
359+
{
360+
"collectionName": "coll",
361+
"databaseName": "retryable-writes-tests",
362+
"documents": [
363+
{
364+
"_id": 1,
365+
"x": 11
366+
},
367+
{
368+
"_id": 2,
369+
"x": 22
370+
},
371+
{
372+
"_id": 3,
373+
"x": 33
374+
}
375+
]
376+
}
377+
]
378+
},
379+
{
380+
"description": "RetryableWriteError label is not added based on writeConcernError in pre-4.4 mongos response",
381+
"runOnRequirements": [
382+
{
383+
"minServerVersion": "4.2",
384+
"maxServerVersion": "4.2.99",
385+
"topologies": [
386+
"sharded"
387+
]
388+
}
389+
],
390+
"operations": [
391+
{
392+
"name": "failPoint",
393+
"object": "testRunner",
394+
"arguments": {
395+
"client": "client0",
396+
"failPoint": {
397+
"configureFailPoint": "failCommand",
398+
"mode": {
399+
"times": 1
400+
},
401+
"data": {
402+
"failCommands": [
403+
"insert"
404+
],
405+
"writeConcernError": {
406+
"code": 91,
407+
"errmsg": "Replication is being shut down"
408+
}
409+
}
410+
}
411+
}
412+
},
413+
{
414+
"name": "insertOne",
415+
"object": "collection0",
416+
"arguments": {
417+
"document": {
418+
"_id": 3,
419+
"x": 33
420+
}
421+
},
422+
"expectError": {
423+
"errorLabelsOmit": [
424+
"RetryableWriteError"
425+
]
426+
}
427+
}
428+
],
429+
"expectEvents": [
430+
{
431+
"client": "client0",
432+
"events": [
433+
{
434+
"commandStartedEvent": {
435+
"command": {
436+
"insert": "coll",
437+
"documents": [
438+
{
439+
"_id": 3,
440+
"x": 33
441+
}
442+
]
443+
},
444+
"commandName": "insert",
445+
"databaseName": "retryable-writes-tests"
446+
}
447+
}
448+
]
449+
}
450+
],
451+
"outcome": [
452+
{
453+
"collectionName": "coll",
454+
"databaseName": "retryable-writes-tests",
455+
"documents": [
456+
{
457+
"_id": 1,
458+
"x": 11
459+
},
460+
{
461+
"_id": 2,
462+
"x": 22
463+
},
464+
{
465+
"_id": 3,
466+
"x": 33
467+
}
468+
]
469+
}
470+
]
171471
}
172472
]
173473
}

0 commit comments

Comments
 (0)