Skip to content

Commit 62d4865

Browse files
committed
CSHARP-2678: Raise an actionable error message when retryWrites fails due to using an unsupported storage engine
1 parent bb78598 commit 62d4865

File tree

4 files changed

+74
-2
lines changed

4 files changed

+74
-2
lines changed

src/MongoDB.Driver.Core/Core/WireProtocol/CommandUsingCommandMessageWireProtocol.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,16 @@ private Type0CommandMessageSection<BsonDocument> CreateType0Section(ConnectionDe
234234
return new Type0CommandMessageSection<BsonDocument>(_command, elementAppendingSerializer);
235235
}
236236

237+
private bool IsRetryableWriteExceptionAndDeploymentDoesNotSupportRetryableWrites(MongoCommandException exception)
238+
{
239+
return
240+
exception.Result.TryGetValue("code", out var errorCode) &&
241+
errorCode.ToInt32() == 20 &&
242+
exception.Result.TryGetValue("errmsg", out var errmsg) &&
243+
errmsg.AsString.StartsWith("Transaction numbers");
244+
}
245+
246+
237247
private void MessageWasProbablySent(CommandRequestMessage message)
238248
{
239249
if (_session.Id != null)
@@ -319,7 +329,17 @@ private TCommandResult ProcessResponse(ConnectionId connectionId, CommandMessage
319329
message = string.Format("Command {0} failed.", commandName);
320330
}
321331

322-
throw new MongoCommandException(connectionId, message, _command, materializedDocument);
332+
var exception = new MongoCommandException(connectionId, message, _command, materializedDocument);
333+
334+
// https://jira.mongodb.org/browse/CSHARP-2678
335+
if (IsRetryableWriteExceptionAndDeploymentDoesNotSupportRetryableWrites(exception))
336+
{
337+
throw WrapNotSupportedRetryableWriteException(exception);
338+
}
339+
else
340+
{
341+
throw exception;
342+
}
323343
}
324344

325345
if (rawDocument.Contains("writeConcernError"))
@@ -354,5 +374,18 @@ private bool ShouldAddTransientTransactionError(MongoException exception)
354374

355375
return false;
356376
}
377+
378+
private MongoException WrapNotSupportedRetryableWriteException(MongoCommandException exception)
379+
{
380+
const string friendlyErrorMessage =
381+
"This MongoDB deployment does not support retryable writes. " +
382+
"Please add retryWrites=false to your connection string.";
383+
return new MongoCommandException(
384+
exception.ConnectionId,
385+
friendlyErrorMessage,
386+
exception.Command,
387+
exception.Result,
388+
innerException: exception);
389+
}
357390
}
358391
}

src/MongoDB.Driver.Core/MongoCommandException.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,21 @@ public MongoCommandException(ConnectionId connectionId, string message, BsonDocu
7575
/// <param name="command">The command.</param>
7676
/// <param name="result">The command result.</param>
7777
public MongoCommandException(ConnectionId connectionId, string message, BsonDocument command, BsonDocument result)
78-
: base(connectionId, message)
78+
: this(connectionId, message, command, result, innerException: null)
79+
{
80+
}
81+
82+
/// <summary>
83+
/// Initializes a new instance of the <see cref="MongoCommandException"/> class.
84+
/// </summary>
85+
/// <param name="connectionId">The connection identifier.</param>
86+
/// <param name="message">The message.</param>
87+
/// <param name="command">The command.</param>
88+
/// // <param name="result">The command result.</param>
89+
/// <param name="innerException">The inner exception.</param>
90+
///
91+
public MongoCommandException(ConnectionId connectionId, string message, BsonDocument command, BsonDocument result, Exception innerException)
92+
: base(connectionId, message, innerException)
7993
{
8094
_command = command; // can be null
8195
_result = result; // can be null

tests/MongoDB.Driver.Tests/RetryableWritesTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,26 @@ public void Insert_with_RetryWrites_true_should_work_whether_retryable_writes_ar
4343
}
4444
}
4545

46+
[SkippableFact]
47+
public void Retryable_write_operation_should_throw_custom_exception_on_servers_using_mmapv1()
48+
{
49+
RequireSupportForRetryableWrites();
50+
RequireServer.Check().StorageEngine("mmapv1");
51+
52+
using (var client = GetClient())
53+
using (var session = client.StartSession())
54+
{
55+
var database = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName);
56+
var collection = database.GetCollection<BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName);
57+
var document = new BsonDocument("x", 1);
58+
var exception = Record.Exception(() => collection.InsertOne(document));
59+
60+
exception.Message.Should().Contain(
61+
"This MongoDB deployment does not support retryable writes. " +
62+
"Please add retryWrites=false to your connection string.");
63+
}
64+
}
65+
4666
[SkippableFact]
4767
public void TxnNumber_should_be_included_with_FindOneAndDelete()
4868
{

tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWriteTestRunner.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ private void VerifyServerRequirements(BsonDocument definition)
5555
{
5656
RequireServer.Check().ClusterType(ClusterType.ReplicaSet);
5757

58+
if (CoreTestConfiguration.GetStorageEngine() == "mmapv1")
59+
{
60+
throw new SkipException("Test skipped because mmapv1 does not support retryable writes.");
61+
}
62+
5863
BsonValue minServerVersion;
5964
if (definition.TryGetValue("minServerVersion", out minServerVersion))
6065
{

0 commit comments

Comments
 (0)