Skip to content

Commit aa272de

Browse files
committed
CSHARP-1283: fixing issue where a short period of connectivity occurs and then shuts down when the replica set configuration has a different host than what was provided by the seedlist.
1 parent 43e12e7 commit aa272de

File tree

9 files changed

+98
-1
lines changed

9 files changed

+98
-1
lines changed

src/MongoDB.Driver.Core.Tests/Core/Clusters/MultiServerClusterTests.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,25 @@ public void Should_remove_a_server_that_is_no_longer_in_the_primary_host_list()
146146
description.Servers.Should().BeEquivalentTo(GetDescriptions(_firstEndPoint, _secondEndPoint));
147147
}
148148

149+
[Test]
150+
public void Should_remove_a_server_whose_canonical_end_point_does_not_match_its_provided_end_point()
151+
{
152+
var nonCanonicalEndPoint = new DnsEndPoint("wrong", 27017);
153+
_settings = _settings.With(endPoints: new[] { nonCanonicalEndPoint, _secondEndPoint });
154+
155+
var subject = CreateSubject();
156+
subject.Initialize();
157+
158+
PublishDescription(nonCanonicalEndPoint, ServerType.ReplicaSetPrimary,
159+
hosts: new[] { _firstEndPoint, _secondEndPoint },
160+
canonicalEndPoint: _firstEndPoint);
161+
162+
var description = subject.Description;
163+
description.State.Should().Be(ClusterState.Disconnected);
164+
description.Type.Should().Be(ClusterType.ReplicaSet);
165+
description.Servers.Should().BeEquivalentTo(GetDescriptions(_firstEndPoint, _secondEndPoint));
166+
}
167+
149168
[Test]
150169
public void Should_not_remove_a_server_that_is_no_longer_in_a_secondaries_host_list()
151170
{
@@ -449,7 +468,7 @@ private void PublishDisconnectedDescription(EndPoint endPoint)
449468
_serverFactory.PublishDescription(description);
450469
}
451470

