Skip to content

Commit c2f679a

Browse files
committed
Remove from the cluster description a replica set member whose "me" hostname:port does not match the one that was used to connect to it.
JAVA-1815
1 parent f399388 commit c2f679a

File tree

6 files changed

+90
-6
lines changed

6 files changed

+90
-6
lines changed

src/main/com/mongodb/MultiServerCluster.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,11 @@ private boolean handleReplicaSetMemberChanged(final ServerDescription newDescrip
187187

188188
ensureServers(newDescription);
189189

190+
if (newDescription.getCanonicalAddress() != null && !newDescription.getAddress().sameHost(newDescription.getCanonicalAddress())) {
191+
removeServer(newDescription.getAddress());
192+
return true;
193+
}
194+
190195
if (newDescription.isPrimary()) {
191196
if (newDescription.getElectionId() != null) {
192197
if (maxElectionId != null && maxElectionId.compareTo(newDescription.getElectionId()) > 0) {
@@ -239,7 +244,10 @@ private void addServer(final ServerAddress serverAddress) {
239244
}
240245

241246
private void removeServer(final ServerAddress serverAddress) {
242-
addressToServerTupleMap.remove(serverAddress).server.close();
247+
ServerTuple removed = addressToServerTupleMap.remove(serverAddress);
248+
if (removed != null) {
249+
removed.server.close();
250+
}
243251
}
244252

245253
private void invalidateOldPrimaries(final ServerAddress newPrimary) {

src/main/com/mongodb/ServerDescription.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class ServerDescription {
5454
private final ServerAddress address;
5555

5656
private final ServerType type;
57+
private final String canonicalAddress;
5758
private final Set<String> hosts;
5859
private final Set<String> passives;
5960
private final Set<String> arbiters;
@@ -74,11 +75,12 @@ class ServerDescription {
7475

7576
private final ObjectId electionId;
7677

77-
private Throwable exception;
78+
private final Throwable exception;
7879

7980
static class Builder {
8081
private ServerAddress address;
8182
private ServerType type = Unknown;
83+
private String canonicalAddress;
8284
private Set<String> hosts = Collections.emptySet();
8385
private Set<String> passives = Collections.emptySet();
8486
private Set<String> arbiters = Collections.emptySet();
@@ -108,6 +110,11 @@ public Builder type(final ServerType type) {
108110
return this;
109111
}
110112

113+
public Builder canonicalAddress(final String canonicalAddress) {
114+
this.canonicalAddress = canonicalAddress;
115+
return this;
116+
}
117+
111118
public Builder hosts(final Set<String> hosts) {
112119
this.hosts = hosts == null ? Collections.<String>emptySet() : Collections.unmodifiableSet(new HashSet<String>(hosts));
113120
return this;
@@ -277,6 +284,16 @@ public boolean isSecondary() {
277284
return ok && (type == ReplicaSetSecondary || type == ShardRouter || type == StandAlone);
278285
}
279286

287+
/**
288+
* Gets the string representing the host name and port that this member of a replica set was configured with,
289+
* e.g. {@code "somehost:27019"}. This is typically derived from the "me" field from the "isMaster" command response.
290+
*
291+
* @return the host name and port that this replica set member is configured with.
292+
*/
293+
public String getCanonicalAddress() {
294+
return canonicalAddress;
295+
}
296+
280297
public Set<String> getHosts() {
281298
return hosts;
282299
}
@@ -411,6 +428,9 @@ public boolean equals(final Object o) {
411428
if (!address.equals(that.address)) {
412429
return false;
413430
}
431+
if (canonicalAddress != null ? !canonicalAddress.equals(that.canonicalAddress) : that.canonicalAddress != null) {
432+
return false;
433+
}
414434
if (!arbiters.equals(that.arbiters)) {
415435
return false;
416436
}
@@ -469,6 +489,7 @@ public boolean equals(final Object o) {
469489
public int hashCode() {
470490
int result = address.hashCode();
471491
result = 31 * result + type.hashCode();
492+
result = 31 * result + (canonicalAddress != null ? canonicalAddress.hashCode() : 0);
472493
result = 31 * result + hosts.hashCode();
473494
result = 31 * result + passives.hashCode();
474495
result = 31 * result + arbiters.hashCode();
@@ -494,6 +515,7 @@ public String toString() {
494515
return "ServerDescription{"
495516
+ "address=" + address
496517
+ ", type=" + type
518+
+ ", canonicalAddress=" + canonicalAddress
497519
+ ", hosts=" + hosts
498520
+ ", passives=" + passives
499521
+ ", arbiters=" + arbiters
@@ -550,6 +572,7 @@ private String getAverageLatencyFormattedInMilliseconds() {
550572
type = notNull("type", builder.type);
551573
state = notNull("state", builder.state);
552574
version = notNull("version", builder.version);
575+
canonicalAddress = builder.canonicalAddress;
553576
hosts = builder.hosts;
554577
passives = builder.passives;
555578
arbiters = builder.arbiters;

src/main/com/mongodb/ServerMonitor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ private ServerDescription createDescription(final CommandResult commandResult, f
245245
.version(serverVersion)
246246
.address(commandResult.getServerUsed())
247247
.type(getServerType(commandResult))
248+
.canonicalAddress(commandResult.getString("me"))
248249
.hosts(listToSet((List<String>) commandResult.get("hosts")))
249250
.passives(listToSet((List<String>) commandResult.get("passives")))
250251
.arbiters(listToSet((List<String>) commandResult.get("arbiters")))

src/test/com/mongodb/MultiServerClusterSpecification.groovy

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,34 @@ class MultiServerClusterSpecification extends Specification {
115115
getClusterDescription(cluster).all == getServerDescriptions(firstServer, secondServer, thirdServer)
116116
}
117117

118+
def 'should remove a secondary server whose reported host name does not match the address connected to'() {
119+
given:
120+
def seedListAddress = new ServerAddress('127.0.0.1:27017')
121+
def cluster = new MultiServerCluster(CLUSTER_ID,
122+
ClusterSettings.builder().hosts([seedListAddress]).build(), factory,
123+
CLUSTER_LISTENER);
124+
125+
when:
126+
sendNotification(seedListAddress, ReplicaSetSecondary, [firstServer, secondServer], 'test', firstServer)
127+
128+
then:
129+
getClusterDescription(cluster).all == getServerDescriptions(firstServer, secondServer)
130+
}
131+
132+
def 'should remove a primary server whose reported host name does not match the address connected to'() {
133+
given:
134+
def seedListAddress = new ServerAddress('127.0.0.1:27017')
135+
def cluster = new MultiServerCluster(CLUSTER_ID,
136+
ClusterSettings.builder().hosts([seedListAddress]).build(), factory,
137+
CLUSTER_LISTENER);
138+
139+
when:
140+
sendNotification(seedListAddress, ReplicaSetPrimary, [firstServer, secondServer], 'test', firstServer)
141+
142+
then:
143+
getClusterDescription(cluster).all == getServerDescriptions(firstServer, secondServer)
144+
}
145+
118146
def 'should remove a server when it no longer appears in hosts reported by the primary'() {
119147
given:
120148
def cluster = new MultiServerCluster(CLUSTER_ID,
@@ -480,19 +508,25 @@ class MultiServerClusterSpecification extends Specification {
480508
sendNotification(serverAddress, serverType, hosts, [], setName)
481509
}
482510

511+
def sendNotification(ServerAddress serverAddress, ServerType serverType, List<ServerAddress> hosts, String setName,
512+
ServerAddress trueAddress) {
513+
factory.getServer(serverAddress).sendNotification(getBuilder(serverAddress, serverType, hosts, [], true, setName, null, trueAddress)
514+
.build())
515+
}
516+
483517
def sendNotification(ServerAddress serverAddress, ServerType serverType, List<ServerAddress> hosts, List<ServerAddress> passives,
484518
String setName) {
485-
factory.getServer(serverAddress).sendNotification(getBuilder(serverAddress, serverType, hosts, passives, true, setName, null)
519+
factory.getServer(serverAddress).sendNotification(getBuilder(serverAddress, serverType, hosts, passives, true, setName, null, null)
486520
.build())
487521
}
488522

489523
def sendNotification(ServerAddress serverAddress, ServerType serverType, List<ServerAddress> hosts, ObjectId electionId) {
490-
factory.getServer(serverAddress).sendNotification(getBuilder(serverAddress, serverType, hosts, [], true, 'test', electionId)
524+
factory.getServer(serverAddress).sendNotification(getBuilder(serverAddress, serverType, hosts, [], true, 'test', electionId, null)
491525
.build())
492526
}
493527

494528
def sendNotification(ServerAddress serverAddress, ServerType serverType, List<ServerAddress> hosts, boolean ok) {
495-
factory.getServer(serverAddress).sendNotification(getBuilder(serverAddress, serverType, hosts, [], ok, null, null).build())
529+
factory.getServer(serverAddress).sendNotification(getBuilder(serverAddress, serverType, hosts, [], ok, null, null, null).build())
496530
}
497531

498532
def getClusterDescription(MultiServerCluster cluster) {
@@ -508,10 +542,11 @@ class MultiServerClusterSpecification extends Specification {
508542
}
509543

510544
def getBuilder(ServerAddress serverAddress, ServerType serverType, List<ServerAddress> hosts, List<ServerAddress> passives, boolean ok,
511-
String setName, ObjectId electionId) {
545+
String setName, ObjectId electionId, ServerAddress trueAddress) {
512546
ServerDescription.builder()
513547
.address(serverAddress)
514548
.type(serverType)
549+
.canonicalAddress(trueAddress == null ? serverAddress.toString() : trueAddress.toString())
515550
.ok(ok)
516551
.state(Connected)
517552
.hosts(hosts*.toString() as Set)

src/test/com/mongodb/ServerDescriptionTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public void testDefaults() throws UnknownHostException {
8080
assertNull(serverDescription.getPrimary());
8181
assertEquals(Collections.<String>emptySet(), serverDescription.getHosts());
8282
assertEquals(new TagSet(), serverDescription.getTagSet());
83+
assertNull(serverDescription.getCanonicalAddress());
8384
assertEquals(Collections.<String>emptySet(), serverDescription.getHosts());
8485
assertEquals(Collections.<String>emptySet(), serverDescription.getPassives());
8586
assertNull(serverDescription.getSetName());
@@ -103,6 +104,7 @@ public void testBuilder() throws UnknownHostException {
103104
.maxWriteBatchSize(1024)
104105
.averageLatency(50000, java.util.concurrent.TimeUnit.NANOSECONDS)
105106
.primary("localhost:27017")
107+
.canonicalAddress("localhost:27018")
106108
.hosts(new HashSet<String>(asList("localhost:27017",
107109
"localhost:27018",
108110
"localhost:27019",
@@ -138,6 +140,7 @@ public void testBuilder() throws UnknownHostException {
138140
assertEquals(1024, serverDescription.getMaxWriteBatchSize());
139141

140142
assertEquals("localhost:27017", serverDescription.getPrimary());
143+
assertEquals("localhost:27018", serverDescription.getCanonicalAddress());
141144
assertEquals(new HashSet<String>(asList("localhost:27017", "localhost:27018", "localhost:27019", "localhost:27020")),
142145
serverDescription.getHosts());
143146
assertEquals(new TagSet(new Tag("dc", "ny")), serverDescription.getTagSet());
@@ -163,6 +166,7 @@ public void testObjectOverrides() throws UnknownHostException {
163166
.maxWriteBatchSize(1024)
164167
.averageLatency(50000, java.util.concurrent.TimeUnit.NANOSECONDS)
165168
.primary("localhost:27017")
169+
.canonicalAddress("localhost:27017")
166170
.hosts(new HashSet<String>(asList("localhost:27017",
167171
"localhost:27018")))
168172
.passives(new HashSet<String>(asList("localhost:27019")))

src/test/com/mongodb/ServerMonitorSpecification.groovy

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ class ServerMonitorSpecification extends FunctionalSpecification {
3232
newDescription.version == expectedVersion
3333
}
3434

35+
def 'should set canonicalAddress'() {
36+
given:
37+
initializeServerMonitor(new ServerAddress())
38+
CommandResult commandResult = database.command(new BasicDBObject('ismaster', 1))
39+
def expectedCanonicalAddress = commandResult.getString('me')
40+
41+
when:
42+
latch.await()
43+
44+
then:
45+
newDescription.canonicalAddress == expectedCanonicalAddress
46+
}
47+
3548
@IgnoreIf( { !serverIsAtLeastVersion(2.6) } )
3649
def 'should set max wire batch size when provided by server'() {
3750
given:

0 commit comments

Comments
 (0)