Skip to content

Commit 2a41907

Browse files
CSHARP-3061: Clarify how a driver must handle wrong set name in single topology.
1 parent 7db7b4c commit 2a41907

File tree

6 files changed

+203
-6
lines changed

6 files changed

+203
-6
lines changed

src/MongoDB.Driver.Core/Core/Clusters/SingleServerCluster.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ internal sealed class SingleServerCluster : Cluster
3131
// fields
3232
private IClusterableServer _server;
3333
private readonly InterlockedInt32 _state;
34+
private readonly string _replicaSetName;
3435

3536
private readonly Action<ClusterClosingEvent> _closingEventHandler;
3637
private readonly Action<ClusterClosedEvent> _closedEventHandler;
@@ -46,6 +47,7 @@ internal SingleServerCluster(ClusterSettings settings, IClusterableServerFactory
4647
: base(settings, serverFactory, eventSubscriber)
4748
{
4849
Ensure.IsEqualTo(settings.EndPoints.Count, 1, "settings.EndPoints.Count");
50+
_replicaSetName = settings.ReplicaSetName; // can be null
4951

5052
_state = new InterlockedInt32(State.Initial);
5153

@@ -177,6 +179,16 @@ private void ServerDescriptionChanged(object sender, ServerDescriptionChangedEve
177179
var newServerDescription = args.NewServerDescription;
178180
var newClusterDescription = Description;
179181

182+
if (_replicaSetName != null)
183+
{
184+
var replicaSetConfig = newServerDescription.ReplicaSetConfig;
185+
if (replicaSetConfig == null || replicaSetConfig.Name != _replicaSetName)
186+
{
187+
// if the replica set name does not match then the ServerType in the ServerDescription MUST be replaced with Unknown
188+
newServerDescription = newServerDescription.With(type: ServerType.Unknown);
189+
}
190+
}
191+
180192
if (newServerDescription.State == ServerState.Disconnected)
181193
{
182194
newClusterDescription = newClusterDescription.WithServerDescription(newServerDescription);

src/MongoDB.Driver.Core/Core/Configuration/ConnectionString.cs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,10 @@ public sealed class ConnectionString
6868
private string _applicationName;
6969
private string _authMechanism;
7070
private string _authSource;
71-
private ClusterConnectionMode _connect;
71+
private ClusterConnectionMode? _connect;
7272
private TimeSpan? _connectTimeout;
7373
private string _databaseName;
74+
private bool? _directConnection; // this option covers several cases from _connect. It won't be available outside of this class
7475
private bool? _fsync;
7576
private TimeSpan? _heartbeatInterval;
7677
private TimeSpan? _heartbeatTimeout;
@@ -206,7 +207,7 @@ public IReadOnlyList<CompressorConfiguration> Compressors
206207
/// </summary>
207208
public ClusterConnectionMode Connect
208209
{
209-
get { return _connect; }
210+
get { return _connect.GetValueOrDefault(); }
210211
}
211212

212213
/// <summary>
@@ -807,6 +808,12 @@ private void Parse()
807808
ExtractScheme(match);
808809
ExtractHosts(match);
809810

811+
if (_connect.HasValue && _directConnection.HasValue)
812+
{
813+
throw new MongoConfigurationException("Connect and directConnection cannot both be specified.");
814+
}
815+
_connect = GetEffectiveConnectionMode(_connect, _directConnection, _replicaSet);
816+
810817
if (_journal.HasValue && _journal.Value && _w != null && _w.Equals(0))
811818
{
812819
throw new MongoConfigurationException("This is an invalid w and journal pair.");
@@ -818,6 +825,16 @@ private void Parse()
818825
"Specifying both tlsInsecure and tlsDisableCertificateRevocationCheck is invalid.");
819826
}
820827

828+
if (_scheme == ConnectionStringScheme.MongoDBPlusSrv && _connect == ClusterConnectionMode.Direct)
829+
{
830+
throw new MongoConfigurationException("Direct connect cannot be used with SRV.");
831+
}
832+
833+
if (_hosts.Count > 1 && _connect == ClusterConnectionMode.Direct)
834+
{
835+
throw new MongoConfigurationException("Direct connect cannot be used with multiple host names.");
836+
}
837+
821838
string protectConnectionString(string connectionString)
822839
{
823840
var protectedString = Regex.Replace(connectionString, @"(?<=://)[^/]*(?=@)", "<hidden>");
@@ -859,6 +876,9 @@ private void ParseOption(string name, string value)
859876
case "connecttimeoutms":
860877
_connectTimeout = ParseTimeSpan(name, value);
861878
break;
879+
case "directconnection":
880+
_directConnection = ParseBoolean(name, value);
881+
break;
862882
case "fsync":
863883
_fsync = ParseBoolean(name, value);
864884
break;
@@ -1199,6 +1219,25 @@ private bool EnsureTlsInsecureIsValid(bool value)
11991219
return value;
12001220
}
12011221

1222+
private ClusterConnectionMode? GetEffectiveConnectionMode(ClusterConnectionMode? connect, bool? directConnection, string replicaSet)
1223+
{
1224+
if (directConnection.HasValue)
1225+
{
1226+
if (directConnection.Value)
1227+
{
1228+
return ClusterConnectionMode.Direct;
1229+
}
1230+
else
1231+
{
1232+
return replicaSet != null ? ClusterConnectionMode.ReplicaSet : ClusterConnectionMode.Automatic;
1233+
}
1234+
}
1235+
else
1236+
{
1237+
return connect;
1238+
}
1239+
}
1240+
12021241
private List<string> GetHostsFromSrvRecords(IEnumerable<SrvRecord> srvRecords)
12031242
{
12041243
var hosts = new List<string>();

tests/MongoDB.Driver.Core.Tests/Core/Clusters/SingleServerClusterTests.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
*/
1515

1616
using System;
17+
using System.Linq;
1718
using System.Net;
1819
using FluentAssertions;
19-
using MongoDB.Driver.Core.Clusters;
20+
using MongoDB.Bson.TestHelpers.XunitExtensions;
2021
using MongoDB.Driver.Core.Configuration;
2122
using MongoDB.Driver.Core.Events;
2223
using MongoDB.Driver.Core.Servers;
@@ -148,18 +149,44 @@ public void Dispose_should_dispose_of_the_server()
148149
_capturedEvents.Any().Should().BeFalse();
149150
}
150151

152+
[Theory]
153+
[ParameterAttributeData]
154+
public void ServerDescription_type_should_be_replaced_with_Unknown_when_isMaster_setName_is_different(
155+
[Values(null, "wrong")] string isMasterSetName)
156+
{
157+
_settings = _settings.With(
158+
connectionMode: ClusterConnectionMode.Direct,
159+
replicaSetName: "rs");
160+
161+
var subject = CreateSubject();
162+
subject.Initialize();
163+
_capturedEvents.Clear();
164+
165+
var replicaSetConfig = new ReplicaSetConfig(new[] { _endPoint }, name: isMasterSetName, _endPoint, 1);
166+
PublishDescription(_endPoint, ServerType.Standalone, replicaSetConfig);
167+
168+
subject.Description.Type.Should().Be(ClusterType.Unknown);
169+
var resultServers = subject.Description.Servers;
170+
resultServers.Count.Should().Be(1);
171+
resultServers.First().Type.Should().Be(ServerType.Unknown);
172+
_capturedEvents.Next().Should().BeOfType<ClusterDescriptionChangedEvent>();
173+
_capturedEvents.Any().Should().BeFalse();
174+
}
175+
176+
// private methods
151177
private SingleServerCluster CreateSubject()
152178
{
153179
return new SingleServerCluster(_settings, _mockServerFactory, _capturedEvents);
154180
}
155181

156-
private void PublishDescription(EndPoint endPoint, ServerType serverType)
182+
private void PublishDescription(EndPoint endPoint, ServerType serverType, ReplicaSetConfig replicaSetConfig = null)
157183
{
158184
var current = _mockServerFactory.GetServerDescription(endPoint);
159185

160186
var description = current.With(
161187
state: ServerState.Connected,
162-
type: serverType);
188+
type: serverType,
189+
replicaSetConfig: replicaSetConfig);
163190

164191
_mockServerFactory.PublishDescription(description);
165192
}

tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionStringTests.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using System.Threading.Tasks;
2222
using FluentAssertions;
2323
using MongoDB.Bson;
24+
using MongoDB.Bson.TestHelpers;
2425
using MongoDB.Driver.Core.Clusters;
2526
using MongoDB.Driver.Core.Compression;
2627
using Xunit;
@@ -514,6 +515,57 @@ public void When_a_database_name_is_specified(string connectionString, string db
514515
subject.DatabaseName.Should().Be(db);
515516
}
516517

518+
[Theory]
519+
[InlineData("mongodb://localhost/?directConnection=true&replicaSet=yeah", true, ClusterConnectionMode.Direct)]
520+
[InlineData("mongodb://localhost/?directConnection=true", true, ClusterConnectionMode.Direct)]
521+
[InlineData("mongodb://localhost/?directConnection=false&replicaSet=yeah", false, ClusterConnectionMode.ReplicaSet)]
522+
[InlineData("mongodb://localhost/?directConnection=false", false, ClusterConnectionMode.Automatic)]
523+
public void When_a_directConenction_is_specified(string connectionString, bool directConnection, ClusterConnectionMode connect)
524+
{
525+
var subject = new ConnectionString(connectionString);
526+
527+
subject.Connect.Should().Be(connect);
528+
subject._directConnection().Should().Be(directConnection);
529+
}
530+
531+
[Theory]
532+
[InlineData("mongodb+srv://localhost/?directConnection=false", false)]
533+
[InlineData("mongodb+srv://localhost/?directConnection=true", true)]
534+
public void When_a_directConnection_is_specified_with_a_srv_scheme(string connectionString, bool shouldThrow)
535+
{
536+
ConnectionString subject = null;
537+
var exception = Record.Exception(() => subject = new ConnectionString(connectionString));
538+
539+
if (shouldThrow)
540+
{
541+
exception.Should().BeOfType<MongoConfigurationException>();
542+
}
543+
else
544+
{
545+
exception.Should().BeNull();
546+
subject._directConnection().Should().BeFalse();
547+
}
548+
}
549+
550+
[Theory]
551+
[InlineData("mongodb://localhost1,localhost2/?directConnection=false", false)]
552+
[InlineData("mongodb://localhost1,localhost2/?directConnection=true", true)]
553+
public void When_a_directConnection_is_specified_with_multiple_hosts(string connectionString, bool shouldThrow)
554+
{
555+
ConnectionString subject = null;
556+
var exception = Record.Exception(() => subject = new ConnectionString(connectionString));
557+
558+
if (shouldThrow)
559+
{
560+
exception.Should().BeOfType<MongoConfigurationException>();
561+
}
562+
else
563+
{
564+
exception.Should().BeNull();
565+
subject._directConnection().Should().BeFalse();
566+
}
567+
}
568+
517569
[Theory]
518570
[InlineData("mongodb://localhost?fsync=true", true)]
519571
[InlineData("mongodb://localhost?fsync=false", false)]
@@ -1057,4 +1109,9 @@ public void When_calling_resolve_on_a_native_connection_string()
10571109
resolved.Should().BeSameAs(subject);
10581110
}
10591111
}
1112+
1113+
public static class ConnectionStringReflector
1114+
{
1115+
public static bool _directConnection(this ConnectionString obj) => (bool)Reflector.GetFieldValue(obj, nameof(_directConnection));
1116+
}
10601117
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"description": "Direct connection to RSPrimary with wrong set name",
3+
"uri": "mongodb://a/?directConnection=true&replicaSet=rs",
4+
"phases": [
5+
{
6+
"responses": [
7+
[
8+
"a:27017",
9+
{
10+
"ok": 1,
11+
"ismaster": true,
12+
"hosts": [
13+
"a:27017",
14+
"b:27017"
15+
],
16+
"setName": "wrong",
17+
"minWireVersion": 0,
18+
"maxWireVersion": 6
19+
}
20+
]
21+
],
22+
"outcome": {
23+
"servers": {
24+
"a:27017": {
25+
"type": "Unknown"
26+
}
27+
},
28+
"topologyType": "Single",
29+
"logicalSessionTimeoutMinutes": null,
30+
"setName": "rs"
31+
}
32+
},
33+
{
34+
"responses": [
35+
[
36+
"a:27017",
37+
{
38+
"ok": 1,
39+
"ismaster": true,
40+
"hosts": [
41+
"a:27017",
42+
"b:27017"
43+
],
44+
"setName": "rs",
45+
"minWireVersion": 0,
46+
"maxWireVersion": 6
47+
}
48+
]
49+
],
50+
"outcome": {
51+
"servers": {
52+
"a:27017": {
53+
"type": "RSPrimary",
54+
"setName": "rs"
55+
}
56+
},
57+
"topologyType": "Single",
58+
"logicalSessionTimeoutMinutes": null,
59+
"setName": "rs"
60+
}
61+
}
62+
]
63+
}

tests/MongoDB.Driver.Tests/MongoClientSettingsTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,6 @@ public void TestFromUrlWithMongoDBX509_without_username()
493493
}
494494

495495
[Theory]
496-
[InlineData("mongodb+srv://username:[email protected]/?connect=direct", ConnectionStringScheme.MongoDB, "localhost.test.build.10gen.cc:27017")]
497496
[InlineData("mongodb+srv://username:[email protected]/?connect=standalone", ConnectionStringScheme.MongoDB, "localhost.test.build.10gen.cc:27017")]
498497
[InlineData("mongodb+srv://username:[email protected]/?connect=automatic", ConnectionStringScheme.MongoDBPlusSrv, "test5.test.build.10gen.cc:53")]
499498
[InlineData("mongodb+srv://username:[email protected]/?connect=replicaset", ConnectionStringScheme.MongoDBPlusSrv, "test5.test.build.10gen.cc:53")]

0 commit comments

Comments
 (0)