452-
private void PublishDescription(EndPoint endPoint, ServerType serverType, IEnumerable<EndPoint> hosts = null, string setName = null, EndPoint primary = null, ElectionId electionId = null)
471+
private void PublishDescription(EndPoint endPoint, ServerType serverType, IEnumerable<EndPoint> hosts = null, string setName = null, EndPoint primary = null, ElectionId electionId = null, EndPoint canonicalEndPoint = null)
453472
{
454473
var current = _serverFactory.GetServerDescription(endPoint);
455474

@@ -462,6 +481,7 @@ private void PublishDescription(EndPoint endPoint, ServerType serverType, IEnume
462481
var description = current.With(
463482
averageRoundTripTime: TimeSpan.FromMilliseconds(10),
464483
replicaSetConfig: serverType.IsReplicaSetMember() ? config : null,
484+
canonicalEndPoint: canonicalEndPoint,
465485
electionId: electionId,
466486
state: ServerState.Connected,
467487
tags: null,

src/MongoDB.Driver.Core.Tests/Core/Connections/IsMasterResultTests.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using MongoDB.Bson;
2121
using MongoDB.Driver.Core.Clusters;
2222
using MongoDB.Driver.Core.Connections;
23+
using MongoDB.Driver.Core.Misc;
2324
using MongoDB.Driver.Core.Servers;
2425
using NUnit.Framework;
2526

@@ -130,6 +131,18 @@ public void MinWireVersion_should_parse_document_correctly(string json, int expe
130131
subject.MinWireVersion.Should().Be(expected);
131132
}
132133

134+
[Test]
135+
[TestCase("{ }", null)]
136+
[TestCase("{ me: 'localhost:27018' }", "localhost:27018")]
137+
public void Me_should_parse_document_correctly(string json, string expectedEndPoint)
138+
{
139+
var endPoint = expectedEndPoint == null ? (EndPoint)null : EndPointHelper.Parse(expectedEndPoint);
140+
141+
var subject = new IsMasterResult(BsonDocument.Parse(json));
142+
143+
subject.Me.Should().Be(endPoint);
144+
}
145+
133146
[Test]
134147
[TestCase("{ ok: 1, isreplicaset: true, setName: \"awesome\", ismaster: true }", ServerType.ReplicaSetGhost)]
135148
[TestCase("{ ok: 1, setName: \"awesome\", ismaster: true }", ServerType.ReplicaSetPrimary)]

src/MongoDB.Driver.Core.Tests/Core/Servers/ServerDescriptionTests.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public void Constructor_with_serverId_and_endPoint_only_should_return_disconnect
4848
{
4949
var subject = new ServerDescription(__serverId, __endPoint);
5050
subject.AverageRoundTripTime.Should().Be(TimeSpan.Zero);
51+
subject.CanonicalEndPoint.Should().BeNull();
5152
subject.ElectionId.Should().BeNull();
5253
subject.EndPoint.Should().Be(__endPoint);
5354
subject.ReplicaSetConfig.Should().BeNull();
@@ -63,6 +64,7 @@ public void Constructor_with_serverId_and_endPoint_only_should_return_disconnect
6364
public void Constructor_with_multiple_parameters_should_return_properly_initialized_instance()
6465
{
6566
var averageRoundTripTime = TimeSpan.FromSeconds(1);
67+
var canonicalEndPoint = new DnsEndPoint("localhost", 27017);
6668
var electionId = new ElectionId(ObjectId.GenerateNewId());
6769
var replicaSetConfig = new ReplicaSetConfig(
6870
new[] { new DnsEndPoint("localhost", 27017), new DnsEndPoint("localhost", 27018) },
@@ -81,13 +83,15 @@ public void Constructor_with_multiple_parameters_should_return_properly_initiali
8183
state: state,
8284
type: type,
8385
averageRoundTripTime: averageRoundTripTime,
86+
canonicalEndPoint: canonicalEndPoint,
8487
electionId: electionId,
8588
replicaSetConfig: replicaSetConfig,
8689
tags: tags,
8790
version: version,
8891
wireVersionRange: wireVersionRange);
8992

9093
subject.AverageRoundTripTime.Should().Be(TimeSpan.FromSeconds(1));
94+
subject.CanonicalEndPoint.Should().Be(canonicalEndPoint);
9195
subject.ElectionId.Should().Be(electionId);
9296
subject.EndPoint.Should().Be(__endPoint);
9397
subject.ReplicaSetConfig.Should().Be(replicaSetConfig);
@@ -98,6 +102,7 @@ public void Constructor_with_multiple_parameters_should_return_properly_initiali
98102
}
99103

100104
[TestCase("AverageRoundTripTime")]
105+
[TestCase("CanonicalEndPoint")]
101106
[TestCase("ElectionId")]
102107
[TestCase("EndPoint")]
103108
[TestCase("ReplicaSetConfig")]
@@ -110,6 +115,7 @@ public void Constructor_with_multiple_parameters_should_return_properly_initiali
110115
public void Equals_should_return_false_when_any_field_is_not_equal(string notEqualField)
111116
{
112117
var averageRoundTripTime = TimeSpan.FromSeconds(1);
118+
var canonicalEndPoint = new DnsEndPoint("localhost", 27017);
113119
var electionId = new ElectionId(ObjectId.GenerateNewId());
114120
var endPoint = new DnsEndPoint("localhost", 27017);
115121
var replicaSetConfig = new ReplicaSetConfig(
@@ -130,6 +136,7 @@ public void Equals_should_return_false_when_any_field_is_not_equal(string notEqu
130136
state: state,
131137
type: type,
132138
averageRoundTripTime: averageRoundTripTime,
139+
canonicalEndPoint: canonicalEndPoint,
133140
replicaSetConfig: replicaSetConfig,
134141
tags: tags,
135142
version: version,
@@ -138,6 +145,7 @@ public void Equals_should_return_false_when_any_field_is_not_equal(string notEqu
138145
switch (notEqualField)
139146
{
140147
case "AverageRoundTripTime": averageRoundTripTime = averageRoundTripTime.Add(TimeSpan.FromSeconds(1)); break;
148+
case "CanonicalEndPoint": canonicalEndPoint = new DnsEndPoint("localhost", 27018); break;
141149
case "ElectionId": electionId = new ElectionId(ObjectId.Empty); break;
142150
case "EndPoint": endPoint = new DnsEndPoint(endPoint.Host, endPoint.Port + 1); serverId = new ServerId(__clusterId, endPoint); break;
143151
case "ReplicaSetConfig": replicaSetConfig = new ReplicaSetConfig(replicaSetConfig.Members, "newname", replicaSetConfig.Primary, replicaSetConfig.Version); break;
@@ -155,6 +163,7 @@ public void Equals_should_return_false_when_any_field_is_not_equal(string notEqu
155163
state: state,
156164
type: type,
157165
averageRoundTripTime: averageRoundTripTime,
166+
canonicalEndPoint: canonicalEndPoint,
158167
electionId: electionId,
159168
replicaSetConfig: replicaSetConfig,
160169
tags: tags,
@@ -177,6 +186,7 @@ public void Equals_should_return_true_when_all_fields_are_equal()
177186
}
178187

179188
[TestCase("AverageRoundTripTime")]
189+
[TestCase("CanonicalEndPoint")]
180190
[TestCase("ElectionId")]
181191
[TestCase("ReplicaSetConfig")]
182192
[TestCase("Tags")]
@@ -186,6 +196,7 @@ public void Equals_should_return_true_when_all_fields_are_equal()
186196
public void WithHeartbeat_should_return_new_instance_when_a_field_is_not_equal(string notEqualField)
187197
{
188198
var averageRoundTripTime = TimeSpan.FromSeconds(1);
199+
var canonicalEndPoint = new DnsEndPoint("localhost", 27017);
189200
var electionId = new ElectionId(ObjectId.GenerateNewId());
190201
var replicaSetConfig = new ReplicaSetConfig(
191202
new[] { new DnsEndPoint("localhost", 27017), new DnsEndPoint("localhost", 27018) },
@@ -213,6 +224,7 @@ public void WithHeartbeat_should_return_new_instance_when_a_field_is_not_equal(s
213224
switch (notEqualField)
214225
{
215226
case "AverageRoundTripTime": averageRoundTripTime = averageRoundTripTime.Add(TimeSpan.FromSeconds(1)); break;
227+
case "CanonicalEndPoint": canonicalEndPoint = new DnsEndPoint("localhost", 27018); break;
216228
case "ElectionId": electionId = new ElectionId(ObjectId.Empty); break;
217229
case "ReplicaSetConfig": replicaSetConfig = new ReplicaSetConfig(replicaSetConfig.Members, "newname", replicaSetConfig.Primary, replicaSetConfig.Version); break;
218230
case "Tags": tags = new TagSet(new[] { new Tag("x", "b") }); break;
@@ -223,6 +235,7 @@ public void WithHeartbeat_should_return_new_instance_when_a_field_is_not_equal(s
223235

224236
var serverDescription2 = subject.With(
225237
averageRoundTripTime: averageRoundTripTime,
238+
canonicalEndPoint: canonicalEndPoint,
226239
replicaSetConfig: replicaSetConfig,
227240
state: ServerState.Connected,
228241
electionId: electionId,

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,12 @@
285285
<EmbeddedResource Include="Specifications\server-discovery-and-monitoring\tests\rs\sec_not_auth.yml" />
286286
<EmbeddedResource Include="Specifications\server-discovery-and-monitoring\tests\rs\unexpected_mongos.yml" />
287287
<EmbeddedResource Include="Specifications\server-discovery-and-monitoring\tests\rs\wrong_set_name.yml" />
288+
<EmbeddedResource Include="Specifications\server-discovery-and-monitoring\tests\rs\primary_mismatched_me.json" />
289+
<EmbeddedResource Include="Specifications\server-discovery-and-monitoring\tests\rs\primary_mismatched_me.yml" />
290+
<EmbeddedResource Include="Specifications\server-discovery-and-monitoring\tests\rs\primary_to_no_primary_mismatched_me.json" />
291+
<EmbeddedResource Include="Specifications\server-discovery-and-monitoring\tests\rs\primary_to_no_primary_mismatched_me.yml" />
292+
<EmbeddedResource Include="Specifications\server-discovery-and-monitoring\tests\rs\secondary_mismatched_me.json" />
293+
<EmbeddedResource Include="Specifications\server-discovery-and-monitoring\tests\rs\secondary_mismatched_me.yml" />
288294
<None Include="Specifications\server-discovery-and-monitoring\tests\sharded\multiple_mongoses.yml" />
289295
<EmbeddedResource Include="Specifications\server-discovery-and-monitoring\tests\sharded\non_mongos_removed.json" />
290296
<None Include="Specifications\server-discovery-and-monitoring\tests\sharded\non_mongos_removed.yml" />

src/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/TestRunner.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ private void ApplyResponse(BsonValue response)
7676
var description = currentDescription.With(
7777
state: isMasterResult.Wrapped.GetValue("ok", false).ToBoolean() ? ServerState.Connected : ServerState.Disconnected,
7878
type: isMasterResult.ServerType,
79+
canonicalEndPoint: isMasterResult.Me,
7980
electionId: isMasterResult.ElectionId,
8081
replicaSetConfig: isMasterResult.GetReplicaSetConfig());
8182

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,12 @@ private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDes
281281
clusterDescription = clusterDescription.WithServerDescription(args.NewServerDescription);
282282
clusterDescription = EnsureServers(clusterDescription, args.NewServerDescription);
283283

284+
if (args.NewServerDescription.CanonicalEndPoint != null &&
285+
!EndPointHelper.Equals(args.NewServerDescription.CanonicalEndPoint, args.NewServerDescription.EndPoint))
286+
{
287+
return RemoveServer(clusterDescription, args.NewServerDescription.EndPoint, "CanonicalEndPoint is different than seed list EndPoint.");
288+
}
289+
284290
if (args.NewServerDescription.Type == ServerType.ReplicaSetPrimary)
285291
{
286292
if (args.NewServerDescription.ElectionId != null)

src/MongoDB.Driver.Core/Core/Connections/IsMasterResult.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,23 @@ public int MaxMessageSize
145145
}
146146
}
147147

148+
/// <summary>
149+
/// Gets the endpoint the server is claiming it is known as.
150+
/// </summary>
151+
public EndPoint Me
152+
{
153+
get
154+
{
155+
BsonValue value;
156+
if (_wrapped.TryGetValue("me", out value))
157+
{
158+
return EndPointHelper.Parse((string)value);
159+
}
160+
161+
return null;
162+
}
163+
}
164+
148165
/// <summary>
149166
/// Gets the type of the server.
150167
/// </summary>

src/MongoDB.Driver.Core/Core/Servers/ClusterableServer.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ private async Task<bool> HeartbeatAsync(CancellationToken cancellationToken)
272272

273273
newDescription = _baseDescription.With(
274274
averageRoundTripTime: averageRoundTripTimeRounded,
275+
canonicalEndPoint: isMasterResult.Me,
275276
electionId: isMasterResult.ElectionId,
276277
maxBatchCount: isMasterResult.MaxBatchCount,
277278
maxDocumentSize: isMasterResult.MaxDocumentSize,

src/MongoDB.Driver.Core/Core/Servers/ServerDescription.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public sealed class ServerDescription : IEquatable<ServerDescription>
3535
{
3636
// fields
3737
private readonly TimeSpan _averageRoundTripTime;
38+
private readonly EndPoint _canonicalEndPoint;
3839
private readonly ElectionId _electionId;
3940
private readonly EndPoint _endPoint;
4041
private readonly Exception _heartbeatException;
@@ -57,6 +58,7 @@ public sealed class ServerDescription : IEquatable<ServerDescription>
5758
/// <param name="serverId">The server identifier.</param>
5859
/// <param name="endPoint">The end point.</param>
5960
/// <param name="averageRoundTripTime">The average round trip time.</param>
61+
/// <param name="canonicalEndPoint">The canonical end point.</param>
6062
/// <param name="electionId">The election identifier.</param>
6163
/// <param name="heartbeatException">The heartbeat exception.</param>
6264
/// <param name="maxBatchCount">The maximum batch count.</param>
@@ -74,6 +76,7 @@ public ServerDescription(
7476
ServerId serverId,
7577
EndPoint endPoint,
7678
Optional<TimeSpan> averageRoundTripTime = default(Optional<TimeSpan>),
79+
Optional<EndPoint> canonicalEndPoint = default(Optional<EndPoint>),
7780
Optional<ElectionId> electionId = default(Optional<ElectionId>),
7881
Optional<Exception> heartbeatException = default(Optional<Exception>),
7982
Optional<int> maxBatchCount = default(Optional<int>),
@@ -95,6 +98,7 @@ public ServerDescription(
9598
}
9699

97100
_averageRoundTripTime = averageRoundTripTime.WithDefault(TimeSpan.Zero);
101+
_canonicalEndPoint = canonicalEndPoint.WithDefault(null);
98102
_electionId = electionId.WithDefault(null);
99103
_endPoint = endPoint;
100104
_heartbeatException = heartbeatException.WithDefault(null);
@@ -123,6 +127,16 @@ public TimeSpan AverageRoundTripTime
123127
get { return _averageRoundTripTime; }
124128
}
125129

130+
/// <summary>
131+
/// Gets the canonical end point. This is the endpoint that the cluster knows this
132+
/// server by. Currently, it only applies to a replica set config and will match
133+
/// what is in the replica set configuration.
134+
/// </summary>
135+
public EndPoint CanonicalEndPoint
136+
{
137+
get { return _canonicalEndPoint; }
138+
}
139+
126140
/// <summary>
127141
/// Gets the election identifier.
128142
/// </summary>
@@ -291,6 +305,7 @@ public bool Equals(ServerDescription other)
291305

292306
return
293307
_averageRoundTripTime == other._averageRoundTripTime &&
308+
object.Equals(_canonicalEndPoint, other._canonicalEndPoint) &&
294309
object.Equals(_electionId, other._electionId) &&
295310
EndPointHelper.Equals(_endPoint, other._endPoint) &&
296311
object.Equals(_heartbeatException, other._heartbeatException) &&
@@ -313,6 +328,7 @@ public override int GetHashCode()
313328
// revision is ignored
314329
return new Hasher()
315330
.Hash(_averageRoundTripTime)
331+
.Hash(_canonicalEndPoint)
316332
.Hash(_electionId)
317333
.Hash(_endPoint)
318334
.Hash(_heartbeatException)
@@ -351,6 +367,7 @@ public override string ToString()
351367
/// Returns a new instance of ServerDescription with some values changed.
352368
/// </summary>
353369
/// <param name="averageRoundTripTime">The average round trip time.</param>
370+
/// <param name="canonicalEndPoint">The canonical end point.</param>
354371
/// <param name="electionId">The election identifier.</param>
355372
/// <param name="heartbeatException">The heartbeat exception.</param>
356373
/// <param name="maxBatchCount">The maximum batch count.</param>
@@ -368,6 +385,7 @@ public override string ToString()
368385
/// </returns>
369386
public ServerDescription With(
370387
Optional<TimeSpan> averageRoundTripTime = default(Optional<TimeSpan>),
388+
Optional<EndPoint> canonicalEndPoint = default(Optional<EndPoint>),
371389
Optional<ElectionId> electionId = default(Optional<ElectionId>),
372390
Optional<Exception> heartbeatException = default(Optional<Exception>),
373391
Optional<int> maxBatchCount = default(Optional<int>),
@@ -383,6 +401,7 @@ public ServerDescription With(
383401
{
384402
if (
385403
averageRoundTripTime.Replaces(_averageRoundTripTime) ||
404+
canonicalEndPoint.Replaces(_canonicalEndPoint) ||
386405
electionId.Replaces(_electionId) ||
387406
heartbeatException.Replaces(_heartbeatException) ||
388407
maxBatchCount.Replaces(_maxBatchCount) ||
@@ -400,6 +419,7 @@ public ServerDescription With(
400419
_serverId,
401420
_endPoint,
402421
averageRoundTripTime: averageRoundTripTime.WithDefault(_averageRoundTripTime),
422+
canonicalEndPoint: canonicalEndPoint.WithDefault(_canonicalEndPoint),
403423
electionId: electionId.WithDefault(_electionId),
404424
heartbeatException: heartbeatException.WithDefault(_heartbeatException),
405425
maxBatchCount: maxBatchCount.WithDefault(_maxBatchCount),

0 commit comments

Comments
 (0)