Skip to content

Commit 70ad9c9

Browse files
authored
Drivers should retry operations if connection handshake fails (#885)
JAVA-4354
1 parent 988ead4 commit 70ad9c9

File tree

7 files changed

+1038
-4
lines changed

7 files changed

+1038
-4
lines changed

driver-core/src/main/com/mongodb/internal/operation/CommandOperationHelper.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.mongodb.MongoException;
2424
import com.mongodb.MongoNodeIsRecoveringException;
2525
import com.mongodb.MongoNotPrimaryException;
26+
import com.mongodb.MongoSecurityException;
2627
import com.mongodb.MongoServerException;
2728
import com.mongodb.MongoSocketException;
2829
import com.mongodb.ReadPreference;
@@ -145,6 +146,7 @@ interface CommandCreator {
145146

146147
private static Throwable chooseRetryableReadException(
147148
@Nullable final Throwable previouslyChosenException, final Throwable mostRecentAttemptException) {
149+
assertFalse(mostRecentAttemptException instanceof ResourceSupplierInternalException);
148150
if (previouslyChosenException == null
149151
|| mostRecentAttemptException instanceof MongoSocketException
150152
|| mostRecentAttemptException instanceof MongoServerException) {
@@ -530,10 +532,12 @@ static boolean isNamespaceError(final Throwable t) {
530532
}
531533

532534
private static boolean shouldAttemptToRetryRead(final RetryState retryState, final Throwable attemptFailure) {
533-
Throwable failure = attemptFailure instanceof ResourceSupplierInternalException ? attemptFailure.getCause() : attemptFailure;
534-
boolean decision = isRetryableException(failure);
535+
assertFalse(attemptFailure instanceof ResourceSupplierInternalException);
536+
boolean decision = isRetryableException(attemptFailure)
537+
|| (attemptFailure instanceof MongoSecurityException
538+
&& attemptFailure.getCause() != null && isRetryableException(attemptFailure.getCause()));
535539
if (!decision) {
536-
logUnableToRetry(retryState.attachment(AttachmentKeys.commandDescriptionSupplier()).orElse(null), failure);
540+
logUnableToRetry(retryState.attachment(AttachmentKeys.commandDescriptionSupplier()).orElse(null), attemptFailure);
537541
}
538542
return decision;
539543
}
@@ -542,7 +546,8 @@ static boolean shouldAttemptToRetryWrite(final RetryState retryState, final Thro
542546
Throwable failure = attemptFailure instanceof ResourceSupplierInternalException ? attemptFailure.getCause() : attemptFailure;
543547
boolean decision = false;
544548
MongoException exceptionRetryableRegardlessOfCommand = null;
545-
if (failure instanceof MongoConnectionPoolClearedException) {
549+
if (failure instanceof MongoConnectionPoolClearedException
550+
|| (failure instanceof MongoSecurityException && failure.getCause() != null && isRetryableException(failure.getCause()))) {
546551
decision = true;
547552
exceptionRetryableRegardlessOfCommand = (MongoException) failure;
548553
}
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
{
2+
"description": "retryable reads handshake failures",
3+
"schemaVersion": "1.3",
4+
"runOnRequirements": [
5+
{
6+
"minServerVersion": "4.2",
7+
"topologies": [
8+
"replicaset",
9+
"sharded",
10+
"load-balanced"
11+
],
12+
"auth": true
13+
}
14+
],
15+
"createEntities": [
16+
{
17+
"client": {
18+
"id": "client0",
19+
"useMultipleMongoses": false,
20+
"observeEvents": [
21+
"commandStartedEvent",
22+
"connectionCheckOutStartedEvent"
23+
]
24+
}
25+
},
26+
{
27+
"database": {
28+
"id": "database0",
29+
"client": "client0",
30+
"databaseName": "retryable-handshake-tests"
31+
}
32+
},
33+
{
34+
"collection": {
35+
"id": "collection0",
36+
"database": "database0",
37+
"collectionName": "coll"
38+
}
39+
}
40+
],
41+
"initialData": [
42+
{
43+
"collectionName": "coll",
44+
"databaseName": "retryable-handshake-tests",
45+
"documents": [
46+
{
47+
"_id": 1,
48+
"x": 11
49+
},
50+
{
51+
"_id": 2,
52+
"x": 22
53+
},
54+
{
55+
"_id": 3,
56+
"x": 33
57+
}
58+
]
59+
}
60+
],
61+
"tests": [
62+
{
63+
"description": "find succeeds after retryable handshake network error",
64+
"operations": [
65+
{
66+
"name": "failPoint",
67+
"object": "testRunner",
68+
"arguments": {
69+
"client": "client0",
70+
"failPoint": {
71+
"configureFailPoint": "failCommand",
72+
"mode": {
73+
"times": 2
74+
},
75+
"data": {
76+
"failCommands": [
77+
"saslContinue",
78+
"ping"
79+
],
80+
"closeConnection": true
81+
}
82+
}
83+
}
84+
},
85+
{
86+
"name": "runCommand",
87+
"object": "database0",
88+
"arguments": {
89+
"commandName": "ping",
90+
"command": {
91+
"ping": 1
92+
}
93+
},
94+
"expectError": {
95+
"isError": true
96+
}
97+
},
98+
{
99+
"name": "find",
100+
"object": "collection0",
101+
"arguments": {
102+
"filter": {
103+
"_id": 2
104+
}
105+
},
106+
"expectResult": [
107+
{
108+
"_id": 2,
109+
"x": 22
110+
}
111+
]
112+
}
113+
],
114+
"expectEvents": [
115+
{
116+
"client": "client0",
117+
"eventType": "cmap",
118+
"events": [
119+
{
120+
"connectionCheckOutStartedEvent": {}
121+
},
122+
{
123+
"connectionCheckOutStartedEvent": {}
124+
},
125+
{
126+
"connectionCheckOutStartedEvent": {}
127+
},
128+
{
129+
"connectionCheckOutStartedEvent": {}
130+
}
131+
]
132+
},
133+
{
134+
"client": "client0",
135+
"events": [
136+
{
137+
"commandStartedEvent": {
138+
"command": {
139+
"ping": 1
140+
},
141+
"databaseName": "retryable-handshake-tests"
142+
}
143+
},
144+
{
145+
"commandStartedEvent": {
146+
"command": {
147+
"find": "coll",
148+
"filter": {
149+
"_id": 2
150+
}
151+
},
152+
"databaseName": "retryable-handshake-tests"
153+
}
154+
}
155+
]
156+
}
157+
]
158+
},
159+
{
160+
"description": "find succeeds after retryable handshake network error (ShutdownInProgress)",
161+
"operations": [
162+
{
163+
"name": "failPoint",
164+
"object": "testRunner",
165+
"arguments": {
166+
"client": "client0",
167+
"failPoint": {
168+
"configureFailPoint": "failCommand",
169+
"mode": {
170+
"times": 2
171+
},
172+
"data": {
173+
"failCommands": [
174+
"saslContinue",
175+
"ping"
176+
],
177+
"errorCode": 91
178+
}
179+
}
180+
}
181+
},
182+
{
183+
"name": "runCommand",
184+
"object": "database0",
185+
"arguments": {
186+
"commandName": "ping",
187+
"command": {
188+
"ping": 1
189+
}
190+
},
191+
"expectError": {
192+
"isError": true
193+
}
194+
},
195+
{
196+
"name": "find",
197+
"object": "collection0",
198+
"arguments": {
199+
"filter": {
200+
"_id": 2
201+
}
202+
},
203+
"expectResult": [
204+
{
205+
"_id": 2,
206+
"x": 22
207+
}
208+
]
209+
}
210+
],
211+
"expectEvents": [
212+
{
213+
"client": "client0",
214+
"eventType": "cmap",
215+
"events": [
216+
{
217+
"connectionCheckOutStartedEvent": {}
218+
},
219+
{
220+
"connectionCheckOutStartedEvent": {}
221+
},
222+
{
223+
"connectionCheckOutStartedEvent": {}
224+
},
225+
{
226+
"connectionCheckOutStartedEvent": {}
227+
}
228+
]
229+
},
230+
{
231+
"client": "client0",
232+
"events": [
233+
{
234+
"commandStartedEvent": {
235+
"command": {
236+
"ping": 1
237+
},
238+
"databaseName": "retryable-handshake-tests"
239+
}
240+
},
241+
{
242+
"commandStartedEvent": {
243+
"command": {
244+
"find": "coll",
245+
"filter": {
246+
"_id": 2
247+
}
248+
},
249+
"databaseName": "retryable-handshake-tests"
250+
}
251+
}
252+
]
253+
}
254+
]
255+
}
256+
]
257+
}

0 commit comments

Comments
 (0)