Skip to content

Commit 559a1ad

Browse files
committed
CSHARP-2229: Retryable writes should consider more errors retryable
1 parent 1a74751 commit 559a1ad

File tree

10 files changed

+319
-80
lines changed

10 files changed

+319
-80
lines changed

src/MongoDB.Driver.Core/Core/Operations/ChangeStreamCursor.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,32 @@ public void Dispose()
131131
// private methods
132132
private bool CanResumeAfter(Exception exception)
133133
{
134-
return exception is IOException || exception is MongoCursorNotFoundException || exception is MongoNotPrimaryException;
134+
var commandException = exception as MongoCommandException;
135+
if (commandException != null)
136+
{
137+
var code = (ServerErrorCode)commandException.Code;
138+
switch (code)
139+
{
140+
case ServerErrorCode.HostNotFound:
141+
case ServerErrorCode.HostUnreachable:
142+
case ServerErrorCode.InterruptedAtShutdown:
143+
case ServerErrorCode.InterruptedDueToReplStateChange:
144+
case ServerErrorCode.NetworkTimeout:
145+
case ServerErrorCode.NotMaster:
146+
case ServerErrorCode.NotMasterNoSlaveOk:
147+
case ServerErrorCode.NotMasterOrSecondary:
148+
case ServerErrorCode.PrimarySteppedDown:
149+
case ServerErrorCode.ShutdownInProgress:
150+
case ServerErrorCode.SocketException:
151+
case ServerErrorCode.WriteConcernFailed:
152+
return true;
153+
154+
default:
155+
return false;
156+
}
157+
}
158+
159+
return exception is MongoConnectionException || exception is MongoCursorNotFoundException || exception is MongoNotPrimaryException;
135160
}
136161

137162
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]

