Skip to content

Commit a0db633

Browse files
CSHARP-2612: Make errors with NonResumableChangeStreamError non resumable.
1 parent ff0aeae commit a0db633

File tree

6 files changed

+111
-11
lines changed

6 files changed

+111
-11
lines changed

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515

1616
using System;
1717
using System.Collections.Generic;
18+
using System.Linq;
1819

1920
namespace MongoDB.Driver.Core.Operations
2021
{
2122
internal static class RetryabilityHelper
2223
{
2324
// private static fields
2425
private static readonly HashSet<ServerErrorCode> __notResumableChangeStreamErrorCodes;
26+
private static readonly HashSet<string> __notResumableChangeStreamErrorLabels;
2527
private static readonly HashSet<Type> __resumableChangeStreamExceptions;
2628
private static readonly HashSet<Type> __retryableReadExceptions;
2729
private static readonly HashSet<Type> __retryableWriteExceptions;
@@ -45,7 +47,7 @@ static RetryabilityHelper()
4547

4648

4749
__retryableReadExceptions = new HashSet<Type>(resumableAndRetryableExceptions);
48-
50+
4951
__retryableWriteExceptions = new HashSet<Type>(resumableAndRetryableExceptions);
5052

5153
var resumableAndRetryableErrorCodes = new HashSet<ServerErrorCode>
@@ -69,6 +71,11 @@ static RetryabilityHelper()
6971
ServerErrorCode.CursorKilled,
7072
ServerErrorCode.Interrupted
7173
};
74+
75+
__notResumableChangeStreamErrorLabels = new HashSet<string>()
76+
{
77+
"NonResumableChangeStreamError"
78+
};
7279
}
7380

7481
// public static methods
@@ -78,14 +85,17 @@ public static bool IsResumableChangeStreamException(Exception exception)
7885
if (commandException != null)
7986
{
8087
var code = (ServerErrorCode)commandException.Code;
81-
return !__notResumableChangeStreamErrorCodes.Contains(code);
88+
var isNonResumable =
89+
__notResumableChangeStreamErrorCodes.Contains(code) ||
90+
__notResumableChangeStreamErrorLabels.Any(c => commandException.HasErrorLabel(c));
91+
return !isNonResumable;
8292
}
8393
else
8494
{
8595
return __resumableChangeStreamExceptions.Contains(exception.GetType());
8696
}
8797
}
88-
98+
8999
public static bool IsRetryableReadException(Exception exception)
90100
{
91101
if (__retryableReadExceptions.Contains(exception.GetType()))

tests/MongoDB.Driver.Core.TestHelpers/CoreExceptionHelper.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public static Exception CreateException(Type exceptionType)
7575
}
7676
}
7777

78-
public static MongoCommandException CreateMongoCommandException(int code)
78+
public static MongoCommandException CreateMongoCommandException(int code = 1, string label = null)
7979
{
8080
var clusterId = new ClusterId(1);
8181
var endPoint = new DnsEndPoint("localhost", 27017);
@@ -84,7 +84,13 @@ public static MongoCommandException CreateMongoCommandException(int code)
8484
var message = "Fake MongoCommandException";
8585
var command = BsonDocument.Parse("{ command : 1 }");
8686
var result = BsonDocument.Parse($"{{ ok: 0, code : {code} }}");
87-
return new MongoCommandException(connectionId, message, command, result);
87+
var commandException = new MongoCommandException(connectionId, message, command, result);
88+
if (label != null)
89+
{
90+
commandException.AddErrorLabel(label);
91+
}
92+
93+
return commandException;
8894
}
8995
}
9096
}

tests/MongoDB.Driver.Core.Tests/Core/Operations/RetryabilityHelperTests.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ public void IsResumableChangeStreamException_should_return_expected_result_using
4545
result.Should().Be(expectedResult);
4646
}
4747

48+
[Theory]
49+
[InlineData("NonResumableChangeStreamError", false)]
50+
public void IsResumableChangeStreamException_should_return_expected_result_using_error_label(string label, bool expectedResult)
51+
{
52+
var exception = CoreExceptionHelper.CreateMongoCommandException(label: label);
53+
54+
var result = RetryabilityHelper.IsResumableChangeStreamException(exception);
55+
56+
result.Should().Be(expectedResult);
57+
}
58+
4859
[Theory]
4960
[InlineData(typeof(IOException), false)]
5061
[InlineData(typeof(MongoConnectionException), true)]

