summaryrefslogtreecommitdiff
path: root/src/mongo/client
diff options
context:
space:
mode:
authorMatthew Saltz <matthew.saltz@mongodb.com>2020-11-18 19:52:24 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-11-20 21:57:34 +0000
commit49cc63d73eddfa176f9c2e46792b3d97242b4839 (patch)
treede68a44637b5c9426708f942f12bc27f6bcae8a4 /src/mongo/client
parentdfa09cc55991094e4c7f4ee7442b2b41b4ff233d (diff)
downloadmongo-49cc63d73eddfa176f9c2e46792b3d97242b4839.tar.gz
SERVER-51680 Remove non-test usages of ScanningReplicaSetMonitor from the code base
Diffstat (limited to 'src/mongo/client')
-rw-r--r--src/mongo/client/README.md8
-rw-r--r--src/mongo/client/SConscript5
-rw-r--r--src/mongo/client/replica_set_monitor_integration_test.cpp17
-rw-r--r--src/mongo/client/scanning_replica_set_monitor_internal_test.cpp516
-rw-r--r--src/mongo/client/scanning_replica_set_monitor_read_preference_test.cpp1170
-rw-r--r--src/mongo/client/scanning_replica_set_monitor_scan_test.cpp1816
-rw-r--r--src/mongo/client/scanning_replica_set_monitor_test_concurrent.cpp431
-rw-r--r--src/mongo/client/scanning_replica_set_monitor_test_fixture.cpp56
-rw-r--r--src/mongo/client/scanning_replica_set_monitor_test_fixture.h88
9 files changed, 3 insertions, 4104 deletions
diff --git a/src/mongo/client/README.md b/src/mongo/client/README.md
index 5a98d0f7886..a09a528e406 100644
--- a/src/mongo/client/README.md
+++ b/src/mongo/client/README.md
@@ -3,13 +3,11 @@
## Replica set monitoring and host targeting
The internal client driver responsible for routing a command request to a replica set must determine which member to target. Host targeting involves finding which nodes in a topology satisfy the $readPreference. Node eligibility depends on the type of a node (i.e primary, secondary, etc.) and its average network latency round-trip-time (RTT). For example, { $readPreference: secondary } requires the client to know which nodes are secondaries and, of those nodes, which nodes fit within a delta of the node with the smallest RTT. A FailedToSatisfyReadPreference error occurs when there is a host selection time out and no eligible nodes are found.
-Nodes in a topology are discovered and monitored through replica set monitoring. Replica set monitoring entails periodically refreshing the local view of topologies for which the client needs to perform targeting. The client has a ReplicaSetMonitor for each replica set it needs to target in the cluster. So, if a mongos needs to target 2 shards for a query, it either has or creates a ReplicaSetMonitor for each of the the corresponding shards.
+Nodes in a topology are discovered and monitored through replica set monitoring. Replica set monitoring entails periodically refreshing the local view of topologies for which the client needs to perform targeting. The client has a ReplicaSetMonitor for each replica set it needs to target in the cluster. So, if a mongos needs to target 2 shards for a query, it either has or creates a ReplicaSetMonitor for each of the corresponding shards.
-The ReplicaSetMonitorInterface supports two replica set monitoring protocols. The older "scanning" protocol involves regularly performing sequential scans over the nodes in a topology, waiting some period between each scan. A scan re-checks each node by calling isMaster. Through the isMaster responses, the client's perception of the topology is refreshed. The "scanning" protocol is off by default but remains implemented through the ScanningReplicaSetMonitor.
+The ReplicaSetMonitorInterface supports the replica set monitoring protocol. The replica set monitoring protocol supports the "awaitable isMaster" command feature and abides by the Server Discovery and Monitoring (SDAM) specifications. The "awaitable isMaster" command feature allows the isMaster command to wait for a significant topology change or timeout before replying. Two different versions of the protocol are supported - "sdam", which does not support awaitable isMaster with exhaust, and "streamable", which does support exhaust and is on by default. Clients who enable the awaitable isMaster (with or without exhaust) will learn much sooner about stepdowns, elections, reconfigs, and other events.
-The new replica set monitoring protocol supports the "awaitable isMaster" command feature and abides by the Server Discovery and Monitoring (SDAM) specifications. The "awaitable isMaster" command feature allows the isMaster command to wait for a significant topology change or timeout before replying. Two different versions of the new protocol are supported - "sdam", which does not support awaitable isMaster with exhaust, and "streamable", which does support exhaust and is on by default. Clients who enable the awaitable isMaster (with or without exhaust) will learn much sooner about stepdowns, elections, reconfigs, and other events.
-
-In the new protocol, the StreamableReplicaSetMonitor is used to gather and maintain information regarding the client's local topology description. The topology description holds the learned states of each member in the replica set. Since the new protocol supports exhaust, the RTT is measured by sending a 'ping' to each node in the topology at a fixed frequency rather than through the isMaster response latency. Aside from the RTT, the remaining information for satisfying read preferences is gathered through awaitable isMaster commands asynchronously sent to each node in the topology.
+In the streamable protocol, the StreamableReplicaSetMonitor is used to gather and maintain information regarding the client's local topology description. The topology description holds the learned states of each member in the replica set. Since the new protocol supports exhaust, the RTT is measured by sending a 'ping' to each node in the topology at a fixed frequency rather than through the isMaster response latency. Aside from the RTT, the remaining information for satisfying read preferences is gathered through awaitable isMaster commands asynchronously sent to each node in the topology.
#### Code references
diff --git a/src/mongo/client/SConscript b/src/mongo/client/SConscript
index 94485e71e95..aad9c82b4d7 100644
--- a/src/mongo/client/SConscript
+++ b/src/mongo/client/SConscript
@@ -351,11 +351,6 @@ env.CppUnitTest(
'read_preference_test.cpp',
'remote_command_retry_scheduler_test.cpp',
'replica_set_monitor_server_parameters_test.cpp',
- 'scanning_replica_set_monitor_internal_test.cpp',
- 'scanning_replica_set_monitor_read_preference_test.cpp',
- 'scanning_replica_set_monitor_scan_test.cpp',
- 'scanning_replica_set_monitor_test_concurrent.cpp',
- 'scanning_replica_set_monitor_test_fixture.cpp',
'server_discovery_monitor_expedited_test.cpp',
'server_discovery_monitor_test.cpp',
'server_ping_monitor_test.cpp',
diff --git a/src/mongo/client/replica_set_monitor_integration_test.cpp b/src/mongo/client/replica_set_monitor_integration_test.cpp
index 318e37cd8dc..fc3b40168f2 100644
--- a/src/mongo/client/replica_set_monitor_integration_test.cpp
+++ b/src/mongo/client/replica_set_monitor_integration_test.cpp
@@ -29,8 +29,6 @@
#include "mongo/platform/basic.h"
#include "mongo/client/replica_set_monitor_manager.h"
-#include "mongo/client/scanning_replica_set_monitor.h"
-#include "mongo/client/scanning_replica_set_monitor_internal.h"
#include "mongo/client/streamable_replica_set_monitor.h"
#include "mongo/db/wire_version.h"
#include "mongo/executor/network_interface_factory.h"
@@ -164,21 +162,6 @@ TEST_F(ReplicaSetMonitorFixture, StreamableRSMWireVersion) {
ASSERT_EQ(rsm->getMaxWireVersion(), WireVersion::LATEST_WIRE_VERSION);
}
-TEST_F(ReplicaSetMonitorFixture, ScanningRSMWireVersion) {
- auto state = std::make_shared<ScanningReplicaSetMonitor::SetState>(
- replSetUri, &notifier, executor.get());
- auto rsm = std::make_shared<ScanningReplicaSetMonitor>(state);
-
- // Schedule isMaster requests and wait for the responses.
- rsm->init();
- auto primaryFuture =
- rsm->getHostOrRefresh(ReadPreferenceSetting(mongo::ReadPreference::PrimaryOnly));
- primaryFuture.get();
-
- ASSERT_EQ(rsm->getMinWireVersion(), WireVersion::LATEST_WIRE_VERSION);
- ASSERT_EQ(rsm->getMaxWireVersion(), WireVersion::LATEST_WIRE_VERSION);
-}
-
} // namespace
} // namespace executor
} // namespace mongo
diff --git a/src/mongo/client/scanning_replica_set_monitor_internal_test.cpp b/src/mongo/client/scanning_replica_set_monitor_internal_test.cpp
deleted file mode 100644
index 766558de2f5..00000000000
--- a/src/mongo/client/scanning_replica_set_monitor_internal_test.cpp
+++ /dev/null
@@ -1,516 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/client/scanning_replica_set_monitor_test_fixture.h"
-
-#include "mongo/client/mongo_uri.h"
-
-namespace mongo {
-namespace {
-
-// -- SetState Construction --
-using InitialStateTest = ScanningReplicaSetMonitorTest;
-
-TEST_F(InitialStateTest, InitialStateMongoURI) {
- auto uri = MongoURI::parse("mongodb://a,b,c/?replicaSet=name");
- ASSERT_OK(uri.getStatus());
- auto state = makeState(uri.getValue());
- ASSERT_EQUALS(state->name, "name");
- ASSERT(state->seedNodes == basicSeedsSet);
- ASSERT(state->lastSeenMaster.empty());
- ASSERT_EQUALS(state->nodes.size(), basicSeeds.size());
- for (size_t i = 0; i < basicSeeds.size(); i++) {
- auto node = state->findNode(basicSeeds[i]);
- ASSERT(node);
- ASSERT_EQUALS(node->host.toString(), basicSeeds[i].toString());
- ASSERT(!node->isUp);
- ASSERT(!node->isMaster);
- ASSERT(node->tags.isEmpty());
- }
-}
-
-// -- Node operations --
-class NodeTest : public ScanningReplicaSetMonitorTest {
-public:
- bool isCompatible(const Node& node, ReadPreference pref, const TagSet& tagSet) {
- auto connStr = ConnectionString::forReplicaSet(kSetName, {node.host});
- auto set = makeState(MongoURI(connStr));
- set->nodes.push_back(node);
-
- ReadPreferenceSetting criteria(pref, tagSet);
- return !set->getMatchingHost(criteria).empty();
- }
-
- const BSONObj SampleIsMasterDoc = BSON("tags" << BSON("dc"
- << "NYC"
- << "p"
- << "2"
- << "region"
- << "NA"));
- const BSONObj SampleTags = SampleIsMasterDoc["tags"].Obj();
- const BSONObj NoTags = BSONObj();
- const BSONObj NoTagIsMasterDoc = BSON("isMaster" << true);
-};
-
-
-TEST_F(NodeTest, SimpleGoodMatch) {
- Node node(((HostAndPort())));
- node.tags = BSON("dc"
- << "sf");
- ASSERT(node.matches(BSON("dc"
- << "sf")));
-}
-
-TEST_F(NodeTest, SimpleBadMatch) {
- Node node((HostAndPort()));
- node.tags = BSON("dc"
- << "nyc");
- ASSERT(!node.matches(BSON("dc"
- << "sf")));
-}
-
-TEST_F(NodeTest, ExactMatch) {
- Node node((HostAndPort()));
- node.tags = SampleTags;
- ASSERT(node.matches(SampleIsMasterDoc["tags"].Obj()));
-}
-
-TEST_F(NodeTest, EmptyTag) {
- Node node((HostAndPort()));
- node.tags = SampleTags;
- ASSERT(node.matches(BSONObj()));
-}
-
-TEST_F(NodeTest, MemberNoTagMatchesEmptyTag) {
- Node node((HostAndPort()));
- node.tags = NoTags;
- ASSERT(node.matches(BSONObj()));
-}
-
-TEST_F(NodeTest, MemberNoTagDoesNotMatch) {
- Node node((HostAndPort()));
- node.tags = NoTags;
- ASSERT(!node.matches(BSON("dc"
- << "NYC")));
-}
-
-TEST_F(NodeTest, IncompleteMatch) {
- Node node((HostAndPort()));
- node.tags = SampleTags;
- ASSERT(!node.matches(BSON("dc"
- << "NYC"
- << "p"
- << "2"
- << "hello"
- << "world")));
-}
-
-TEST_F(NodeTest, PartialMatch) {
- Node node((HostAndPort()));
- node.tags = SampleTags;
- ASSERT(node.matches(BSON("dc"
- << "NYC"
- << "p"
- << "2")));
-}
-
-TEST_F(NodeTest, SingleTagCrit) {
- Node node((HostAndPort()));
- node.tags = SampleTags;
- ASSERT(node.matches(BSON("p"
- << "2")));
-}
-
-TEST_F(NodeTest, BadSingleTagCrit) {
- Node node((HostAndPort()));
- node.tags = SampleTags;
- ASSERT(!node.matches(BSON("dc"
- << "SF")));
-}
-
-TEST_F(NodeTest, NonExistingFieldTag) {
- Node node((HostAndPort()));
- node.tags = SampleTags;
- ASSERT(!node.matches(BSON("noSQL"
- << "Mongo")));
-}
-
-TEST_F(NodeTest, UnorederedMatching) {
- Node node((HostAndPort()));
- node.tags = SampleTags;
- ASSERT(node.matches(BSON("p"
- << "2"
- << "dc"
- << "NYC")));
-}
-
-TEST_F(NodeTest, SameValueDiffKey) {
- Node node((HostAndPort()));
- node.tags = SampleTags;
- ASSERT(!node.matches(BSON("datacenter"
- << "NYC")));
-}
-
-TEST_F(NodeTest, PriNodeCompatibleTag) {
- Node node(HostAndPort("dummy", 3));
- node.tags = SampleTags;
-
- node.isUp = true;
- node.isMaster = true;
-
- BSONArrayBuilder builder;
- builder.append(BSON("dc"
- << "NYC"));
-
- TagSet tags(BSONArray(builder.done()));
-
- ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
- ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
-}
-
-TEST_F(NodeTest, SecNodeCompatibleTag) {
- Node node(HostAndPort("dummy", 3));
- node.tags = SampleTags;
-
- node.isUp = true;
- node.isMaster = false;
-
- BSONArrayBuilder builder;
- builder.append(BSON("dc"
- << "NYC"));
-
- TagSet tags(BSONArray(builder.done()));
-
- ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
-}
-
-TEST_F(NodeTest, PriNodeNotCompatibleTag) {
- Node node(HostAndPort("dummy", 3));
- node.tags = SampleTags;
-
- node.isUp = true;
- node.isMaster = true;
-
- BSONArrayBuilder builder;
- builder.append(BSON("dc"
- << "SF"));
-
- TagSet tags(BSONArray(builder.done()));
-
- ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
- ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
- ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
-}
-
-TEST_F(NodeTest, SecNodeNotCompatibleTag) {
- Node node(HostAndPort("dummy", 3));
- node.tags = SampleTags;
-
- node.isUp = true;
- node.isMaster = false;
-
- BSONArrayBuilder builder;
- builder.append(BSON("dc"
- << "SF"));
-
- TagSet tags(BSONArray(builder.done()));
-
- ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
- ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
- ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
- ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
- ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
-}
-
-TEST_F(NodeTest, PriNodeCompatiblMultiTag) {
- Node node(HostAndPort("dummy", 3));
- node.tags = SampleTags;
-
- node.isUp = true;
- node.isMaster = true;
-
- BSONArrayBuilder builder;
- builder.append(BSON("dc"
- << "RP"));
- builder.append(BSON("dc"
- << "NYC"
- << "p"
- << "2"));
-
- TagSet tags(BSONArray(builder.done()));
-
- ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
- ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
-}
-
-TEST_F(NodeTest, SecNodeCompatibleMultiTag) {
- Node node(HostAndPort("dummy", 3));
- node.tags = SampleTags;
-
- node.isUp = true;
- node.isMaster = false;
-
- BSONArrayBuilder builder;
- builder.append(BSON("dc"
- << "RP"));
- builder.append(BSON("dc"
- << "NYC"
- << "p"
- << "2"));
-
- TagSet tags(BSONArray(builder.done()));
-
- ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
-}
-
-TEST_F(NodeTest, PriNodeNotCompatibleMultiTag) {
- Node node(HostAndPort("dummy", 3));
- node.tags = SampleTags;
-
- node.isUp = true;
- node.isMaster = true;
-
- BSONArrayBuilder builder;
- builder.append(BSON("dc"
- << "sf"));
- builder.append(BSON("dc"
- << "NYC"
- << "P"
- << "4"));
-
- TagSet tags(BSONArray(builder.done()));
-
- ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
- ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
- ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
- ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
-}
-
-TEST_F(NodeTest, SecNodeNotCompatibleMultiTag) {
- Node node(HostAndPort("dummy", 3));
- node.tags = SampleTags;
-
- node.isUp = true;
- node.isMaster = false;
-
- BSONArrayBuilder builder;
- builder.append(BSON("dc"
- << "sf"));
- builder.append(BSON("dc"
- << "NYC"
- << "P"
- << "4"));
-
- TagSet tags(BSONArray(builder.done()));
-
- ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
- ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
- ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
- ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
- ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
-}
-
-// -- IsMasterReply operations --
-using IsMasterReplyTest = ScanningReplicaSetMonitorTest;
-TEST_F(IsMasterReplyTest, IsMasterBadParse) {
- BSONObj ismaster = BSON("hosts" << BSON_ARRAY("mongo.example:badport"));
- IsMasterReply imr(HostAndPort("mongo.example:27017"), -1, ismaster);
- ASSERT_EQUALS(imr.ok, false);
-}
-
-TEST_F(IsMasterReplyTest, IsMasterReplyRSNotInitiated) {
- BSONObj ismaster = BSON(
- "ismaster" << false << "secondary" << false << "info"
- << "can't get local.system.replset config from self or any seed (EMPTYCONFIG)"
- << "isreplicaset" << true << "maxBsonObjectSize" << 16777216
- << "maxMessageSizeBytes" << 48000000 << "maxWriteBatchSize" << 1000
- << "localTime" << mongo::jsTime() << "maxWireVersion" << 2 << "minWireVersion"
- << 0 << "ok" << 1);
-
- IsMasterReply imr(HostAndPort(), -1, ismaster);
-
- ASSERT_EQUALS(imr.ok, true);
- ASSERT_EQUALS(imr.setName, "");
- ASSERT_EQUALS(imr.hidden, false);
- ASSERT_EQUALS(imr.secondary, false);
- ASSERT_EQUALS(imr.isMaster, false);
- ASSERT_EQUALS(imr.configVersion, 0);
- ASSERT(!imr.electionId.isSet());
- ASSERT(imr.primary.empty());
- ASSERT(imr.members.empty());
- ASSERT(imr.tags.isEmpty());
-}
-
-TEST_F(IsMasterReplyTest, IsMasterReplyRSPrimary) {
- BSONObj ismaster = BSON("setName"
- << "test"
- << "setVersion" << 1 << "electionId" << OID("7fffffff0000000000000001")
- << "ismaster" << true << "secondary" << false << "hosts"
- << BSON_ARRAY("mongo.example:3000") << "primary"
- << "mongo.example:3000"
- << "me"
- << "mongo.example:3000"
- << "maxBsonObjectSize" << 16777216 << "maxMessageSizeBytes" << 48000000
- << "maxWriteBatchSize" << 1000 << "localTime" << mongo::jsTime()
- << "maxWireVersion" << 2 << "minWireVersion" << 0 << "ok" << 1);
-
- IsMasterReply imr(HostAndPort("mongo.example:3000"), -1, ismaster);
-
- ASSERT_EQUALS(imr.ok, true);
- ASSERT_EQUALS(imr.host.toString(), HostAndPort("mongo.example:3000").toString());
- ASSERT_EQUALS(imr.setName, "test");
- ASSERT_EQUALS(imr.configVersion, 1);
- ASSERT_EQUALS(imr.electionId, OID("7fffffff0000000000000001"));
- ASSERT_EQUALS(imr.hidden, false);
- ASSERT_EQUALS(imr.secondary, false);
- ASSERT_EQUALS(imr.isMaster, true);
- ASSERT_EQUALS(imr.primary.toString(), HostAndPort("mongo.example:3000").toString());
- ASSERT(imr.members.count(HostAndPort("mongo.example:3000")));
- ASSERT(imr.tags.isEmpty());
-}
-
-TEST_F(IsMasterReplyTest, IsMasterReplyPassiveSecondary) {
- BSONObj ismaster = BSON("setName"
- << "test"
- << "setVersion" << 2 << "electionId" << OID("7fffffff0000000000000001")
- << "ismaster" << false << "secondary" << true << "hosts"
- << BSON_ARRAY("mongo.example:3000") << "passives"
- << BSON_ARRAY("mongo.example:3001") << "primary"
- << "mongo.example:3000"
- << "passive" << true << "me"
- << "mongo.example:3001"
- << "maxBsonObjectSize" << 16777216 << "maxMessageSizeBytes" << 48000000
- << "maxWriteBatchSize" << 1000 << "localTime" << mongo::jsTime()
- << "maxWireVersion" << 2 << "minWireVersion" << 0 << "ok" << 1);
-
- IsMasterReply imr(HostAndPort("mongo.example:3001"), -1, ismaster);
-
- ASSERT_EQUALS(imr.ok, true);
- ASSERT_EQUALS(imr.host.toString(), HostAndPort("mongo.example:3001").toString());
- ASSERT_EQUALS(imr.setName, "test");
- ASSERT_EQUALS(imr.configVersion, 2);
- ASSERT_EQUALS(imr.hidden, false);
- ASSERT_EQUALS(imr.secondary, true);
- ASSERT_EQUALS(imr.isMaster, false);
- ASSERT_EQUALS(imr.primary.toString(), HostAndPort("mongo.example:3000").toString());
- ASSERT(imr.members.count(HostAndPort("mongo.example:3000")));
- ASSERT(imr.members.count(HostAndPort("mongo.example:3001")));
- ASSERT(imr.passives.count(HostAndPort("mongo.example:3001")));
- ASSERT(imr.tags.isEmpty());
- ASSERT(!imr.electionId.isSet());
-}
-
-TEST_F(IsMasterReplyTest, IsMasterReplyHiddenSecondary) {
- BSONObj ismaster = BSON("setName"
- << "test"
- << "setVersion" << 2 << "electionId" << OID("7fffffff0000000000000001")
- << "ismaster" << false << "secondary" << true << "hosts"
- << BSON_ARRAY("mongo.example:3000") << "primary"
- << "mongo.example:3000"
- << "passive" << true << "hidden" << true << "me"
- << "mongo.example:3001"
- << "maxBsonObjectSize" << 16777216 << "maxMessageSizeBytes" << 48000000
- << "maxWriteBatchSize" << 1000 << "localTime" << mongo::jsTime()
- << "maxWireVersion" << 2 << "minWireVersion" << 0 << "ok" << 1);
-
- IsMasterReply imr(HostAndPort("mongo.example:3001"), -1, ismaster);
-
- ASSERT_EQUALS(imr.ok, true);
- ASSERT_EQUALS(imr.host.toString(), HostAndPort("mongo.example:3001").toString());
- ASSERT_EQUALS(imr.setName, "test");
- ASSERT_EQUALS(imr.configVersion, 2);
- ASSERT_EQUALS(imr.hidden, true);
- ASSERT_EQUALS(imr.secondary, true);
- ASSERT_EQUALS(imr.isMaster, false);
- ASSERT_EQUALS(imr.primary.toString(), HostAndPort("mongo.example:3000").toString());
- ASSERT(imr.members.count(HostAndPort("mongo.example:3000")));
- ASSERT(imr.tags.isEmpty());
- ASSERT(!imr.electionId.isSet());
-}
-
-TEST_F(IsMasterReplyTest, IsMasterSecondaryWithTags) {
- BSONObj ismaster = BSON("setName"
- << "test"
- << "setVersion" << 2 << "electionId" << OID("7fffffff0000000000000001")
- << "ismaster" << false << "secondary" << true << "hosts"
- << BSON_ARRAY("mongo.example:3000"
- << "mongo.example:3001")
- << "primary"
- << "mongo.example:3000"
- << "me"
- << "mongo.example:3001"
- << "maxBsonObjectSize" << 16777216 << "maxMessageSizeBytes" << 48000000
- << "maxWriteBatchSize" << 1000 << "localTime" << mongo::jsTime()
- << "maxWireVersion" << 2 << "minWireVersion" << 0 << "tags"
- << BSON("dc"
- << "nyc"
- << "use"
- << "production")
- << "ok" << 1);
-
- IsMasterReply imr(HostAndPort("mongo.example:3001"), -1, ismaster);
-
- ASSERT_EQUALS(imr.ok, true);
- ASSERT_EQUALS(imr.host.toString(), HostAndPort("mongo.example:3001").toString());
- ASSERT_EQUALS(imr.setName, "test");
- ASSERT_EQUALS(imr.configVersion, 2);
- ASSERT_EQUALS(imr.hidden, false);
- ASSERT_EQUALS(imr.secondary, true);
- ASSERT_EQUALS(imr.isMaster, false);
- ASSERT_EQUALS(imr.primary.toString(), HostAndPort("mongo.example:3000").toString());
- ASSERT(imr.members.count(HostAndPort("mongo.example:3000")));
- ASSERT(imr.members.count(HostAndPort("mongo.example:3001")));
- ASSERT(imr.tags.hasElement("dc"));
- ASSERT(imr.tags.hasElement("use"));
- ASSERT(!imr.electionId.isSet());
- ASSERT_EQUALS(imr.tags["dc"].str(), "nyc");
- ASSERT_EQUALS(imr.tags["use"].str(), "production");
-}
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/client/scanning_replica_set_monitor_read_preference_test.cpp b/src/mongo/client/scanning_replica_set_monitor_read_preference_test.cpp
deleted file mode 100644
index 5fafe64071d..00000000000
--- a/src/mongo/client/scanning_replica_set_monitor_read_preference_test.cpp
+++ /dev/null
@@ -1,1170 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/client/scanning_replica_set_monitor_test_fixture.h"
-
-#include <memory>
-
-#include "mongo/client/mongo_uri.h"
-#include "mongo/client/read_preference.h"
-
-namespace mongo {
-namespace {
-
-class ReadPrefTest : public ScanningReplicaSetMonitorTest {
-public:
- ReadPrefTest() = default;
- virtual ~ReadPrefTest() = default;
-
- HostAndPort selectNode(const SetState::Nodes& nodes,
- ReadPreference pref,
- const TagSet& tagSet,
- int latencyThresholdMillis,
- bool* isPrimarySelected) {
- invariant(!nodes.empty());
-
- auto connStr = ConnectionString::forReplicaSet(kSetName, {nodes.front().host});
- auto set = makeState(MongoURI(connStr));
- set->nodes = nodes;
- set->latencyThresholdMicros = latencyThresholdMillis * 1000;
-
- ReadPreferenceSetting criteria(pref, tagSet);
- HostAndPort out = set->getMatchingHost(criteria);
- if (isPrimarySelected && !out.empty()) {
- Node* node = set->findNode(out);
- ASSERT(node);
- *isPrimarySelected = node->isMaster;
- }
-
- return out;
- }
-
- std::vector<HostAndPort> selectNodes(const SetState::Nodes& nodes,
- ReadPreference pref,
- const TagSet& tagSet,
- int latencyThresholdMillis,
- bool* isPrimarySelected) {
- invariant(!nodes.empty());
-
- auto connStr = ConnectionString::forReplicaSet(kSetName, {nodes.front().host});
- auto set = makeState(MongoURI(connStr));
- set->nodes = nodes;
- set->latencyThresholdMicros = latencyThresholdMillis * 1000;
-
- ReadPreferenceSetting criteria(pref, tagSet);
- auto out = set->getMatchingHosts(criteria);
- if (isPrimarySelected && !out.empty()) {
- for (auto& host : out) {
- Node* node = set->findNode(host);
- ASSERT(node);
-
- if (node->isMaster) {
- *isPrimarySelected = node->isMaster;
- break;
- }
- }
- }
-
- return out;
- }
-
- auto getThreeMemberWithTags() {
- SetState::Nodes nodes;
-
- nodes.push_back(Node(HostAndPort("a")));
- nodes.push_back(Node(HostAndPort("b")));
- nodes.push_back(Node(HostAndPort("c")));
-
- nodes[0].isUp = true;
- nodes[1].isUp = true;
- nodes[2].isUp = true;
-
- nodes[0].isMaster = false;
- nodes[1].isMaster = true;
- nodes[2].isMaster = false;
-
- nodes[0].tags = BSON("dc"
- << "nyc"
- << "p"
- << "1");
- nodes[1].tags = BSON("dc"
- << "sf");
- nodes[2].tags = BSON("dc"
- << "nyc"
- << "p"
- << "2");
-
- return nodes;
- }
-
- BSONArray getDefaultTagSet() {
- BSONArrayBuilder arrayBuilder;
- arrayBuilder.append(BSONObj());
- return arrayBuilder.arr();
- }
-
- BSONArray getP2TagSet() {
- BSONArrayBuilder arrayBuilder;
- arrayBuilder.append(BSON("p"
- << "2"));
- return arrayBuilder.arr();
- }
-
- BSONArray getSingleNoMatchTag() {
- BSONArrayBuilder arrayBuilder;
- arrayBuilder.append(BSON("k"
- << "x"));
- return arrayBuilder.arr();
- }
-
- BSONArray getMultiNoMatchTag() {
- BSONArrayBuilder arrayBuilder;
- arrayBuilder.append(BSON("mongo"
- << "db"));
- arrayBuilder.append(BSON("by"
- << "10gen"));
- return arrayBuilder.arr();
- }
-};
-
-TEST_F(ReadPrefTest, PrimaryOnly) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::PrimaryOnly, tags, 3, &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- ASSERT_EQUALS("b", host.host());
-}
-
-TEST_F(ReadPrefTest, PrimaryOnlyMulti) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- bool isPrimarySelected = false;
- std::vector<HostAndPort> hosts =
- selectNodes(nodes, mongo::ReadPreference::PrimaryOnly, tags, 3, &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- ASSERT_EQUALS(hosts.size(), 1ull);
- ASSERT_EQUALS("b", hosts[0].host());
-}
-
-TEST_F(ReadPrefTest, PrimaryOnlyPriNotOk) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::PrimaryOnly, tags, 3, &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(ReadPrefTest, PrimaryMissing) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- nodes[1].isMaster = false;
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::PrimaryOnly, tags, 3, &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(ReadPrefTest, PrimaryMissingMulti) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- nodes[1].isMaster = false;
-
- bool isPrimarySelected = false;
- std::vector<HostAndPort> hosts =
- selectNodes(nodes, mongo::ReadPreference::PrimaryOnly, tags, 3, &isPrimarySelected);
-
- ASSERT(hosts.empty());
-}
-
-TEST_F(ReadPrefTest, PriPrefWithPriOk) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::PrimaryPreferred, tags, 1, &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- ASSERT_EQUALS("b", host.host());
-}
-
-TEST_F(ReadPrefTest, PriPrefWithPriNotOk) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::PrimaryPreferred, tags, 1, &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT(host.host() == "a" || host.host() == "c");
-}
-
-TEST_F(ReadPrefTest, SecOnly) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryOnly, tags, 1, &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("a", host.host());
-}
-
-TEST_F(ReadPrefTest, SecOnlyMulti) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- bool isPrimarySelected = false;
- std::vector<HostAndPort> hosts =
- selectNodes(nodes, mongo::ReadPreference::SecondaryOnly, tags, 1, &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- std::sort(hosts.begin(), hosts.end());
-
- ASSERT_EQUALS(hosts.size(), 2ull);
- ASSERT_EQUALS("a", hosts[0].host());
- ASSERT_EQUALS("c", hosts[1].host());
-}
-
-TEST_F(ReadPrefTest, SecOnlyOnlyPriOk) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
- nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryOnly, tags, 1, &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(ReadPrefTest, SecPref) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryPreferred, tags, 1, &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("a", host.host());
-}
-
-TEST_F(ReadPrefTest, SecPrefWithNoSecOk) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
- nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryPreferred, tags, 1, &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- ASSERT_EQUALS("b", host.host());
-}
-
-TEST_F(ReadPrefTest, SecPrefWithNoNodeOk) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
- nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryPreferred, tags, 1, &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(ReadPrefTest, NearestAllLocal) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- nodes[0].latencyMicros = 1 * 1000;
- nodes[1].latencyMicros = 2 * 1000;
- nodes[2].latencyMicros = 3 * 1000;
-
- bool isPrimarySelected = 0;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::Nearest, tags, 3, &isPrimarySelected);
-
- // Any host is ok
- ASSERT(!host.empty());
- ASSERT_EQUALS(isPrimarySelected, host.host() == "b");
-}
-
-TEST_F(ReadPrefTest, NearestOneLocal) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getDefaultTagSet());
-
- nodes[0].latencyMicros = 10 * 1000;
- nodes[1].latencyMicros = 20 * 1000;
- nodes[2].latencyMicros = 30 * 1000;
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::Nearest, tags, 3, &isPrimarySelected);
-
- ASSERT_EQUALS("a", host.host());
- ASSERT(!isPrimarySelected);
-}
-
-TEST_F(ReadPrefTest, PriOnlyWithTagsNoMatch) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getP2TagSet());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::PrimaryOnly, tags, 3, &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- // Note: PrimaryOnly ignores tag
- ASSERT_EQUALS("b", host.host());
-}
-
-TEST_F(ReadPrefTest, PriPrefPriNotOkWithTags) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getP2TagSet());
-
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::PrimaryPreferred, tags, 3, &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("c", host.host());
-}
-
-TEST_F(ReadPrefTest, PriPrefPriOkWithTagsNoMatch) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getSingleNoMatchTag());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::PrimaryPreferred, tags, 3, &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- ASSERT_EQUALS("b", host.host());
-}
-
-TEST_F(ReadPrefTest, PriPrefPriNotOkWithTagsNoMatch) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getSingleNoMatchTag());
-
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::PrimaryPreferred, tags, 3, &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(ReadPrefTest, SecOnlyWithTags) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getP2TagSet());
-
- bool isPrimarySelected;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryOnly, tags, 3, &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("c", host.host());
-}
-
-TEST_F(ReadPrefTest, SecOnlyWithTagsMatchOnlyPri) {
- auto nodes = getThreeMemberWithTags();
-
- BSONArrayBuilder arrayBuilder;
- arrayBuilder.append(BSON("dc"
- << "sf"));
- TagSet tags(arrayBuilder.arr());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryOnly, tags, 3, &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(ReadPrefTest, SecPrefWithTags) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getP2TagSet());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryPreferred, tags, 3, &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("c", host.host());
-}
-
-TEST_F(ReadPrefTest, SecPrefSecNotOkWithTags) {
- auto nodes = getThreeMemberWithTags();
-
- BSONArrayBuilder arrayBuilder;
- arrayBuilder.append(BSON("dc"
- << "nyc"));
- TagSet tags(arrayBuilder.arr());
-
- nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryPreferred, tags, 3, &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("a", host.host());
-}
-
-TEST_F(ReadPrefTest, SecPrefPriOkWithTagsNoMatch) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getSingleNoMatchTag());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryPreferred, tags, 3, &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- ASSERT_EQUALS("b", host.host());
-}
-
-TEST_F(ReadPrefTest, SecPrefPriNotOkWithTagsNoMatch) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getSingleNoMatchTag());
-
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryPreferred, tags, 3, &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(ReadPrefTest, SecPrefPriOkWithSecNotMatchTag) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getSingleNoMatchTag());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryPreferred, tags, 3, &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- ASSERT_EQUALS("b", host.host());
-}
-
-TEST_F(ReadPrefTest, NearestWithTags) {
- auto nodes = getThreeMemberWithTags();
-
- BSONArrayBuilder arrayBuilder;
- arrayBuilder.append(BSON("p"
- << "1"));
- TagSet tags(arrayBuilder.arr());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::Nearest, tags, 3, &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("a", host.host());
-}
-
-TEST_F(ReadPrefTest, NearestWithTagsNoMatch) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getSingleNoMatchTag());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::Nearest, tags, 3, &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(ReadPrefTest, MultiPriOnlyTag) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getMultiNoMatchTag());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::PrimaryOnly, tags, 3, &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- ASSERT_EQUALS("b", host.host());
-}
-
-TEST_F(ReadPrefTest, MultiPriOnlyPriNotOkTag) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getMultiNoMatchTag());
-
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::PrimaryOnly, tags, 3, &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(ReadPrefTest, PriPrefPriOk) {
- auto nodes = getThreeMemberWithTags();
-
- BSONArrayBuilder arrayBuilder;
- arrayBuilder.append(BSON("p"
- << "1"));
- arrayBuilder.append(BSON("p"
- << "2"));
-
- TagSet tags(arrayBuilder.arr());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::PrimaryPreferred, tags, 3, &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- ASSERT_EQUALS("b", host.host());
-}
-
-class MultiTagsTest : public ReadPrefTest {
-public:
- MultiTagsTest() = default;
- virtual ~MultiTagsTest() = default;
-
- const TagSet& getMatchesFirstTagSet() {
- if (matchFirstTags.get() != nullptr) {
- return *matchFirstTags;
- }
-
- BSONArrayBuilder arrayBuilder;
- arrayBuilder.append(BSON("p"
- << "1"));
- arrayBuilder.append(BSON("p"
- << "2"));
- matchFirstTags.reset(new TagSet(arrayBuilder.arr()));
-
- return *matchFirstTags;
- }
-
- const TagSet& getMatchesSecondTagSet() {
- if (matchSecondTags.get() != nullptr) {
- return *matchSecondTags;
- }
-
- BSONArrayBuilder arrayBuilder;
- arrayBuilder.append(BSON("p"
- << "3"));
- arrayBuilder.append(BSON("p"
- << "2"));
- arrayBuilder.append(BSON("p"
- << "1"));
- matchSecondTags.reset(new TagSet(arrayBuilder.arr()));
-
- return *matchSecondTags;
- }
-
- const TagSet& getMatchesOnlyFirstTagSet() {
- if (matchOnlyFirstTag.get() != nullptr) {
- return *matchOnlyFirstTag;
- }
-
- BSONArrayBuilder arrayBuilder;
- arrayBuilder.append(BSON("p"
- << "12"));
- arrayBuilder.append(BSON("p"
- << "23"));
- arrayBuilder.append(BSON("p"
- << "19"));
- arrayBuilder.append(BSON("p"
- << "34"));
- arrayBuilder.append(BSON("p"
- << "1"));
- matchOnlyFirstTag.reset(new TagSet(arrayBuilder.arr()));
-
- return *matchOnlyFirstTag;
- }
-
- const TagSet& getMatchesPriTagSet() {
- if (matchPriTags.get() != nullptr) {
- return *matchPriTags;
- }
-
- BSONArrayBuilder arrayBuilder;
- arrayBuilder.append(BSON("dc"
- << "sf"));
- arrayBuilder.append(BSON("p"
- << "1"));
- matchPriTags.reset(new TagSet(arrayBuilder.arr()));
-
- return *matchPriTags;
- }
-
- const stdx::unordered_set<std::string> vectorToSet(std::vector<HostAndPort> hosts) {
- stdx::unordered_set<std::string> matchSet;
- std::transform(hosts.begin(),
- hosts.end(),
- std::inserter(matchSet, matchSet.begin()),
- [](const auto& node) { return node.host(); });
- return matchSet;
- }
-
-private:
- std::unique_ptr<TagSet> matchFirstTags;
- std::unique_ptr<TagSet> matchSecondTags;
- std::unique_ptr<TagSet> matchOnlyFirstTag;
- std::unique_ptr<TagSet> matchPriTags;
-};
-
-TEST_F(MultiTagsTest, MultiTagsTestMatchesSecondaries) {
- auto nodes = getThreeMemberWithTags();
-
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- std::vector<HostAndPort> hosts = selectNodes(nodes,
- mongo::ReadPreference::PrimaryPreferred,
- getMatchesFirstTagSet(),
- 3,
- &isPrimarySelected);
- ASSERT_EQUALS(hosts.size(), 2ull);
-
- ASSERT(!isPrimarySelected);
- ASSERT(vectorToSet(hosts) == stdx::unordered_set<std::string>({"a", "c"}));
-}
-
-TEST_F(MultiTagsTest, PriPrefPriNotOkMatchesFirstNotOk) {
- auto nodes = getThreeMemberWithTags();
-
- nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(nodes,
- mongo::ReadPreference::PrimaryPreferred,
- getMatchesFirstTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("c", host.host());
-}
-
-TEST_F(MultiTagsTest, PriPrefPriNotOkMatchesSecondTest) {
- auto nodes = getThreeMemberWithTags();
-
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- std::vector<HostAndPort> hosts = selectNodes(nodes,
- mongo::ReadPreference::PrimaryPreferred,
- getMatchesSecondTagSet(),
- 3,
- &isPrimarySelected);
- ASSERT_EQUALS(hosts.size(), 2ull);
-
- ASSERT(!isPrimarySelected);
- ASSERT(vectorToSet(hosts) == stdx::unordered_set<std::string>({"a", "c"}));
-}
-
-TEST_F(MultiTagsTest, PriPrefPriNotOkMatchesSecondNotOkTest) {
- auto nodes = getThreeMemberWithTags();
-
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
- nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(nodes,
- mongo::ReadPreference::PrimaryPreferred,
- getMatchesSecondTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("a", host.host());
-}
-
-TEST_F(MultiTagsTest, PriPrefPriNotOkMatchesOnlySecondTest) {
- auto nodes = getThreeMemberWithTags();
-
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(nodes,
- mongo::ReadPreference::PrimaryPreferred,
- getMatchesOnlyFirstTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("a", host.host());
-}
-
-TEST_F(MultiTagsTest, PriPrefPriNotOkMatchesOnlySecondNotOkTest) {
- auto nodes = getThreeMemberWithTags();
-
- nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(nodes,
- mongo::ReadPreference::PrimaryPreferred,
- getMatchesOnlyFirstTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(MultiTagsTest, PriPrefPriOkNoMatch) {
- auto nodes = this->getThreeMemberWithTags();
-
- TagSet tags(getMultiNoMatchTag());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::PrimaryPreferred, tags, 3, &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- ASSERT_EQUALS("b", host.host());
-}
-
-TEST_F(MultiTagsTest, PriPrefPriNotOkNoMatch) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getMultiNoMatchTag());
-
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::PrimaryPreferred, tags, 3, &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(MultiTagsTest, SecOnlyMatchesFirstTest) {
- auto nodes = getThreeMemberWithTags();
-
- bool isPrimarySelected = false;
- std::vector<HostAndPort> hosts = selectNodes(nodes,
- mongo::ReadPreference::SecondaryOnly,
- getMatchesFirstTagSet(),
- 3,
- &isPrimarySelected);
- ASSERT_EQUALS(hosts.size(), 2ull);
-
- ASSERT(!isPrimarySelected);
- ASSERT(vectorToSet(hosts) == stdx::unordered_set<std::string>({"a", "c"}));
-}
-
-TEST_F(MultiTagsTest, SecOnlyMatchesFirstNotOk) {
- auto nodes = getThreeMemberWithTags();
-
- nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(nodes,
- mongo::ReadPreference::SecondaryOnly,
- getMatchesFirstTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("c", host.host());
-}
-
-TEST_F(MultiTagsTest, SecOnlyMatchesSecond) {
- auto nodes = getThreeMemberWithTags();
-
- bool isPrimarySelected = false;
- std::vector<HostAndPort> hosts = selectNodes(nodes,
- mongo::ReadPreference::SecondaryOnly,
- getMatchesSecondTagSet(),
- 3,
- &isPrimarySelected);
- ASSERT_EQUALS(hosts.size(), 2ull);
-
- ASSERT(!isPrimarySelected);
- ASSERT(vectorToSet(hosts) == stdx::unordered_set<std::string>({"a", "c"}));
-}
-
-TEST_F(MultiTagsTest, SecOnlyMatchesSecondNotOk) {
- auto nodes = getThreeMemberWithTags();
-
- nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(nodes,
- mongo::ReadPreference::SecondaryOnly,
- getMatchesSecondTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("a", host.host());
-}
-
-TEST_F(MultiTagsTest, SecOnlyMatchesOnlyFirst) {
- auto nodes = getThreeMemberWithTags();
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(nodes,
- mongo::ReadPreference::SecondaryOnly,
- getMatchesOnlyFirstTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("a", host.host());
-}
-
-TEST_F(MultiTagsTest, SecOnlyMatchesOnlyFirstNotOk) {
- auto nodes = getThreeMemberWithTags();
-
- nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(nodes,
- mongo::ReadPreference::SecondaryOnly,
- getMatchesOnlyFirstTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(MultiTagsTest, SecOnlyMultiTagsTestWithPriMatch) {
- auto nodes = getThreeMemberWithTags();
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(
- nodes, mongo::ReadPreference::SecondaryOnly, getMatchesPriTagSet(), 3, &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("a", host.host());
-}
-
-TEST_F(MultiTagsTest, SecOnlyMultiTagsTestNoMatch) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getMultiNoMatchTag());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryOnly, tags, 3, &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(MultiTagsTest, SecPrefMatchesFirst) {
- auto nodes = getThreeMemberWithTags();
-
- bool isPrimarySelected = false;
- std::vector<HostAndPort> hosts = selectNodes(nodes,
- mongo::ReadPreference::SecondaryPreferred,
- getMatchesFirstTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT_EQUALS(hosts.size(), 2ull);
-
- ASSERT(!isPrimarySelected);
- ASSERT(vectorToSet(hosts) == stdx::unordered_set<std::string>({"a", "c"}));
-}
-
-TEST_F(MultiTagsTest, SecPrefMatchesFirstNotOk) {
- auto nodes = getThreeMemberWithTags();
-
- nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(nodes,
- mongo::ReadPreference::SecondaryPreferred,
- getMatchesFirstTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("c", host.host());
-}
-
-TEST_F(MultiTagsTest, SecPrefMatchesSecond) {
- auto nodes = getThreeMemberWithTags();
-
- bool isPrimarySelected = false;
- std::vector<HostAndPort> hosts = selectNodes(nodes,
- mongo::ReadPreference::SecondaryPreferred,
- getMatchesSecondTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT_EQUALS(hosts.size(), 2ull);
-
- ASSERT(!isPrimarySelected);
- ASSERT(vectorToSet(hosts) == stdx::unordered_set<std::string>({"a", "c"}));
-}
-
-TEST_F(MultiTagsTest, SecPrefMatchesSecondNotOk) {
- auto nodes = getThreeMemberWithTags();
-
- nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(nodes,
- mongo::ReadPreference::SecondaryPreferred,
- getMatchesSecondTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("a", host.host());
-}
-
-TEST_F(MultiTagsTest, SecPrefMatchesOnlyFirst) {
- auto nodes = getThreeMemberWithTags();
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(nodes,
- mongo::ReadPreference::SecondaryPreferred,
- getMatchesOnlyFirstTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("a", host.host());
-}
-
-TEST_F(MultiTagsTest, SecPrefMatchesOnlyFirstNotOk) {
- auto nodes = getThreeMemberWithTags();
-
- nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(nodes,
- mongo::ReadPreference::SecondaryPreferred,
- getMatchesOnlyFirstTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- ASSERT_EQUALS("b", host.host());
-}
-
-TEST_F(MultiTagsTest, SecPrefMultiTagsTestWithPriMatch) {
- auto nodes = getThreeMemberWithTags();
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(nodes,
- mongo::ReadPreference::SecondaryPreferred,
- getMatchesPriTagSet(),
- 3,
- &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("a", host.host());
-}
-
-TEST_F(MultiTagsTest, SecPrefMultiTagsTestNoMatch) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getMultiNoMatchTag());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryPreferred, tags, 3, &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- ASSERT_EQUALS("b", host.host());
-}
-
-TEST_F(MultiTagsTest, SecPrefMultiTagsTestNoMatchPriNotOk) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getMultiNoMatchTag());
-
- nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::SecondaryPreferred, tags, 3, &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(MultiTagsTest, NearestMatchesFirst) {
- auto nodes = getThreeMemberWithTags();
-
- bool isPrimarySelected = false;
- std::vector<HostAndPort> hosts = selectNodes(
- nodes, mongo::ReadPreference::Nearest, getMatchesFirstTagSet(), 3, &isPrimarySelected);
-
- ASSERT_EQUALS(hosts.size(), 2ull);
-
- ASSERT(!isPrimarySelected);
- ASSERT(vectorToSet(hosts) == stdx::unordered_set<std::string>({"a", "c"}));
-}
-
-TEST_F(MultiTagsTest, NearestMatchesFirstNotOk) {
- auto nodes = getThreeMemberWithTags();
-
- BSONArrayBuilder arrayBuilder;
- arrayBuilder.append(BSON("p"
- << "1"));
- arrayBuilder.append(BSON("dc"
- << "sf"));
-
- TagSet tags(arrayBuilder.arr());
-
- nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::Nearest, tags, 3, &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- ASSERT_EQUALS("b", host.host());
-}
-
-TEST_F(MultiTagsTest, NearestMatchesSecond) {
- auto nodes = getThreeMemberWithTags();
-
- bool isPrimarySelected = false;
- std::vector<HostAndPort> hosts = selectNodes(
- nodes, mongo::ReadPreference::Nearest, getMatchesSecondTagSet(), 3, &isPrimarySelected);
-
- ASSERT_EQUALS(hosts.size(), 2ull);
-
- ASSERT(!isPrimarySelected);
- ASSERT(vectorToSet(hosts) == stdx::unordered_set<std::string>({"a", "c"}));
-}
-
-TEST_F(MultiTagsTest, NearestMatchesSecondNotOk) {
- auto nodes = getThreeMemberWithTags();
-
- BSONArrayBuilder arrayBuilder;
- arrayBuilder.append(BSON("z"
- << "2"));
- arrayBuilder.append(BSON("p"
- << "2"));
- arrayBuilder.append(BSON("dc"
- << "sf"));
-
- TagSet tags(arrayBuilder.arr());
-
- nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::Nearest, tags, 3, &isPrimarySelected);
-
- ASSERT(isPrimarySelected);
- ASSERT_EQUALS("b", host.host());
-}
-
-TEST_F(MultiTagsTest, NearestMatchesOnlyFirst) {
- auto nodes = getThreeMemberWithTags();
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(
- nodes, mongo::ReadPreference::Nearest, getMatchesOnlyFirstTagSet(), 3, &isPrimarySelected);
-
- ASSERT(!isPrimarySelected);
- ASSERT_EQUALS("a", host.host());
-}
-
-TEST_F(MultiTagsTest, NeatestMatchesLastNotOk) {
- auto nodes = getThreeMemberWithTags();
-
- nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
-
- bool isPrimarySelected = false;
- HostAndPort host = selectNode(
- nodes, mongo::ReadPreference::Nearest, getMatchesOnlyFirstTagSet(), 3, &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(MultiTagsTest, NearestMultiTagsTestWithPriMatch) {
- auto nodes = getThreeMemberWithTags();
-
- bool isPrimarySelected = false;
- std::vector<HostAndPort> hosts = selectNodes(
- nodes, mongo::ReadPreference::Nearest, getMatchesPriTagSet(), 3, &isPrimarySelected);
-
- ASSERT_EQUALS(hosts.size(), 2ull);
- ASSERT(isPrimarySelected);
-
- ASSERT(vectorToSet(hosts) == stdx::unordered_set<std::string>({"a", "b"}));
-}
-
-TEST_F(MultiTagsTest, NearestMultiTagsTestNoMatch) {
- auto nodes = getThreeMemberWithTags();
- TagSet tags(getMultiNoMatchTag());
-
- bool isPrimarySelected = false;
- HostAndPort host =
- selectNode(nodes, mongo::ReadPreference::Nearest, tags, 3, &isPrimarySelected);
-
- ASSERT(host.empty());
-}
-
-TEST_F(MultiTagsTest, DefaultConstructorMatchesAll) {
- TagSet tags;
- ASSERT_BSONOBJ_EQ(tags.getTagBSON(), BSON_ARRAY(BSONObj()));
-}
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/client/scanning_replica_set_monitor_scan_test.cpp b/src/mongo/client/scanning_replica_set_monitor_scan_test.cpp
deleted file mode 100644
index 3004fb140f1..00000000000
--- a/src/mongo/client/scanning_replica_set_monitor_scan_test.cpp
+++ /dev/null
@@ -1,1816 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kNetwork
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/client/scanning_replica_set_monitor_test_fixture.h"
-
-#include "mongo/client/mongo_uri.h"
-#include "mongo/logv2/log.h"
-
-namespace mongo {
-namespace {
-
-using CoreScanTest = ScanningReplicaSetMonitorTest;
-
-TEST_F(CoreScanTest, CheckAllSeedsSerial) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- std::set<HostAndPort> seen;
-
- for (size_t i = 0; i < basicSeeds.size(); i++) {
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(basicSeedsSet.count(ns.host));
- ASSERT(!seen.count(ns.host));
- seen.insert(ns.host);
-
- // mock a reply
- bool primary = ns.host.host() == "a";
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary
- << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "ok" << true));
- }
-
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- ASSERT(ns.host.empty());
-
- // validate final state
- ASSERT_EQUALS(state->nodes.size(), basicSeeds.size());
- for (size_t i = 0; i < basicSeeds.size(); i++) {
- auto node = state->findNode(basicSeeds[i]);
- ASSERT(node);
- ASSERT_EQUALS(node->host.toString(), basicSeeds[i].toString());
- ASSERT(node->isUp);
- ASSERT_EQUALS(node->isMaster, node->host.host() == "a");
- ASSERT(node->tags.isEmpty());
- }
-}
-
-TEST_F(CoreScanTest, CheckAllSeedsParallel) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- std::set<HostAndPort> seen;
-
- // get all hosts to contact first
- for (size_t i = 0; i < basicSeeds.size(); i++) {
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(basicSeedsSet.count(ns.host));
- ASSERT(!seen.count(ns.host));
- seen.insert(ns.host);
- }
-
-
- // mock all replies
- for (size_t i = 0; i < basicSeeds.size(); i++) {
- // All hosts to talk to are already dispatched, but no reply has been received
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::WAIT);
- ASSERT(ns.host.empty());
-
- bool primary = i == 0;
- refresher.receivedIsMaster(basicSeeds[i],
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary
- << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "ok" << true));
- }
-
- // Now all hosts have returned data
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- ASSERT(ns.host.empty());
-
- // validate final state
- ASSERT_EQUALS(state->nodes.size(), basicSeeds.size());
- for (size_t i = 0; i < basicSeeds.size(); i++) {
- auto node = state->findNode(basicSeeds[i]);
- ASSERT(node);
- ASSERT_EQUALS(node->host.toString(), basicSeeds[i].toString());
- ASSERT(node->isUp);
- ASSERT_EQUALS(node->isMaster, i == 0);
- ASSERT(node->tags.isEmpty());
- }
-}
-
-TEST_F(CoreScanTest, NoMasterInitAllUp) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- std::set<HostAndPort> seen;
-
- for (size_t i = 0; i < basicSeeds.size(); i++) {
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(basicSeedsSet.count(ns.host));
- ASSERT(!seen.count(ns.host));
- seen.insert(ns.host);
-
- // mock a reply
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << false << "secondary" << true << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "ok" << true));
- }
-
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- ASSERT(ns.host.empty());
-
- // validate final state
- ASSERT_EQUALS(state->nodes.size(), basicSeeds.size());
- for (size_t i = 0; i < basicSeeds.size(); i++) {
- auto node = state->findNode(basicSeeds[i]);
- ASSERT(node);
- ASSERT_EQUALS(node->host.toString(), basicSeeds[i].toString());
- ASSERT(node->isUp);
- ASSERT_EQUALS(node->isMaster, false);
- ASSERT(node->tags.isEmpty());
- }
-}
-
-TEST_F(CoreScanTest, MasterNotInSeeds_NoPrimaryInIsMaster) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- std::set<HostAndPort> seen;
-
- for (size_t i = 0; i < basicSeeds.size(); i++) {
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(basicSeedsSet.count(ns.host));
- ASSERT(!seen.count(ns.host));
- seen.insert(ns.host);
-
- // mock a reply
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << false << "secondary" << true << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c"
- << "d")
- << "ok" << true));
- }
-
- // Only look at "d" after exhausting all other hosts
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT_EQUALS(ns.host.host(), "d");
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << true << "secondary" << false << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c"
- << "d")
- << "ok" << true));
-
-
- ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- ASSERT(ns.host.empty());
-
- // validate final state
- ASSERT_EQUALS(state->nodes.size(), basicSeeds.size() + 1);
- for (size_t i = 0; i < basicSeeds.size(); i++) {
- auto node = state->findNode(basicSeeds[i]);
- ASSERT(node);
- ASSERT_EQUALS(node->host.toString(), basicSeeds[i].toString());
- ASSERT(node->isUp);
- ASSERT_EQUALS(node->isMaster, false);
- ASSERT(node->tags.isEmpty());
- }
-
- auto node = state->findNode(HostAndPort("d"));
- ASSERT(node);
- ASSERT_EQUALS(node->host.host(), "d");
- ASSERT(node->isUp);
- ASSERT_EQUALS(node->isMaster, true);
- ASSERT(node->tags.isEmpty());
-}
-
-TEST_F(CoreScanTest, MasterNotInSeeds_PrimaryInIsMaster) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- std::set<HostAndPort> seen;
- std::set<HostAndPort> expected = basicSeedsSet;
- HostAndPort d("d");
- expected.insert(d);
-
- for (size_t i = 0; i < basicSeeds.size() + 1; i++) {
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(expected.count(ns.host));
-
- ASSERT(!seen.count(ns.host));
- seen.insert(ns.host);
-
- // mock a reply
- bool primary = ns.host.host() == "d";
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary
- << "primary"
- << "d"
- << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c"
- << "d")
- << "ok" << true));
- }
-
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- ASSERT(ns.host.empty());
-
- // validate final state
- ASSERT_EQUALS(state->nodes.size(), basicSeeds.size() + 1);
- for (size_t i = 0; i < basicSeeds.size(); i++) {
- auto node = state->findNode(basicSeeds[i]);
- ASSERT(node);
- ASSERT_EQUALS(node->host.toString(), basicSeeds[i].toString());
- ASSERT(node->isUp);
- ASSERT_EQUALS(node->isMaster, false);
- ASSERT(node->tags.isEmpty());
- }
-
- auto node = state->findNode(HostAndPort("d"));
- ASSERT(node);
- ASSERT_EQUALS(node->host.host(), "d");
- ASSERT(node->isUp);
- ASSERT_EQUALS(node->isMaster, true);
- ASSERT(node->tags.isEmpty());
-}
-
-// Make sure we can use slaves we find even if we can't find a primary
-TEST_F(CoreScanTest, SlavesUsableEvenIfNoMaster) {
- auto connStr = ConnectionString::forReplicaSet(kSetName, {HostAndPort("a")});
- auto state = makeState(MongoURI(connStr));
- Refresher refresher(state);
-
- const ReadPreferenceSetting secondary(ReadPreference::SecondaryOnly, TagSet());
-
- // Mock a reply from the only host we know about and have it claim to not be master or know
- // about any other hosts. This leaves the scan with no more hosts to scan, but all hosts are
- // still marked as down since we never contacted a master. The next call to
- // Refresher::getNextStep will apply all unconfimedReplies and return DONE.
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT_EQUALS(ns.host.host(), "a");
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << false << "secondary" << true << "hosts"
- << BSON_ARRAY("a") << "ok" << true));
-
- // Check intended conditions for entry to getNextStep().
- ASSERT(state->currentScan->hostsToScan.empty());
- ASSERT(state->currentScan->waitingFor.empty());
- ASSERT(state->currentScan->possibleNodes == state->currentScan->triedHosts);
- ASSERT(state->getMatchingHost(secondary).empty());
-
- // getNextStep() should add the possible nodes to the replica set provisionally after being told
- // that there are no more hosts to contact. That is the final act of the scan.
- ASSERT_EQ(refresher.getNextStep().step, Refresher::NextStep::DONE);
-
- // Future calls should be able to return directly from the cached data.
- ASSERT(!state->getMatchingHost(secondary).empty());
-}
-
-// Test multiple nodes that claim to be master (we use a last-wins policy)
-TEST_F(CoreScanTest, MultipleMasterLastNodeWins) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- std::set<HostAndPort> seen;
-
- // get all hosts to contact first
- for (size_t i = 0; i != basicSeeds.size(); ++i) {
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(basicSeedsSet.count(ns.host));
- ASSERT(!seen.count(ns.host));
- seen.insert(ns.host);
- }
-
- const ReadPreferenceSetting primaryOnly(ReadPreference::PrimaryOnly, TagSet());
-
- // mock all replies
- for (size_t i = 0; i != basicSeeds.size(); ++i) {
- // All hosts to talk to are already dispatched, but no reply has been received
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::WAIT);
- ASSERT(ns.host.empty());
-
- refresher.receivedIsMaster(basicSeeds[i],
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << true << "secondary" << false << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "ok" << true));
-
- // Ensure the set primary is the host we just got a reply from
- HostAndPort currentPrimary = state->getMatchingHost(primaryOnly);
- ASSERT_EQUALS(currentPrimary.host(), basicSeeds[i].host());
- ASSERT_EQUALS(state->nodes.size(), basicSeeds.size());
-
- // Check the state of each individual node
- for (size_t j = 0; j != basicSeeds.size(); ++j) {
- auto node = state->findNode(basicSeeds[j]);
- ASSERT(node);
- ASSERT_EQUALS(node->host.toString(), basicSeeds[j].toString());
- ASSERT_EQUALS(node->isUp, j <= i);
- ASSERT_EQUALS(node->isMaster, j == i);
- ASSERT(node->tags.isEmpty());
- }
- }
-
- // Now all hosts have returned data
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- ASSERT(ns.host.empty());
-}
-
-// Test nodes disagree about who is in the set, master is source of truth
-TEST_F(CoreScanTest, MasterIsSourceOfTruth) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- BSONArray primaryHosts = BSON_ARRAY("a"
- << "b"
- << "d");
- BSONArray secondaryHosts = BSON_ARRAY("a"
- << "b"
- << "c");
-
- // mock all replies
- NextStep ns = refresher.getNextStep();
- while (ns.step == NextStep::CONTACT_HOST) {
- bool primary = ns.host.host() == "a";
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary
- << "hosts" << (primary ? primaryHosts : secondaryHosts)
- << "ok" << true));
-
- ns = refresher.getNextStep();
- }
-
- // Ensure that we have heard from all hosts and scan is done
- ASSERT_EQUALS(ns.step, NextStep::DONE);
-
- // Ensure that d is in the set but c is not
- ASSERT(state->findNode(HostAndPort("d")));
- ASSERT(!state->findNode(HostAndPort("c")));
-}
-
-// Test multiple master nodes that disagree about set membership
-TEST_F(CoreScanTest, MultipleMastersDisagree) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- BSONArray hostsForSeed[3];
- hostsForSeed[0] = BSON_ARRAY("a"
- << "b"
- << "c"
- << "d");
- hostsForSeed[1] = BSON_ARRAY("a"
- << "b"
- << "c"
- << "e");
- hostsForSeed[2] = hostsForSeed[0];
-
- std::set<HostAndPort> seen;
-
- for (size_t i = 0; i != basicSeeds.size(); ++i) {
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(basicSeedsSet.count(ns.host));
- ASSERT(!seen.count(ns.host));
- seen.insert(ns.host);
- }
-
- const ReadPreferenceSetting primaryOnly(ReadPreference::PrimaryOnly, TagSet());
-
- // mock all replies
- for (size_t i = 0; i != basicSeeds.size(); ++i) {
- refresher.receivedIsMaster(basicSeeds[i],
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << true << "secondary" << false << "hosts"
- << hostsForSeed[i % 2] << "ok" << true));
-
- // Ensure the primary is the host we just got a reply from
- HostAndPort currentPrimary = state->getMatchingHost(primaryOnly);
- ASSERT_EQUALS(currentPrimary.host(), basicSeeds[i].host());
-
- // Ensure each primary discovered becomes source of truth
- if (i == 1) {
- // "b" thinks node "e" is a member but "d" is not
- ASSERT(state->findNode(HostAndPort("e")));
- ASSERT(!state->findNode(HostAndPort("d")));
- } else {
- // "a" and "c" think node "d" is a member but "e" is not
- ASSERT(state->findNode(HostAndPort("d")));
- ASSERT(!state->findNode(HostAndPort("e")));
- }
- }
-
- // next step should be to contact "d"
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT_EQUALS(ns.host.host(), "d");
- seen.insert(ns.host);
-
- // reply from "d"
- refresher.receivedIsMaster(HostAndPort("d"),
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << false << "secondary" << true << "hosts"
- << hostsForSeed[0] << "ok" << true));
-
- // scan should be complete
- ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- ASSERT(ns.host.empty());
-
- // Validate final state (only "c" should be master and "d" was added)
- ASSERT_EQUALS(state->nodes.size(), basicSeeds.size() + 1);
-
- auto nodes = state->nodes;
- for (auto it = nodes.begin(); it != nodes.end(); ++it) {
- const auto& node = *it;
- ASSERT(node.isUp);
- ASSERT_EQUALS(node.isMaster, node.host.host() == "c");
- ASSERT(seen.count(node.host));
- }
-}
-
-// Ensure getMatchingHost returns hosts even if scan is ongoing
-TEST_F(CoreScanTest, GetMatchingDuringScan) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- const ReadPreferenceSetting primaryOnly(ReadPreference::PrimaryOnly, TagSet());
- const ReadPreferenceSetting secondaryOnly(ReadPreference::SecondaryOnly, TagSet());
-
- for (std::vector<HostAndPort>::const_iterator it = basicSeeds.begin(); it != basicSeeds.end();
- ++it) {
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(basicSeedsSet.count(ns.host));
- ASSERT(state->getMatchingHost(primaryOnly).empty());
- ASSERT(state->getMatchingHost(secondaryOnly).empty());
- }
-
- // mock replies and validate set state as replies come back
- for (size_t i = 0; i != basicSeeds.size(); ++i) {
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::WAIT);
- ASSERT(ns.host.empty());
-
- bool primary = (i == 1);
- refresher.receivedIsMaster(basicSeeds[i],
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary
- << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "ok" << true));
-
- bool hasPrimary = !(state->getMatchingHost(primaryOnly).empty());
- bool hasSecondary = !(state->getMatchingHost(secondaryOnly).empty());
-
- // secondary node has not been confirmed by primary until i == 1
- if (i >= 1) {
- ASSERT(hasPrimary);
- ASSERT(hasSecondary);
- } else {
- ASSERT(!hasPrimary);
- ASSERT(!hasSecondary);
- }
- }
-
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- ASSERT(ns.host.empty());
-}
-
-// Ensure nothing breaks when out-of-band failedHost is called during scan
-TEST_F(CoreScanTest, OutOfBandFailedHost) {
- auto state = makeState(basicUri);
- auto rsm = std::make_shared<ScanningReplicaSetMonitor>(state);
- Refresher refresher(state);
-
- for (size_t i = 0; i != basicSeeds.size(); ++i) {
- NextStep ns = refresher.getNextStep();
- }
-
- for (size_t i = 0; i != basicSeeds.size(); ++i) {
- bool primary = (i == 0);
-
- refresher.receivedIsMaster(basicSeeds[i],
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary
- << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "ok" << true));
-
- if (i >= 1) {
- HostAndPort a("a");
- rsm->failedHost(a, {ErrorCodes::InternalError, "Test error"});
- auto node = state->findNode(a);
- ASSERT(node);
- ASSERT(!node->isUp);
- ASSERT(!node->isMaster);
- } else {
- auto node = state->findNode(HostAndPort("a"));
- ASSERT(node);
- ASSERT(node->isUp);
- ASSERT(node->isMaster);
- }
- }
-}
-
-// Newly elected primary with electionId >= maximum electionId seen by the Refresher
-TEST_F(CoreScanTest, NewPrimaryWithMaxElectionId) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- std::set<HostAndPort> seen;
-
- // get all hosts to contact first
- for (size_t i = 0; i != basicSeeds.size(); ++i) {
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(basicSeedsSet.count(ns.host));
- ASSERT(!seen.count(ns.host));
- seen.insert(ns.host);
- }
-
- const ReadPreferenceSetting primaryOnly(ReadPreference::PrimaryOnly, TagSet());
-
- // mock all replies
- for (size_t i = 0; i != basicSeeds.size(); ++i) {
- // All hosts to talk to are already dispatched, but no reply has been received
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::WAIT);
- ASSERT(ns.host.empty());
-
- refresher.receivedIsMaster(basicSeeds[i],
- -1,
- BSON(
- "setName"
- << "name"
- << "ismaster" << true << "secondary" << false << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "electionId"
- << OID::fromTerm(i) // electionId must increase every cycle.
- << "ok" << true));
-
- // Ensure the set primary is the host we just got a reply from
- HostAndPort currentPrimary = state->getMatchingHost(primaryOnly);
- ASSERT_EQUALS(currentPrimary.host(), basicSeeds[i].host());
- ASSERT_EQUALS(state->nodes.size(), basicSeeds.size());
-
- // Check the state of each individual node
- for (size_t j = 0; j != basicSeeds.size(); ++j) {
- auto node = state->findNode(basicSeeds[j]);
- ASSERT(node);
- ASSERT_EQUALS(node->host.toString(), basicSeeds[j].toString());
- ASSERT_EQUALS(node->isUp, j <= i);
- ASSERT_EQUALS(node->isMaster, j == i);
- ASSERT(node->tags.isEmpty());
- }
- }
-
- // Now all hosts have returned data
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- ASSERT(ns.host.empty());
-}
-
-// Ignore electionId of secondaries
-TEST_F(CoreScanTest, IgnoreElectionIdFromSecondaries) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- std::set<HostAndPort> seen;
-
- const OID primaryElectionId = OID::gen();
-
- // mock all replies
- for (size_t i = 0; i != basicSeeds.size(); ++i) {
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(basicSeedsSet.count(ns.host));
- ASSERT(!seen.count(ns.host));
- seen.insert(ns.host);
-
- // mock a reply
- const bool primary = ns.host.host() == "a";
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary
- << "electionId"
- << (primary ? primaryElectionId : OID::gen()) << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "ok" << true));
- }
-
- // check that the SetState's maxElectionId == primary's electionId
- ASSERT_EQUALS(state->maxElectionId, primaryElectionId);
-
- // Now all hosts have returned data
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- ASSERT(ns.host.empty());
-}
-
-// Stale Primary with obsolete electionId
-TEST_F(CoreScanTest, StalePrimaryWithObsoleteElectionId) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- const OID firstElectionId = OID::gen();
- const OID secondElectionId = OID::gen();
-
- std::set<HostAndPort> seen;
-
- // contact first host claiming to be primary with greater electionId
- {
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(basicSeedsSet.count(ns.host));
- ASSERT(!seen.count(ns.host));
- seen.insert(ns.host);
-
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << true << "secondary" << false
- << "setVersion" << 1 << "electionId" << secondElectionId
- << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "ok" << true));
-
- auto node = state->findNode(ns.host);
- ASSERT(node);
- ASSERT_TRUE(node->isMaster);
- ASSERT_EQUALS(state->maxElectionId, secondElectionId);
- }
-
- // contact second host claiming to be primary with smaller electionId
- {
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(basicSeedsSet.count(ns.host));
- ASSERT(!seen.count(ns.host));
- seen.insert(ns.host);
-
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << true << "secondary" << false
- << "electionId" << firstElectionId << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "ok" << true));
-
- auto node = state->findNode(ns.host);
- ASSERT(node);
- // The SetState shouldn't see this host as master
- ASSERT_FALSE(node->isMaster);
- // the max electionId should remain the same
- ASSERT_EQUALS(state->maxElectionId, secondElectionId);
- }
-
- // third host is a secondary
- {
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(basicSeedsSet.count(ns.host));
- ASSERT(!seen.count(ns.host));
- seen.insert(ns.host);
-
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << false << "secondary" << true << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "ok" << true));
-
- auto node = state->findNode(ns.host);
- ASSERT(node);
- ASSERT_FALSE(node->isMaster);
- // the max electionId should remain the same
- ASSERT_EQUALS(state->maxElectionId, secondElectionId);
- }
-
- // Now all hosts have returned data
- NextStep ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- ASSERT(ns.host.empty());
-}
-
-TEST_F(CoreScanTest, NoPrimaryUpCheck) {
- auto state = makeState(basicUri);
- ScanningReplicaSetMonitor rsm(state);
- ASSERT_FALSE(rsm.isKnownToHaveGoodPrimary());
-}
-
-TEST_F(CoreScanTest, PrimaryIsUpCheck) {
- auto state = makeState(basicUri);
- state->nodes.front().isMaster = true;
- ScanningReplicaSetMonitor rsm(state);
- ASSERT_TRUE(rsm.isKnownToHaveGoodPrimary());
-}
-
-/**
- * Repl protocol verion 0 and 1 compatibility checking.
- */
-TEST_F(CoreScanTest, TwoPrimaries2ndHasNewerConfigVersion) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- auto ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(basicSeedsSet.count(ns.host));
-
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << true << "secondary" << false << "setVersion"
- << 1 << "electionId" << OID("7fffffff0000000000000001")
- << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "ok" << true));
-
- // check that the SetState's maxElectionId == primary's electionId
- ASSERT_EQUALS(state->maxElectionId, OID("7fffffff0000000000000001"));
- ASSERT_EQUALS(state->configVersion, 1);
-
- const OID primaryElectionId = OID::gen();
-
- // Newer setVersion, no election id
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << true << "secondary" << false << "setVersion"
- << 2 << "electionId" << primaryElectionId << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "ok" << true));
-
- ASSERT_EQUALS(state->maxElectionId, primaryElectionId);
- ASSERT_EQUALS(state->configVersion, 2);
-}
-
-/**
- * Repl protocol verion 0 and 1 compatibility checking.
- */
-TEST_F(CoreScanTest, TwoPrimaries2ndHasOlderConfigVersion) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- auto ns = refresher.getNextStep();
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(basicSeedsSet.count(ns.host));
-
- const OID primaryElectionId = OID::gen();
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << true << "secondary" << false << "electionId"
- << primaryElectionId << "setVersion" << 2 << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "ok" << true));
-
- ASSERT_EQUALS(state->maxElectionId, primaryElectionId);
- ASSERT_EQUALS(state->configVersion, 2);
-
- // Older setVersion, but election id > previous election id. Newer setVersion should win.
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << true << "secondary" << false << "setVersion"
- << 1 << "electionId" << OID("7fffffff0000000000000001")
- << "hosts"
- << BSON_ARRAY("a"
- << "b"
- << "c")
- << "ok" << true));
-
- ASSERT_EQUALS(state->maxElectionId, primaryElectionId);
- ASSERT_EQUALS(state->configVersion, 2);
-}
-
-using MaxStalenessMSTest = ScanningReplicaSetMonitorTest;
-
-/**
- * Success finding node matching maxStalenessMS parameter
- */
-TEST_F(MaxStalenessMSTest, MaxStalenessMSMatch) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
- repl::OpTime opTime{Timestamp{10, 10}, 10};
-
- const ReadPreferenceSetting secondary(ReadPreference::SecondaryOnly, TagSet(), Seconds(100));
- BSONArray hosts = BSON_ARRAY("a"
- << "b"
- << "c");
-
- Date_t lastWriteDateStale = Date_t::now() - Seconds(1000);
- Date_t lastWriteDateNonStale = Date_t::now() - Seconds(10);
- // mock all replies
- NextStep ns = refresher.getNextStep();
- while (ns.step == NextStep::CONTACT_HOST) {
- bool primary = ns.host.host() == "a";
- bool nonStale = ns.host.host() == "c";
- nonStale |= primary;
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary
- << "hosts" << hosts << "lastWrite"
- << BSON("lastWriteDate" << (nonStale ? lastWriteDateNonStale
- : lastWriteDateStale)
- << "opTime" << opTime)
- << "ok" << true));
- ns = refresher.getNextStep();
- }
-
- // Ensure that we have heard from all hosts and scan is done
- ASSERT_EQUALS(ns.step, NextStep::DONE);
-
- // make sure all secondaries are in the scan
- ASSERT(state->findNode(HostAndPort("b")));
- ASSERT(state->findNode(HostAndPort("c")));
-
- HostAndPort nonStale = state->getMatchingHost(secondary);
- ASSERT_EQUALS(nonStale.host(), "c");
-}
-
-/**
- * Fail matching maxStalenessMS parameter ( all secondary nodes are stale)
- */
-TEST_F(MaxStalenessMSTest, MaxStalenessMSNoMatch) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
- repl::OpTime opTime{Timestamp{10, 10}, 10};
-
- const ReadPreferenceSetting secondary(ReadPreference::SecondaryOnly, TagSet(), Seconds(200));
- BSONArray hosts = BSON_ARRAY("a"
- << "b"
- << "c");
-
- Date_t lastWriteDateStale = Date_t::now() - Seconds(1000);
- Date_t lastWriteDateNonStale = Date_t::now() - Seconds(100);
- // mock all replies
- NextStep ns = refresher.getNextStep();
- while (ns.step == NextStep::CONTACT_HOST) {
- bool primary = ns.host.host() == "a";
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary
- << "hosts" << hosts << "lastWrite"
- << BSON("lastWriteDate" << (primary ? lastWriteDateNonStale
- : lastWriteDateStale)
- << "opTime" << opTime)
- << "ok" << true));
-
- ns = refresher.getNextStep();
- }
-
- // Ensure that we have heard from all hosts and scan is done
- ASSERT_EQUALS(ns.step, NextStep::DONE);
-
- // make sure all secondaries are in the scan
- ASSERT(state->findNode(HostAndPort("b")));
- ASSERT(state->findNode(HostAndPort("c")));
-
- HostAndPort notFound = state->getMatchingHost(secondary);
- ASSERT_EQUALS(notFound.host(), "");
-}
-
-/**
- * Success matching maxStalenessMS parameter when there is no primary node.
- */
-TEST_F(MaxStalenessMSTest, MaxStalenessMSNoPrimaryMatch) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
- repl::OpTime opTime{Timestamp{10, 10}, 10};
-
- const ReadPreferenceSetting secondary(ReadPreference::SecondaryOnly, TagSet(), Seconds(200));
- BSONArray hosts = BSON_ARRAY("a"
- << "b"
- << "c");
-
- Date_t lastWriteDateStale = Date_t::now() - Seconds(1000);
- Date_t lastWriteDateNonStale = Date_t::now() - Seconds(100);
- // mock all replies
- NextStep ns = refresher.getNextStep();
- while (ns.step == NextStep::CONTACT_HOST) {
- bool isNonStale = ns.host.host() == "a";
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << false << "secondary" << true << "hosts"
- << hosts << "lastWrite"
- << BSON("lastWriteDate"
- << (isNonStale ? lastWriteDateNonStale
- : lastWriteDateStale)
- << "opTime" << opTime)
- << "ok" << true));
-
- ns = refresher.getNextStep();
- }
-
- // Ensure that we have heard from all hosts and scan is done
- ASSERT_EQUALS(ns.step, NextStep::DONE);
-
- // make sure all secondaries are in the scan
- ASSERT(state->findNode(HostAndPort("a")));
- ASSERT(state->findNode(HostAndPort("b")));
- ASSERT(state->findNode(HostAndPort("c")));
-
- HostAndPort notStale = state->getMatchingHost(secondary);
- ASSERT_EQUALS(notStale.host(), "a");
-}
-
-
-/**
- * Fail matching maxStalenessMS parameter when all nodes are failed
- */
-TEST_F(MaxStalenessMSTest, MaxStalenessMSAllFailed) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
- repl::OpTime opTime{Timestamp{10, 10}, 10};
-
- const ReadPreferenceSetting secondary(ReadPreference::SecondaryOnly, TagSet(), Seconds(200));
- BSONArray hosts = BSON_ARRAY("a"
- << "b"
- << "c");
-
- Date_t lastWriteDateStale = Date_t::now() - Seconds(1000);
- Date_t lastWriteDateNonStale = Date_t::now() - Seconds(100);
- // mock all replies
- NextStep ns = refresher.getNextStep();
- while (ns.step == NextStep::CONTACT_HOST) {
- bool isNonStale = ns.host.host() == "a";
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << false << "secondary" << true << "hosts"
- << hosts << "lastWrite"
- << BSON("lastWriteDate"
- << (isNonStale ? lastWriteDateNonStale
- : lastWriteDateStale)
- << "opTime" << opTime)
- << "ok" << true));
-
- ns = refresher.getNextStep();
- }
-
- // Ensure that we have heard from all hosts and scan is done
- ASSERT_EQUALS(ns.step, NextStep::DONE);
-
- // make sure all secondaries are in the scan
- refresher.failedHost(HostAndPort("a"), {ErrorCodes::InternalError, "Test error"});
- refresher.failedHost(HostAndPort("b"), {ErrorCodes::InternalError, "Test error"});
- refresher.failedHost(HostAndPort("c"), {ErrorCodes::InternalError, "Test error"});
-
- HostAndPort notStale = state->getMatchingHost(secondary);
- ASSERT_EQUALS(notStale.host(), "");
-}
-
-/**
- * Fail matching maxStalenessMS parameter when all nodes except primary are failed
- */
-TEST_F(MaxStalenessMSTest, MaxStalenessMSAllButPrimaryFailed) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
- repl::OpTime opTime{Timestamp{10, 10}, 10};
-
- const ReadPreferenceSetting secondary(ReadPreference::SecondaryOnly, TagSet(), Seconds(200));
- BSONArray hosts = BSON_ARRAY("a"
- << "b"
- << "c");
-
- Date_t lastWriteDateStale = Date_t::now() - Seconds(1000);
- Date_t lastWriteDateNonStale = Date_t::now() - Seconds(100);
- // mock all replies
- NextStep ns = refresher.getNextStep();
- while (ns.step == NextStep::CONTACT_HOST) {
- bool primary = ns.host.host() == "a";
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary
- << "hosts" << hosts << "lastWrite"
- << BSON("lastWriteDate" << (primary ? lastWriteDateNonStale
- : lastWriteDateStale)
- << "opTime" << opTime)
- << "ok" << true));
- ns = refresher.getNextStep();
- }
-
- // Ensure that we have heard from all hosts and scan is done
- ASSERT_EQUALS(ns.step, NextStep::DONE);
-
- // make sure the primary is in the scan
- ASSERT(state->findNode(HostAndPort("a")));
- refresher.failedHost(HostAndPort("b"), {ErrorCodes::InternalError, "Test error"});
- refresher.failedHost(HostAndPort("c"), {ErrorCodes::InternalError, "Test error"});
-
- // No match because the request needs secondaryOnly host
- HostAndPort notStale = state->getMatchingHost(secondary);
- ASSERT_EQUALS(notStale.host(), "");
-}
-
-/**
- * Fail matching maxStalenessMS parameter one secondary failed, one secondary is stale
- */
-TEST_F(MaxStalenessMSTest, MaxStalenessMSOneSecondaryFailed) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
- repl::OpTime opTime{Timestamp{10, 10}, 10};
-
- const ReadPreferenceSetting secondary(ReadPreference::SecondaryOnly, TagSet(), Seconds(200));
- BSONArray hosts = BSON_ARRAY("a"
- << "b"
- << "c");
-
- Date_t lastWriteDateStale = Date_t::now() - Seconds(1000);
- Date_t lastWriteDateNonStale = Date_t::now() - Seconds(100);
- // mock all replies
- NextStep ns = refresher.getNextStep();
- while (ns.step == NextStep::CONTACT_HOST) {
- bool primary = ns.host.host() == "a";
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary
- << "hosts" << hosts << "lastWrite"
- << BSON("lastWriteDate" << (primary ? lastWriteDateNonStale
- : lastWriteDateStale)
- << "opTime" << opTime)
- << "ok" << true));
- ns = refresher.getNextStep();
- }
-
- // Ensure that we have heard from all hosts and scan is done
- ASSERT_EQUALS(ns.step, NextStep::DONE);
-
- ASSERT(state->findNode(HostAndPort("a")));
- ASSERT(state->findNode(HostAndPort("b")));
- refresher.failedHost(HostAndPort("c"), {ErrorCodes::InternalError, "Test error"});
-
- // No match because the write date is stale
- HostAndPort notStale = state->getMatchingHost(secondary);
- ASSERT_EQUALS(notStale.host(), "");
-}
-
-/**
- * Success matching maxStalenessMS parameter when one secondary failed
- */
-TEST_F(MaxStalenessMSTest, MaxStalenessMSNonStaleSecondaryMatched) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
- repl::OpTime opTime{Timestamp{10, 10}, 10};
-
- const ReadPreferenceSetting secondary(ReadPreference::SecondaryOnly, TagSet(), Seconds(200));
- BSONArray hosts = BSON_ARRAY("a"
- << "b"
- << "c");
-
- Date_t lastWriteDateStale = Date_t::now() - Seconds(1000);
- Date_t lastWriteDateNonStale = Date_t::now() - Seconds(100);
- // mock all replies
- NextStep ns = refresher.getNextStep();
- while (ns.step == NextStep::CONTACT_HOST) {
- bool primary = ns.host.host() == "a";
- bool isNonStale = ns.host.host() == "b";
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary
- << "hosts" << hosts << "lastWrite"
- << BSON("lastWriteDate"
- << (isNonStale ? lastWriteDateNonStale
- : lastWriteDateStale)
- << "opTime" << opTime)
- << "ok" << true));
- ns = refresher.getNextStep();
- }
-
- // Ensure that we have heard from all hosts and scan is done
- ASSERT_EQUALS(ns.step, NextStep::DONE);
-
- refresher.failedHost(HostAndPort("a"), {ErrorCodes::InternalError, "Test error"});
- ASSERT(state->findNode(HostAndPort("b")));
- refresher.failedHost(HostAndPort("c"), {ErrorCodes::InternalError, "Test error"});
-
- HostAndPort notStale = state->getMatchingHost(secondary);
- ASSERT_EQUALS(notStale.host(), "b");
-}
-
-/**
- * Fail matching maxStalenessMS parameter when no lastWrite in the response
- */
-TEST_F(MaxStalenessMSTest, MaxStalenessMSNoLastWrite) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- const ReadPreferenceSetting secondary(ReadPreference::SecondaryOnly, TagSet(), Seconds(200));
- BSONArray hosts = BSON_ARRAY("a"
- << "b"
- << "c");
-
- // mock all replies
- NextStep ns = refresher.getNextStep();
- while (ns.step == NextStep::CONTACT_HOST) {
- bool primary = ns.host.host() == "a";
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary
- << "hosts" << hosts << "ok" << true));
- ns = refresher.getNextStep();
- }
-
- // Ensure that we have heard from all hosts and scan is done
- ASSERT_EQUALS(ns.step, NextStep::DONE);
-
- ASSERT(state->findNode(HostAndPort("a")));
- ASSERT(state->findNode(HostAndPort("b")));
- ASSERT(state->findNode(HostAndPort("c")));
-
- ASSERT(state->getMatchingHost(secondary).empty());
-}
-
-/**
- * Match when maxStalenessMS=0 and no lastWrite in the response
- */
-TEST_F(MaxStalenessMSTest, MaxStalenessMSZeroNoLastWrite) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- const ReadPreferenceSetting secondary(ReadPreference::SecondaryOnly, TagSet(), Seconds(0));
- BSONArray hosts = BSON_ARRAY("a"
- << "b"
- << "c");
-
- // mock all replies
- NextStep ns = refresher.getNextStep();
- while (ns.step == NextStep::CONTACT_HOST) {
- bool primary = ns.host.host() == "a";
- refresher.receivedIsMaster(ns.host,
- -1,
- BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary
- << "hosts" << hosts << "ok" << true));
- ns = refresher.getNextStep();
- }
-
- // Ensure that we have heard from all hosts and scan is done
- ASSERT_EQUALS(ns.step, NextStep::DONE);
-
- ASSERT(state->findNode(HostAndPort("a")));
- ASSERT(state->findNode(HostAndPort("b")));
- ASSERT(state->findNode(HostAndPort("c")));
-
- ASSERT(!state->getMatchingHost(secondary).empty());
-}
-
-using MinClusterTimeTest = ScanningReplicaSetMonitorTest;
-/**
- * Success matching minClusterTime
- */
-TEST_F(MinClusterTimeTest, MinClusterTimeMatched) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- Timestamp minClusterTimeSetting{10, 10};
- repl::OpTime opTimeNonStale{Timestamp{10, 11}, 1};
- repl::OpTime opTimeStale{Timestamp{10, 9}, 1};
-
- ReadPreferenceSetting readPref(ReadPreference::Nearest, TagSet());
- readPref.minClusterTime = minClusterTimeSetting;
- BSONArray hosts = BSON_ARRAY("a"
- << "b"
- << "c");
-
- // mock all replies
- NextStep ns = refresher.getNextStep();
- while (ns.step == NextStep::CONTACT_HOST) {
- bool primary = ns.host.host() == "a";
- bool isNonStale = ns.host.host() == "b";
- BSONObj bson = BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary << "hosts" << hosts
- << "lastWrite"
- << BSON("opTime" << (isNonStale ? opTimeNonStale.toBSON()
- : opTimeStale.toBSON()))
- << "ok" << true);
- refresher.receivedIsMaster(ns.host, -1, bson);
- ns = refresher.getNextStep();
- }
-
- // Ensure that we have heard from all hosts and scan is done
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- HostAndPort notStale = state->getMatchingHost(readPref);
- ASSERT_EQUALS(notStale.host(), "b");
-}
-
-/**
- * Failure matching minClusterTime on primary for SecondaryOnly
- */
-TEST_F(MinClusterTimeTest, MinClusterTimeNotMatched) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- Timestamp minClusterTimeSetting{10, 10};
- repl::OpTime opTimeNonStale{Timestamp{10, 11}, 1};
- repl::OpTime opTimeStale{Timestamp{10, 9}, 1};
-
- ReadPreferenceSetting readPref(ReadPreference::SecondaryOnly, TagSet());
- readPref.minClusterTime = minClusterTimeSetting;
- BSONArray hosts = BSON_ARRAY("a"
- << "b"
- << "c");
-
- // mock all replies
- NextStep ns = refresher.getNextStep();
- while (ns.step == NextStep::CONTACT_HOST) {
- bool primary = ns.host.host() == "a";
- bool isNonStale = ns.host.host() == "a";
- BSONObj bson = BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary << "hosts" << hosts
- << "lastWrite"
- << BSON("opTime" << (isNonStale ? opTimeNonStale.toBSON()
- : opTimeStale.toBSON()))
- << "ok" << true);
- refresher.receivedIsMaster(ns.host, -1, bson);
- ns = refresher.getNextStep();
- }
-
- // Ensure that we have heard from all hosts and scan is done
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- HostAndPort notStale = state->getMatchingHost(readPref);
- ASSERT(notStale.host() != "a");
-}
-
-/**
- * Ignore minClusterTime if none is matched
- */
-TEST_F(MinClusterTimeTest, MinClusterTimeIgnored) {
- auto state = makeState(basicUri);
- Refresher refresher(state);
-
- Timestamp minClusterTimeSetting{10, 10};
- repl::OpTime opTimeStale{Timestamp{10, 9}, 1};
-
- Date_t lastWriteDateStale = Date_t::now() - Seconds(1000);
- Date_t lastWriteDateNonStale = Date_t::now() - Seconds(100);
-
- ReadPreferenceSetting readPref(ReadPreference::SecondaryOnly, TagSet(), Seconds(200));
- readPref.minClusterTime = minClusterTimeSetting;
- BSONArray hosts = BSON_ARRAY("a"
- << "b"
- << "c");
-
- // mock all replies
- NextStep ns = refresher.getNextStep();
- while (ns.step == NextStep::CONTACT_HOST) {
- bool primary = ns.host.host() == "a";
- bool isNonStale = ns.host.host() == "c";
- BSONObj bson = BSON("setName"
- << "name"
- << "ismaster" << primary << "secondary" << !primary << "hosts" << hosts
- << "lastWrite"
- << BSON("lastWriteDate"
- << (isNonStale || primary ? lastWriteDateNonStale
- : lastWriteDateStale)
- << "opTime" << opTimeStale.toBSON())
- << "ok" << true);
- refresher.receivedIsMaster(ns.host, -1, bson);
- ns = refresher.getNextStep();
- }
-
- // Ensure that we have heard from all hosts and scan is done
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- HostAndPort notStale = state->getMatchingHost(readPref);
- ASSERT_EQUALS(notStale.host(), "c");
-}
-
-// -- ReplicaSetChangeNotifier/Listener tests --
-
-class Listener : public ReplicaSetChangeNotifier::Listener {
-public:
- void logEvent(StringData name, const Key& key) {
- LOGV2(20190, "Replica set change listener event", "name"_attr = name, "key"_attr = key);
- }
- void logEvent(StringData name, const State& state) {
- LOGV2(20191,
- "Replica set change listener event",
- "name"_attr = name,
- "stateGeneration"_attr = state.generation,
- "stateConnStr"_attr = state.connStr,
- "statePrimary"_attr = state.primary);
- }
-
- void onFoundSet(const Key& key) noexcept override {
- lastFoundSetId = ++eventId;
- logEvent("FoundSet", key);
- }
- void onPossibleSet(const State& state) noexcept override {
- lastPossibleSetId = ++eventId;
- logEvent("PossibleSet", state);
- lastState = state;
- }
- void onConfirmedSet(const State& state) noexcept override {
- lastConfirmedSetId = ++eventId;
- logEvent("ConfirmedSet", state);
- lastState = state;
- }
- void onDroppedSet(const Key& key) noexcept override {
- lastDroppedSetId = ++eventId;
- logEvent("DroppedSet", key);
- }
-
- int64_t eventId = -1;
- int64_t lastFoundSetId = -1;
- int64_t lastPossibleSetId = -1;
- int64_t lastConfirmedSetId = -1;
- int64_t lastDroppedSetId = -1;
- State lastState;
-};
-
-class ChangeNotifierTest : public ScanningReplicaSetMonitorTest {
-public:
- ChangeNotifierTest() = default;
- virtual ~ChangeNotifierTest() = default;
-
- enum class NodeState {
- kUnknown = 0,
- kPrimary,
- kSecondary,
- kStandalone,
- };
-
- auto& listener() const {
- return *static_cast<Listener*>(_listener.get());
- }
-
- void updateSet(std::map<HostAndPort, NodeState> replicaSet) {
- auto refresher = Refresher(_state);
- std::set<HostAndPort> seen;
- HostAndPort primary;
- std::set<HostAndPort> members;
-
- BSONArrayBuilder arrayBuilder;
- for (const auto& [host, nodeState] : replicaSet) {
- if (nodeState == NodeState::kStandalone) {
- continue;
- }
-
- if (nodeState == NodeState::kPrimary) {
- primary = host;
- }
-
- members.insert(host);
-
- arrayBuilder.append(StringData(host.host()));
- }
- auto bsonHosts = arrayBuilder.arr();
-
- auto markIsMaster = [&](auto host, bool isMaster) {
- refresher.receivedIsMaster(host,
- -1,
- BSON("setName" << kSetName << "ismaster" << isMaster
- << "secondary" << !isMaster << "hosts"
- << bsonHosts << "ok" << true));
- };
-
- auto markFailed = [&](auto host) {
- refresher.failedHost(host, {ErrorCodes::InternalError, "Test error"});
- };
-
- auto gen = listener().lastState.generation;
-
- NextStep ns = refresher.getNextStep();
- for (; ns.step != NextStep::DONE; ns = refresher.getNextStep()) {
- ASSERT_EQUALS(ns.step, NextStep::CONTACT_HOST);
- ASSERT(replicaSet.count(ns.host));
- ASSERT(!seen.count(ns.host));
- seen.insert(ns.host);
-
- // mock a reply
- switch (replicaSet[ns.host]) {
- case NodeState::kStandalone: {
- markFailed(ns.host);
- } break;
- case NodeState::kPrimary: {
- markIsMaster(ns.host, true);
- } break;
- case NodeState::kSecondary: {
- markIsMaster(ns.host, false);
- } break;
- case NodeState::kUnknown:
- MONGO_UNREACHABLE;
- };
- }
-
- // Verify that the listener received the right data
- if (gen != listener().lastState.generation) {
- // Our State is what the notifier thinks it should be
- ASSERT_EQUALS(listener().lastState.connStr,
- listener().getCurrentState(kSetName.toString()).connStr);
- ASSERT_EQUALS(listener().lastState.primary,
- listener().getCurrentState(kSetName.toString()).primary);
- ASSERT_EQUALS(listener().lastState.generation,
- listener().getCurrentState(kSetName.toString()).generation);
-
- // Our State is what we'd expect
- ASSERT_EQUALS(listener().lastState.connStr.getSetName(), kSetName);
- ASSERT_EQUALS(listener().lastState.connStr.getServers().size(), members.size());
- ASSERT_EQUALS(listener().lastState.primary, primary);
- }
-
- ASSERT_EQUALS(ns.step, NextStep::DONE);
- ASSERT(ns.host.empty());
- }
-
-protected:
- std::shared_ptr<decltype(_notifier)::Listener> _listener = _notifier.makeListener<Listener>();
-
- std::shared_ptr<SetState> _state = makeState(basicUri);
-};
-
-TEST_F(ChangeNotifierTest, NotifyNominal) {
- auto currentId = -1;
-
- // State exists. Signal: null
- ASSERT_EQ(listener().lastFoundSetId, currentId);
-
- // Initializing the state. Signal: FoundSet
- _state->init();
- ASSERT_EQ(listener().lastFoundSetId, ++currentId);
-
- // 'a' claims to be primary. Signal: Confirmed
- updateSet({
- {
- HostAndPort("a"),
- NodeState::kPrimary,
- },
- {
- HostAndPort("b"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("c"),
- NodeState::kSecondary,
- },
- });
- ASSERT_EQ(listener().lastConfirmedSetId, ++currentId);
-
- // Getting another scan with the same details. Signal: null
- updateSet({
- {
- HostAndPort("a"),
- NodeState::kPrimary,
- },
- {
- HostAndPort("b"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("c"),
- NodeState::kSecondary,
- },
- });
- ASSERT_EQ(listener().eventId, currentId);
-
- // Dropped. Signal: Dropped
- _state->drop();
- ASSERT_EQ(listener().lastDroppedSetId, ++currentId);
-}
-
-TEST_F(ChangeNotifierTest, NotifyElections) {
- auto currentId = -1;
-
- // State exists. Signal: null
- ASSERT_EQ(listener().lastFoundSetId, currentId);
-
- // Initializing the state. Signal: FoundSet
- _state->init();
- ASSERT_EQ(listener().lastFoundSetId, ++currentId);
-
- // 'a' claims to be primary. Signal: ConfirmedSet
- updateSet({
- {
- HostAndPort("a"),
- NodeState::kPrimary,
- },
- {
- HostAndPort("b"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("c"),
- NodeState::kSecondary,
- },
- });
- ASSERT_EQ(listener().lastConfirmedSetId, ++currentId);
-
- // 'b' claims to be primary. Signal: ConfirmedSet
- updateSet({
- {
- HostAndPort("a"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("b"),
- NodeState::kPrimary,
- },
- {
- HostAndPort("c"),
- NodeState::kSecondary,
- },
- });
- ASSERT_EQ(listener().lastConfirmedSetId, ++currentId);
-
- // All hosts tell us that they are not primary. Signal: null
- updateSet({
- {
- HostAndPort("a"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("b"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("c"),
- NodeState::kSecondary,
- },
- });
- ASSERT_EQ(listener().eventId, currentId);
-
- // 'a' claims to be primary again. Signal: ConfirmedSet
- updateSet({
- {
- HostAndPort("a"),
- NodeState::kPrimary,
- },
- {
- HostAndPort("b"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("c"),
- NodeState::kSecondary,
- },
- });
- ASSERT_EQ(listener().lastConfirmedSetId, ++currentId);
-
- // Dropped. Signal: Dropped
- _state->drop();
- ASSERT_EQ(listener().lastDroppedSetId, ++currentId);
-}
-
-TEST_F(ChangeNotifierTest, NotifyReconfig) {
- auto currentId = -1;
-
- // State exists. Signal: null
- ASSERT_EQ(listener().lastFoundSetId, currentId);
-
- // Initializing the state. Signal: FoundSet
- _state->init();
- ASSERT_EQ(listener().lastFoundSetId, ++currentId);
-
- // Update the set with a full scan showing no primary. Signal: PossibleSet
- updateSet({
- {
- HostAndPort("a"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("b"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("c"),
- NodeState::kSecondary,
- },
- });
- ASSERT_EQ(listener().eventId, ++currentId);
-
- // Mark 'a' as removed. Signal: null
- updateSet({
- {
- HostAndPort("a"),
- NodeState::kStandalone,
- },
- {
- HostAndPort("b"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("c"),
- NodeState::kSecondary,
- },
- });
- ASSERT_EQ(listener().eventId, currentId);
-
- // Discover 'd' as secondary. Signal: PossibleSet
- updateSet({
- {
- HostAndPort("a"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("b"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("c"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("d"),
- NodeState::kSecondary,
- },
- });
- ASSERT_EQ(listener().lastPossibleSetId, ++currentId);
-
- // Mark 'b' as primary, no 'd'. Signal: ConfirmedSet
- updateSet({
- {
- HostAndPort("a"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("b"),
- NodeState::kPrimary,
- },
- {
- HostAndPort("c"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("d"),
- NodeState::kStandalone,
- },
- });
- ASSERT_EQ(listener().lastConfirmedSetId, ++currentId);
-
- // Mark 'a' as removed. Signal: ConfirmedSet
- updateSet({
- {
- HostAndPort("a"),
- NodeState::kStandalone,
- },
- {
- HostAndPort("b"),
- NodeState::kPrimary,
- },
- {
- HostAndPort("c"),
- NodeState::kSecondary,
- },
- });
- ASSERT_EQ(listener().lastConfirmedSetId, ++currentId);
-
- // Mark 'a' as secondary again. Signal: ConfirmedSet
- updateSet({
- {
- HostAndPort("b"),
- NodeState::kPrimary,
- },
- {
- HostAndPort("c"),
- NodeState::kSecondary,
- },
- {
- HostAndPort("a"),
- NodeState::kSecondary,
- },
- });
- ASSERT_EQ(listener().lastConfirmedSetId, ++currentId);
-
- // Dropped. Signal: Dropped
- _state->drop();
- ASSERT_EQ(listener().lastDroppedSetId, ++currentId);
-}
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/client/scanning_replica_set_monitor_test_concurrent.cpp b/src/mongo/client/scanning_replica_set_monitor_test_concurrent.cpp
deleted file mode 100644
index 21370178ed5..00000000000
--- a/src/mongo/client/scanning_replica_set_monitor_test_concurrent.cpp
+++ /dev/null
@@ -1,431 +0,0 @@
-/**
- * Copyright (C) 2019-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/client/replica_set_monitor.h"
-#include "mongo/client/replica_set_monitor_protocol_test_util.h"
-#include "mongo/client/scanning_replica_set_monitor_internal.h"
-#include "mongo/dbtests/mock/mock_replica_set.h"
-#include "mongo/executor/network_interface_mock.h"
-#include "mongo/executor/thread_pool_mock.h"
-#include "mongo/executor/thread_pool_task_executor.h"
-#include "mongo/executor/thread_pool_task_executor_test_fixture.h"
-#include "mongo/logv2/log.h"
-#include "mongo/unittest/log_test.h"
-#include "mongo/unittest/unittest.h"
-#include "mongo/util/duration.h"
-
-namespace mongo {
-namespace {
-
-using executor::NetworkInterfaceMock;
-using executor::RemoteCommandResponse;
-using executor::ThreadPoolExecutorTest;
-using InNetworkGuard = NetworkInterfaceMock::InNetworkGuard;
-using NetworkOperationIterator = NetworkInterfaceMock::NetworkOperationIterator;
-using StepKind = ScanningReplicaSetMonitor::Refresher::NextStep::StepKind;
-
-class ScanningReplicaSetMonitorConcurrentTest : public ThreadPoolExecutorTest {
-protected:
- void setUp() {
- ReplicaSetMonitorProtocolTestUtil::setRSMProtocol(ReplicaSetMonitorProtocol::kScanning);
- auto serviceContext = ServiceContext::make();
- ThreadPoolExecutorTest::setUp();
- launchExecutorThread();
- _startTime = getNet()->now();
- setGlobalServiceContext(std::move(serviceContext));
- }
-
- void tearDown() {
- shutdownExecutorThread();
- joinExecutorThread();
- ReplicaSetMonitor::cleanup();
- ThreadPoolExecutorTest::tearDown();
- ReplicaSetMonitorProtocolTestUtil::resetRSMProtocol();
- }
-
- bool hasReadyRequests() {
- NetworkInterfaceMock::InNetworkGuard ing(getNet());
- return getNet()->hasReadyRequests();
- }
-
- Milliseconds elapsed() {
- return getNet()->now() - _startTime;
- }
-
- long long elapsedMS() {
- return durationCount<Milliseconds>(elapsed());
- }
-
- void processReadyRequests(MockReplicaSet& replSet) {
- while (hasReadyRequests()) {
- InNetworkGuard guard(getNet());
- auto noi = getNet()->getNextReadyRequest();
- respondToCommand(guard, replSet, noi);
- }
- }
-
- // Provide the MockReplicaSet's default response to a command such as "isMaster". Pass an
- // InNetworkGuard to prove you have called NetworkInterfaceMock::enterNetwork().
- void respondToCommand(const InNetworkGuard& guard,
- MockReplicaSet& replSet,
- NetworkOperationIterator& noi) {
- const auto net = getNet();
- const auto request = noi->getRequest();
- _numChecks[request.target]++;
- LOGV2_DEBUG(20192,
- 2,
- "Got mock network operation",
- "elapsed"_attr = elapsed(),
- "request"_attr = request.toString());
- const auto node = replSet.getNode(request.target.toString());
- if (node->isRunning()) {
- const auto opmsg = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj);
- const auto reply = node->runCommand(request.id, opmsg)->getCommandReply();
- LOGV2_DEBUG(20193, 2, "Replying", "reply"_attr = reply);
- net->scheduleSuccessfulResponse(noi, RemoteCommandResponse(reply, Milliseconds(0)));
- } else {
- LOGV2_DEBUG(20194, 2, "Black hole");
- net->blackHole(noi);
- }
- net->runReadyNetworkOperations();
- }
-
- template <typename Duration>
- void advanceTime(Duration d) {
- const auto net = getNet();
- InNetworkGuard guard(net);
-
- // Operations can happen inline with advanceTime(), so log before and after the call.
- LOGV2_DEBUG(20195,
- 3,
- "Advancing time",
- "elapsedStart"_attr = elapsed(),
- "elapsedEnd"_attr = (elapsed() + d));
- net->advanceTime(net->now() + d);
- LOGV2_DEBUG(20196, 3, "Advanced time", "elapsed"_attr = elapsed());
- }
-
- int getNumChecks(HostAndPort host) {
- return _numChecks[host];
- }
-
- const ReadPreferenceSetting primaryOnly =
- ReadPreferenceSetting(mongo::ReadPreference::PrimaryOnly, TagSet());
- const ReadPreferenceSetting secondaryOnly =
- ReadPreferenceSetting(mongo::ReadPreference::SecondaryOnly, TagSet());
- const ReadPreferenceSetting secondaryWithTags =
- ReadPreferenceSetting(mongo::ReadPreference::SecondaryOnly,
- TagSet(BSON_ARRAY(BSON("nonexistent"
- << "tag"))));
- ReplicaSetChangeNotifier& getNotifier() {
- return _notifier;
- }
-
-private:
- unittest::MinimumLoggedSeverityGuard _networkSeverityGuard{logv2::LogComponent::kNetwork,
- logv2::LogSeverity::Debug(2)};
-
- std::map<HostAndPort, int> _numChecks;
- Date_t _startTime;
-
- ReplicaSetChangeNotifier _notifier;
-};
-
-// If one node is unresponsive and the available node doesn't satisfy our read preference, the
-// replica set monitor should re-check the available node every kExpeditedRefreshPeriod = 500ms
-// until expiration.
-//
-// 1. Create a replica set with two secondaries, Node 0 and Node 1
-// 2. Begin two ReplicaSetMonitor::getHostOrRefresh calls with primaryOnly and secondaryOnly prefs
-// 3. Node 0 responds but Node 1 does not
-// 4. Assert the ReplicaSetMonitor rechecks Node 0 periodically while waiting for Node 1
-// 6. At 2 seconds, Node 0 becomes primary
-// 7. At 2.5 seconds the monitor rechecks Node 0 and discovers it's primary. Assert that
-// getHostOrRefresh(primaryOnly) succeeds.
-// 8. At 5 seconds the Node 1 check times out, assert getHostOrRefresh(secondaryOnly) fails
-#if 0
-TEST_F(ScanningReplicaSetMonitorConcurrentTest, RechecksAvailableNodesUntilExpiration) {
- MockReplicaSet replSet("test", 2, false /* hasPrimary */, false /* dollarPrefixHosts */);
- const auto node0 = HostAndPort(replSet.getSecondaries()[0]);
- const auto node1 = HostAndPort(replSet.getSecondaries()[1]);
- auto state = std::make_shared<ScanningReplicaSetMonitor::SetState>(
- replSet.getURI(), &getNotifier(), &getExecutor());
- auto monitor = std::make_shared<ScanningReplicaSetMonitor>(state);
-
- // Node 1 is unresponsive.
- replSet.kill(replSet.getSecondaries()[1]);
- monitor->init();
-
- // ReplicaSetMonitor deliberately completes a future when a primary is discovered or the scan
- // ends, even if the future expires sooner. Thus timeouts don't matter so long as they're less
- // than the scan's duration, which is socketTimeoutSecs = 5 seconds.
- const auto primaryFuture = monitor->getHostOrRefresh(primaryOnly, Seconds(4));
- const auto secondaryFuture = monitor->getHostOrRefresh(secondaryOnly, Milliseconds(4800));
-
- // Monitor rechecks Node 0 every 500ms, Node 1 times out after 5 seconds.
- while (elapsed() <= Seconds(20)) {
- processReadyRequests(replSet);
-
- // After 2 seconds, make Node 0 primary.
- if (elapsed() == Milliseconds(2100)) {
- replSet.setPrimary(node0.toString());
- }
-
- if (elapsed() < Milliseconds(2500)) {
- ASSERT(!primaryFuture.isReady());
- }
-
- // The monitor discovers Node 0 is primary, getHostOrRefresh(primaryOnly) succeeds.
- if (elapsed() == Milliseconds(2500)) {
- ASSERT(primaryFuture.isReady());
- ASSERT_EQ(primaryFuture.get(), node0);
- }
-
- // After 5 seconds, getHostOrRefresh(secondaryOnly) receives its timeout.
- if (elapsed() < Milliseconds(5000)) {
- ASSERT(!secondaryFuture.isReady());
- ASSERT_EQ(getNumChecks(node0), 1 + (elapsedMS() / 500));
- ASSERT_EQ(getNumChecks(node1), 1);
- } else {
- ASSERT(secondaryFuture.isReady());
- ASSERT_NOT_OK(secondaryFuture.getNoThrow());
- }
-
- // Once Node 1 times out, monitoring slows to the usual 30-second interval.
- if (elapsed() >= Milliseconds(5000)) {
- ASSERT_EQ(getNumChecks(node0), 11);
- ASSERT_EQ(getNumChecks(node1), 1);
- }
-
- advanceTime(Milliseconds(100));
- }
-}
-#endif // 0
-
-// Like previous test, but simulate a stepdown and election while waiting for unresponsive node.
-//
-// 1. Create a replica set with three secondaries, Node 0, Node 1, and Node 2
-// 2. Begin two ReplicaSetMonitor::getHostOrRefresh calls with primaryOnly and secondaryOnly prefs
-// 3. Node 0 and 1 respond as secondaries, Node 2 does not respond
-// 4. After 1 second, Node 0 becomes primary
-// 5. After 2 seconds, Node 0 steps down
-// 6. After 3 seconds, Node 1 becomes primary
-TEST_F(ScanningReplicaSetMonitorConcurrentTest, StepdownAndElection) {
- MockReplicaSet replSet("test", 3, false /* hasPrimary */, false /* dollarPrefixHosts */);
- const auto node0 = HostAndPort(replSet.getSecondaries()[0]);
- const auto node1 = HostAndPort(replSet.getSecondaries()[1]);
- const auto node2 = HostAndPort(replSet.getSecondaries()[2]);
- auto state = std::make_shared<ScanningReplicaSetMonitor::SetState>(
- replSet.getURI(), &getNotifier(), &getExecutor());
- auto monitor = std::make_shared<ScanningReplicaSetMonitor>(state);
-
- // Node 2 is unresponsive.
- replSet.kill(replSet.getSecondaries()[2]);
- monitor->init();
-
- auto primaryFuture = monitor->getHostOrRefresh(primaryOnly, Seconds(4));
- auto unsatisfiableFuture = monitor->getHostOrRefresh(secondaryWithTags, Milliseconds(4250));
-
- // Receive first isMasters in any order. Nodes 0 and 1 respond, Node 2 doesn't.
- // Then monitor rechecks Node 0 and 1 every 500ms, Node 2 times out after 5 seconds.
- while (elapsed() <= Seconds(5)) {
- processReadyRequests(replSet);
-
- // After 1 second, make Node 0 primary.
- if (elapsed() == Milliseconds(1100)) {
- replSet.setPrimary(node0.toString());
- }
-
- if (elapsed() < Milliseconds(1500)) {
- ASSERT(!primaryFuture.isReady());
- }
-
- // The monitor discovers Node 0 is primary, getHostOrRefresh(primaryOnly) succeeds.
- if (elapsed() == Milliseconds(1500)) {
- ASSERT(primaryFuture.isReady());
- ASSERT_EQ(primaryFuture.get(), node0);
- }
-
- // After 2 seconds, Node 0 steps down.
- if (elapsed() == Milliseconds(2100)) {
- replSet.setPrimary("");
- }
-
- // The monitor discovers Node 0 is secondary.
- if (elapsed() == Milliseconds(2500)) {
- primaryFuture = monitor->getHostOrRefresh(primaryOnly, Seconds(4));
- ASSERT(!primaryFuture.isReady());
- }
-
- // After 3 seconds, make Node 1 primary.
- if (elapsed() == Milliseconds(3100)) {
- replSet.setPrimary(node1.toString());
- }
-
- // The monitor discovers Node 1 is primary.
- if (elapsed() == Milliseconds(3500)) {
- ASSERT(primaryFuture.isReady());
- ASSERT_EQ(primaryFuture.get(), node1);
- }
-
- // At 4.5 seconds, getHostOrRefresh(secondaryWithTags) receives its timeout.
- if (elapsed() < Milliseconds(4500)) {
- ASSERT(!unsatisfiableFuture.isReady());
- } else {
- ASSERT(unsatisfiableFuture.isReady());
- ASSERT_NOT_OK(unsatisfiableFuture.getNoThrow());
- }
-
- if (elapsed() < Milliseconds(5000)) {
- // New getHostOrRefresh calls can trigger checks faster than every 500ms, so "+ 2".
- ASSERT_LTE(getNumChecks(node0), elapsedMS() / 500 + 2);
- ASSERT_LTE(getNumChecks(node1), elapsedMS() / 500 + 2);
- ASSERT_EQ(getNumChecks(node2), 1);
- }
-
- advanceTime(Milliseconds(100));
- }
-}
-
-// Check that isMaster is being called at most every 500ms.
-//
-// 1. Create a replica set with two secondaries, Node 0 and Node 1
-// 2. Begin a ReplicaSetMonitor::getHostOrRefresh call with primaryOnly
-// 3. Node 0 responds but Node 1 does not
-// 4. After 0.5s call ReplicaSetMonitor::getHostOrRefresh again
-TEST_F(ScanningReplicaSetMonitorConcurrentTest, IsMasterFrequency) {
- MockReplicaSet replSet("test", 2, /* hasPrimary = */ false, /* dollarPrefixHosts = */ false);
- const auto node0 = HostAndPort(replSet.getSecondaries()[0]);
- const auto node1 = HostAndPort(replSet.getSecondaries()[1]);
-
- auto state = std::make_shared<ScanningReplicaSetMonitor::SetState>(
- replSet.getURI(), &getNotifier(), &getExecutor());
- auto monitor = std::make_shared<ScanningReplicaSetMonitor>(state);
-
- // Node 1 is unresponsive.
- replSet.kill(replSet.getSecondaries()[1]);
- monitor->init();
-
- std::vector<SemiFuture<HostAndPort>> primaryFutures;
- primaryFutures.push_back(monitor->getHostOrRefresh(primaryOnly, Seconds(4)));
-
- // Because Node 1 is unresponsive, the monitor rechecks Node 0 every 500ms
- // until Node 1 times out after 5 seconds.
- auto checkUntil = [&](auto timeToStop, auto func) {
- while (elapsed() < timeToStop) {
- processReadyRequests(replSet);
- func();
- // all primaryFutures are not ready, because the monitor cannot find the primary
- for (SemiFuture<HostAndPort>& future : primaryFutures) {
- ASSERT(!future.isReady());
- }
- advanceTime(Milliseconds(100));
- }
- };
-
- // Up until 500ms there is only one isMaster call.
- // At 500ms, the monitor rechecks node 0.
- checkUntil(Milliseconds(500), [&]() {
- ASSERT_EQ(getNumChecks(node0), 1);
- ASSERT_EQ(getNumChecks(node1), 1);
- });
-
- // Triggers isMaster calls that will be delayed until 1000ms
- checkUntil(Milliseconds(1000), [&]() {
- // this should schedule a new isMaster call at 1000ms and cancel the
- // previous job scheduled for the same time
- primaryFutures.push_back(monitor->getHostOrRefresh(primaryOnly, Seconds(4)));
- ASSERT_EQ(getNumChecks(node0), 2);
- ASSERT_EQ(getNumChecks(node1), 1);
- });
-
- // At 1000ms, only one scheduled isMaster call runs, because all others have been canceled
- checkUntil(Milliseconds(1500), [&]() {
- ASSERT_EQ(getNumChecks(node0), 3);
- ASSERT_EQ(getNumChecks(node1), 1);
- });
-
- advanceTime(Seconds(5));
- processReadyRequests(replSet);
-
- for (auto& future : primaryFutures) {
- ASSERT(future.isReady());
- }
-}
-
-// Check that requests actually experience timeout despite in-flight isMasters
-TEST_F(ScanningReplicaSetMonitorConcurrentTest, RecheckUntilTimeout) {
- MockReplicaSet replSet("test", 2, /* hasPrimary = */ false, /* dollarPrefixHosts = */ false);
- const auto node0 = HostAndPort(replSet.getSecondaries()[0]);
- const auto node1 = HostAndPort(replSet.getSecondaries()[1]);
-
- auto state = std::make_shared<ScanningReplicaSetMonitor::SetState>(
- replSet.getURI(), &getNotifier(), &getExecutor());
- auto monitor = std::make_shared<ScanningReplicaSetMonitor>(state);
-
- // Node 1 is unresponsive.
- replSet.kill(replSet.getSecondaries()[1]);
- monitor->init();
-
- // Set the timeout to be more than 500ms before the third timeout on node 1. This way the
- // isMaster to node 1 is in flight and the timeout occurs at 14500 when the isMaster to node
- // 0 succeeds.
- auto hostFuture = monitor->getHostOrRefresh(primaryOnly, Milliseconds(14250));
-
- auto checkUntil = [&](auto timeToStop, auto func) {
- while (elapsed() < timeToStop) {
- processReadyRequests(replSet);
- func();
- advanceTime(Milliseconds(100));
- }
-
- processReadyRequests(replSet);
- };
-
- // Every 500ms, the monitor rechecks node 0 after the previous successful isMaster.
- // Every 5s, the monitor rechecks node 1 after the previous isMaster experiences timeout.
- constexpr auto kTimeoutPeriodMS =
- Milliseconds(ScanningReplicaSetMonitor::kCheckTimeout).count() +
- ScanningReplicaSetMonitor::kExpeditedRefreshPeriod.count();
- checkUntil(Milliseconds(14500), [&]() {
- ASSERT_EQ(getNumChecks(node0),
- elapsedMS() / ScanningReplicaSetMonitor::kExpeditedRefreshPeriod.count() + 1);
- ASSERT_EQ(getNumChecks(node1), elapsedMS() / kTimeoutPeriodMS + 1);
- ASSERT(!hostFuture.isReady());
- });
-
-
- ASSERT(hostFuture.isReady());
-}
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/client/scanning_replica_set_monitor_test_fixture.cpp b/src/mongo/client/scanning_replica_set_monitor_test_fixture.cpp
deleted file mode 100644
index e97fc2a5ff3..00000000000
--- a/src/mongo/client/scanning_replica_set_monitor_test_fixture.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * Copyright (C) 2020-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/client/scanning_replica_set_monitor_test_fixture.h"
-
-namespace mongo {
-
-/**
- * Setup every test to use replicaSetMonitorProtocol::kScanning.
- */
-void ScanningReplicaSetMonitorTest::setUp() {
- setGlobalServiceContext(ServiceContext::make());
- ReplicaSetMonitorProtocolTestUtil::setRSMProtocol(ReplicaSetMonitorProtocol::kScanning);
- ReplicaSetMonitor::cleanup();
-}
-
-void ScanningReplicaSetMonitorTest::tearDown() {
- ReplicaSetMonitor::cleanup();
- ReplicaSetMonitorProtocolTestUtil::resetRSMProtocol();
-}
-
-const std::vector<HostAndPort> ScanningReplicaSetMonitorTest::basicSeeds = {
- HostAndPort("a"), HostAndPort("b"), HostAndPort("c")};
-const std::set<HostAndPort> ScanningReplicaSetMonitorTest::basicSeedsSet = {std::begin(basicSeeds),
- std::end(basicSeeds)};
-const MongoURI ScanningReplicaSetMonitorTest::basicUri(ConnectionString::forReplicaSet(kSetName,
- basicSeeds));
-} // namespace mongo
diff --git a/src/mongo/client/scanning_replica_set_monitor_test_fixture.h b/src/mongo/client/scanning_replica_set_monitor_test_fixture.h
deleted file mode 100644
index e7ee6bf7723..00000000000
--- a/src/mongo/client/scanning_replica_set_monitor_test_fixture.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/**
- * Copyright (C) 2020-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include <set>
-#include <type_traits>
-#include <vector>
-
-#include "mongo/client/replica_set_change_notifier.h"
-#include "mongo/client/replica_set_monitor_protocol_test_util.h"
-#include "mongo/client/scanning_replica_set_monitor.h"
-#include "mongo/client/scanning_replica_set_monitor_internal.h"
-#include "mongo/unittest/unittest.h"
-
-namespace mongo {
-
-// NOTE: Unless stated otherwise, all tests assume exclusive access to state belongs to the
-// current (only) thread, so they do not lock SetState::mutex before examining state. This is
-// NOT something that non-test code should do.
-
-class ScanningReplicaSetMonitorTest : public unittest::Test {
-public:
- // Pull in nested types
- using SetState = ScanningReplicaSetMonitor::SetState;
- using Node = SetState::Node;
-
- using IsMasterReply = ScanningReplicaSetMonitor::IsMasterReply;
-
- using Refresher = ScanningReplicaSetMonitor::Refresher;
- using NextStep = Refresher::NextStep;
-
- static constexpr StringData kSetName = "name"_sd;
-
- ScanningReplicaSetMonitorTest() = default;
- virtual ~ScanningReplicaSetMonitorTest() = default;
-
- template <typename... Args>
- using StateIsConstructible =
- std::enable_if_t<std::is_constructible_v<SetState,
- Args...,
- ReplicaSetChangeNotifier* const,
- executor::TaskExecutor* const>>;
-
- template <typename... Args, typename = StateIsConstructible<Args...>>
- auto makeState(Args&&... args) {
- return std::make_shared<ScanningReplicaSetMonitor::SetState>(
- std::forward<Args>(args)..., &_notifier, nullptr);
- }
-
- void setUp() override;
- void tearDown() override;
-
- static const std::vector<HostAndPort> basicSeeds;
- static const std::set<HostAndPort> basicSeedsSet;
- static const MongoURI basicUri;
-
-protected:
- ReplicaSetChangeNotifier _notifier;
-};
-
-} // namespace mongo