src/MongoDB.Driver.Core/Core/Operations/RetryableWriteOperationExecutor.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,31 @@ private static bool AreRetryableWritesSupported(ConnectionDescription connection
137137

138138
private static bool IsRetryableException(Exception ex)
139139
{
140+
var commandException = ex as MongoCommandException;
141+
if (commandException != null)
142+
{
143+
var code = (ServerErrorCode)commandException.Code;
144+
switch (code)
145+
{
146+
case ServerErrorCode.HostNotFound:
147+
case ServerErrorCode.HostUnreachable:
148+
case ServerErrorCode.InterruptedAtShutdown:
149+
case ServerErrorCode.InterruptedDueToReplStateChange:
150+
case ServerErrorCode.NetworkTimeout:
151+
case ServerErrorCode.NotMaster:
152+
case ServerErrorCode.NotMasterNoSlaveOk:
153+
case ServerErrorCode.NotMasterOrSecondary:
154+
case ServerErrorCode.PrimarySteppedDown:
155+
case ServerErrorCode.ShutdownInProgress:
156+
case ServerErrorCode.SocketException:
157+
case ServerErrorCode.WriteConcernFailed:
158+
return true;
159+
160+
default:
161+
return false;
162+
}
163+
}
164+
140165
return
141166
ex is MongoConnectionException ||
142167
ex is MongoNotPrimaryException ||

src/MongoDB.Driver.Core/MongoDB.Driver.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@
326326
<Compile Include="Core\Clusters\ServerSelectors\RandomServerSelector.cs" />
327327
<Compile Include="Core\Clusters\ServerSelectors\ReadPreferenceServerSelector.cs" />
328328
<Compile Include="Core\Clusters\SingleServerCluster.cs" />
329+
<Compile Include="ServerErrorCode.cs" />
329330
<Compile Include="SingleBatchAsyncCursor.cs" />
330331
<Compile Include="Tag.cs" />
331332
<Compile Include="TagSet.cs" />
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/* Copyright 2018-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
namespace MongoDB.Driver
17+
{
18+
internal enum ServerErrorCode
19+
{
20+
// this is not a complete list, more will be added as needed
21+
HostNotFound = 7,
22+
HostUnreachable = 6,
23+
InterruptedAtShutdown = 11600,
24+
InterruptedDueToReplStateChange = 11602,
25+
NetworkTimeout = 89,
26+
NotMaster = 10107,
27+
NotMasterNoSlaveOk = 13435,
28+
NotMasterOrSecondary = 13436,
29+
PrimarySteppedDown = 189,
30+
ShutdownInProgress = 91,
31+
SocketException = 9001,
32+
WriteConcernFailed = 64
33+
}
34+
}

tests/MongoDB.Bson.TestHelpers/Reflector.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* limitations under the License.
1414
*/
1515

16+
using System;
1617
using System.Linq;
1718
using System.Reflection;
1819

@@ -42,5 +43,14 @@ public static object Invoke<T1>(object obj, string name, T1 arg1)
4243
.Single();
4344
return methodInfo.Invoke(obj, new object[] { arg1 });
4445
}
46+
47+
public static object InvokeStatic<T1>(Type type, string name, T1 arg1)
48+
{
49+
var parameterTypes = new[] { typeof(T1) };
50+
var methodInfo = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
51+
.Where(m => m.Name == name && m.GetParameters().Select(p => p.ParameterType).SequenceEqual(parameterTypes))
52+
.Single();
53+
return methodInfo.Invoke(null, new object[] { arg1 });
54+
}
4555
}
4656
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/* Copyright 2018-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.IO;
18+
using System.Net;
19+
using MongoDB.Bson;
20+
using MongoDB.Driver.Core.Clusters;
21+
using MongoDB.Driver.Core.Connections;
22+
using MongoDB.Driver.Core.Servers;
23+
24+
namespace MongoDB.Driver.Core.TestHelpers
25+
{
26+
public static class CoreExceptionHelper
27+
{
28+
public static Exception CreateException(Type exceptionType)
29+
{
30+
switch (exceptionType.Name)
31+
{
32+
case "IOException":
33+
return new IOException("Fake IOException.");
34+
35+
case "MongoConnectionException":
36+
{
37+
var clusterId = new ClusterId(1);
38+
var serverId = new ServerId(clusterId, new DnsEndPoint("localhost", 27017));
39+
var connectionId = new ConnectionId(serverId, 1);
40+
var message = "Fake MongoConnectionException";
41+
var innerException = new Exception();
42+
return new MongoConnectionException(connectionId, message, innerException);
43+
}
44+
45+
case "MongoCursorNotFoundException":
46+
{
47+
var clusterId = new ClusterId(1);
48+
var serverId = new ServerId(clusterId, new DnsEndPoint("localhost", 27017));
49+
var connectionId = new ConnectionId(serverId, 1);
50+
var cursorId = 1L;
51+
var query = new BsonDocument();
52+
return new MongoCursorNotFoundException(connectionId, cursorId, query);
53+
}
54+
55+
case "MongoNodeIsRecoveringException":
56+
{
57+
var clusterId = new ClusterId(1);
58+
var serverId = new ServerId(clusterId, new DnsEndPoint("localhost", 27017));
59+
var connectionId = new ConnectionId(serverId, 1);
60+
var result = new BsonDocument();
61+
return new MongoNodeIsRecoveringException(connectionId, result);
62+
}
63+
64+
case "MongoNotPrimaryException":
65+
{
66+
var clusterId = new ClusterId(1);
67+
var serverId = new ServerId(clusterId, new DnsEndPoint("localhost", 27017));
68+
var connectionId = new ConnectionId(serverId, 1);
69+
var result = new BsonDocument();
70+
return new MongoNotPrimaryException(connectionId, result);
71+
}
72+
73+
default:
74+
throw new ArgumentException($"Unexpected exception type: {exceptionType.Name}.", nameof(exceptionType));
75+
}
76+
}
77+
78+
public static MongoCommandException CreateMongoCommandException(int code)
79+
{
80+
var clusterId = new ClusterId(1);
81+
var endPoint = new DnsEndPoint("localhost", 27017);
82+
var serverId = new ServerId(clusterId, endPoint);
83+
var connectionId = new ConnectionId(serverId);
84+
var message = "Fake MongoCommandException";
85+
var command = BsonDocument.Parse("{ command : 1 }");
86+
var result = BsonDocument.Parse($"{{ ok: 0, code : {code} }}");
87+
return new MongoCommandException(connectionId, message, command, result);
88+
}
89+
}
90+
}

tests/MongoDB.Driver.Core.TestHelpers/MongoDB.Driver.Core.TestHelpers.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
</ItemGroup>
6060
<ItemGroup>
6161
<Compile Include="ClusterDescriptionParser.cs" />
62+
<Compile Include="CoreExceptionHelper.cs" />
6263
<Compile Include="CoreTestConfiguration.cs" />
6364
<Compile Include="EventCapturer.cs" />
6465
<Compile Include="ICoreSessionHandleExtensions.cs" />

0 commit comments

Comments
 (0)