tests/MongoDB.Driver.Tests/Specifications/change-streams/ChangeStreamTestRunner.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
using FluentAssertions;
2020
using MongoDB.Bson;
2121
using MongoDB.Bson.TestHelpers.JsonDrivenTests;
22-
using MongoDB.Bson.TestHelpers.XunitExtensions;
2322
using MongoDB.Driver.Core;
2423
using MongoDB.Driver.Core.Clusters;
2524
using MongoDB.Driver.Core.Events;
@@ -258,7 +257,14 @@ private List<CommandStartedEvent> GetEvents(EventCapturer eventCapturer)
258257
private List<ChangeStreamDocument<BsonDocument>> ReadChangeStreamDocuments(IAsyncCursor<ChangeStreamDocument<BsonDocument>> cursor, BsonDocument test, bool async)
259258
{
260259
var result = new List<ChangeStreamDocument<BsonDocument>>();
261-
var expectedNumberOfDocuments = test["result"]["success"].AsBsonArray.Count;
260+
var resultDocument = test["result"].AsBsonDocument;
261+
if (!resultDocument.TryGetValue("success", out var successNode))
262+
{
263+
// skip an empty batch which is the result of an initial "aggregate" change stream request
264+
// next `MoveNext` will call a server
265+
cursor.MoveNext();
266+
}
267+
int expectedNumberOfDocuments = successNode?.AsBsonArray.Count ?? 0;
262268

263269
while (async ? cursor.MoveNextAsync().GetAwaiter().GetResult() : cursor.MoveNext())
264270
{
@@ -452,10 +458,17 @@ private void AssertError(Exception actualException, BsonDocument expectedResult)
452458
JsonDrivenHelper.EnsureAllFieldsAreValid(expectedResult, "error");
453459
var expectedError = expectedResult["error"].AsBsonDocument;
454460

455-
JsonDrivenHelper.EnsureAllFieldsAreValid((BsonDocument)expectedError, "code");
461+
JsonDrivenHelper.EnsureAllFieldsAreValid(expectedError, "code", "errorLabels");
456462
var code = expectedError["code"].ToInt32();
457-
458463
actualMongoCommandException.Code.Should().Be(code);
464+
465+
if (expectedError.TryGetValue("errorLabels", out var expectedLabels))
466+
{
467+
foreach (var expectedLabel in expectedLabels.AsBsonArray)
468+
{
469+
actualMongoCommandException.HasErrorLabel(expectedLabel.ToString()).Should().BeTrue();
470+
}
471+
}
459472
}
460473

461474
// nested types

tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/change-streams-errors.json

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,43 @@
7373
"code": 40324
7474
}
7575
}
76+
},
77+
{
78+
"description": "Change Stream should error when _id is projected out",
79+
"minServerVersion": "4.1.11",
80+
"target": "collection",
81+
"topology": [
82+
"replicaset",
83+
"sharded"
84+
],
85+
"changeStreamPipeline": [
86+
{
87+
"$project": {
88+
"_id": 0
89+
}
90+
}
91+
],
92+
"changeStreamOptions": {},
93+
"operations": [
94+
{
95+
"database": "change-stream-tests",
96+
"collection": "test",
97+
"name": "insertOne",
98+
"arguments": {
99+
"document": {
100+
"z": 3
101+
}
102+
}
103+
}
104+
],
105+
"result": {
106+
"error": {
107+
"code": 280,
108+
"errorLabels": [
109+
"NonResumableChangeStreamError"
110+
]
111+
}
112+
}
76113
}
77114
]
78-
}
115+
}

tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/change-streams-errors.yml

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,27 @@ tests:
5050
database_name: *database_name
5151
result:
5252
error:
53-
code: 40324
53+
code: 40324
54+
-
55+
description: Change Stream should error when _id is projected out
56+
minServerVersion: "4.1.11"
57+
target: collection
58+
topology:
59+
- replicaset
60+
- sharded
61+
changeStreamPipeline:
62+
-
63+
$project: { _id: 0 }
64+
changeStreamOptions: {}
65+
operations:
66+
-
67+
database: *database_name
68+
collection: *collection_name
69+
name: insertOne
70+
arguments:
71+
document:
72+
z: 3
73+
result:
74+
error:
75+
code: 280
76+
errorLabels: [ "NonResumableChangeStreamError" ]

0 commit comments

Comments
 (0)