Skip to content

Commit 91e55bd

Browse files
GODRIVER-3032 Don't retry writes on pre-4.4 mongos (#1915)
1 parent 9afdc8c commit 91e55bd

File tree

4 files changed

+142
-4
lines changed

4 files changed

+142
-4
lines changed

testdata/retryable-writes/unified/insertOne-serverErrors.json

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,99 @@
377377
}
378378
]
379379
},
380+
{
381+
"description": "RetryableWriteError label is not added based on writeConcernError in pre-4.4 mongos response",
382+
"runOnRequirements": [
383+
{
384+
"minServerVersion": "4.2",
385+
"maxServerVersion": "4.2.99",
386+
"topologies": [
387+
"sharded"
388+
]
389+
}
390+
],
391+
"operations": [
392+
{
393+
"name": "failPoint",
394+
"object": "testRunner",
395+
"arguments": {
396+
"client": "client0",
397+
"failPoint": {
398+
"configureFailPoint": "failCommand",
399+
"mode": {
400+
"times": 1
401+
},
402+
"data": {
403+
"failCommands": [
404+
"insert"
405+
],
406+
"writeConcernError": {
407+
"code": 91,
408+
"errmsg": "Replication is being shut down"
409+
}
410+
}
411+
}
412+
}
413+
},
414+
{
415+
"name": "insertOne",
416+
"object": "collection0",
417+
"arguments": {
418+
"document": {
419+
"_id": 3,
420+
"x": 33
421+
}
422+
},
423+
"expectError": {
424+
"errorLabelsOmit": [
425+
"RetryableWriteError"
426+
]
427+
}
428+
}
429+
],
430+
"expectEvents": [
431+
{
432+
"client": "client0",
433+
"events": [
434+
{
435+
"commandStartedEvent": {
436+
"command": {
437+
"insert": "coll",
438+
"documents": [
439+
{
440+
"_id": 3,
441+
"x": 33
442+
}
443+
]
444+
},
445+
"commandName": "insert",
446+
"databaseName": "retryable-writes-tests"
447+
}
448+
}
449+
]
450+
}
451+
],
452+
"outcome": [
453+
{
454+
"collectionName": "coll",
455+
"databaseName": "retryable-writes-tests",
456+
"documents": [
457+
{
458+
"_id": 1,
459+
"x": 11
460+
},
461+
{
462+
"_id": 2,
463+
"x": 22
464+
},
465+
{
466+
"_id": 3,
467+
"x": 33
468+
}
469+
]
470+
}
471+
]
472+
},
380473
{
381474
"description": "InsertOne succeeds after connection failure",
382475
"operations": [

testdata/retryable-writes/unified/insertOne-serverErrors.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,44 @@ tests:
157157
# writeConcernError doesn't prevent the server from applying the write
158158
- { _id: 3, x: 33 }
159159

160+
- description: "RetryableWriteError label is not added based on writeConcernError in pre-4.4 mongos response"
161+
runOnRequirements:
162+
- minServerVersion: "4.2"
163+
maxServerVersion: "4.2.99"
164+
topologies: [ sharded ]
165+
operations:
166+
- name: failPoint
167+
object: testRunner
168+
arguments:
169+
client: *client0
170+
failPoint:
171+
configureFailPoint: failCommand
172+
# Trigger the fail point only once since a RetryableWriteError label
173+
# will not be added and the write will not be retried.
174+
mode: { times: 1 }
175+
data:
176+
failCommands: [ "insert" ]
177+
writeConcernError:
178+
code: 91 # ShutdownInProgress
179+
errmsg: "Replication is being shut down"
180+
- name: insertOne
181+
object: *collection0
182+
arguments:
183+
document: { _id: 3, x: 33 }
184+
expectError:
185+
errorLabelsOmit: [ "RetryableWriteError" ]
186+
expectEvents:
187+
- client: *client0
188+
events:
189+
- commandStartedEvent: *insertCommandStartedEvent
190+
outcome:
191+
- collectionName: *collectionName
192+
databaseName: *databaseName
193+
documents:
194+
- { _id: 1, x: 11 }
195+
- { _id: 2, x: 22 }
196+
# writeConcernError doesn't prevent the server from applying the write
197+
- { _id: 3, x: 33 }
160198
-
161199
description: 'InsertOne succeeds after connection failure'
162200
operations:

x/mongo/driver/errors.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func (wce WriteCommandError) Error() string {
140140
}
141141

142142
// Retryable returns true if the error is retryable
143-
func (wce WriteCommandError) Retryable(wireVersion *description.VersionRange) bool {
143+
func (wce WriteCommandError) Retryable(serverKind description.ServerKind, wireVersion *description.VersionRange) bool {
144144
for _, label := range wce.Labels {
145145
if label == RetryableWriteError {
146146
return true
@@ -153,7 +153,7 @@ func (wce WriteCommandError) Retryable(wireVersion *description.VersionRange) bo
153153
if wce.WriteConcernError == nil {
154154
return false
155155
}
156-
return wce.WriteConcernError.Retryable()
156+
return wce.WriteConcernError.Retryable(serverKind, wireVersion)
157157
}
158158

159159
// HasErrorLabel returns true if the error contains the specified label.
@@ -186,7 +186,14 @@ func (wce WriteConcernError) Error() string {
186186
}
187187

188188
// Retryable returns true if the error is retryable
189-
func (wce WriteConcernError) Retryable() bool {
189+
func (wce WriteConcernError) Retryable(serverKind description.ServerKind, wireVersion *description.VersionRange) bool {
190+
if serverKind == description.ServerKindMongos && wireVersion.Max < 9 {
191+
// For a pre-4.4 mongos response, we can trust that mongos will have already
192+
// retried the operation if necessary. Drivers should not retry to avoid
193+
// "excessive retrying".
194+
return false
195+
}
196+
190197
for _, code := range retryableCodes {
191198
if wce.Code == int64(code) {
192199
return true

x/mongo/driver/operation.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@ func (op Operation) Execute(ctx context.Context) error {
820820
}
821821

822822
connDesc := conn.Description()
823-
retryableErr := tt.Retryable(connDesc.WireVersion)
823+
retryableErr := tt.Retryable(connDesc.Kind, connDesc.WireVersion)
824824
preRetryWriteLabelVersion := connDesc.WireVersion != nil && connDesc.WireVersion.Max < 9
825825
inTransaction := op.Client != nil &&
826826
!(op.Client.Committing || op.Client.Aborting) && op.Client.TransactionRunning()

0 commit comments

Comments
 (0)