/** * Copyright (C) 2012 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * 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 * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * 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 GNU Affero General 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/client/connpool.h" #include "mongo/client/dbclientinterface.h" #include "mongo/client/dbclient_rs.h" #include "mongo/client/replica_set_monitor.h" #include "mongo/client/replica_set_monitor_internal.h" #include "mongo/dbtests/mock/mock_conn_registry.h" #include "mongo/dbtests/mock/mock_replica_set.h" #include "mongo/unittest/unittest.h" #include #include #include using std::map; using std::vector; using std::set; using std::string; using boost::scoped_ptr; using mongo::BSONObj; using mongo::BSONObjBuilder; using mongo::BSONArray; using mongo::BSONArrayBuilder; using mongo::BSONElement; using mongo::ConnectionString; using mongo::HostAndPort; using mongo::MockReplicaSet; using mongo::ReadPreference; using mongo::ReadPreferenceSetting; using mongo::repl::ReplicaSetConfig; using mongo::ReplicaSetMonitor; using mongo::ReplicaSetMonitorPtr; using mongo::ScopedDbConnection; using mongo::TagSet; // Pull nested types to top-level scope typedef ReplicaSetMonitor::IsMasterReply IsMasterReply; typedef ReplicaSetMonitor::ScanState ScanState; typedef ReplicaSetMonitor::ScanStatePtr ScanStatePtr; typedef ReplicaSetMonitor::SetState SetState; typedef ReplicaSetMonitor::SetStatePtr SetStatePtr; typedef ReplicaSetMonitor::Refresher Refresher; typedef Refresher::NextStep NextStep; typedef ScanState::UnconfirmedReplies UnconfirmedReplies; typedef SetState::Node Node; typedef SetState::Nodes Nodes; namespace mongo_test { bool isCompatible(const Node& node, ReadPreference pref, const TagSet& tagSet) { set seeds; seeds.insert(node.host); SetState set("name", seeds); set.nodes.push_back(node); ReadPreferenceSetting criteria(pref, tagSet); return !set.getMatchingHost(criteria).empty(); } HostAndPort selectNode(const vector& nodes, ReadPreference pref, const TagSet& tagSet, int latencyThresholdMillis, bool* isPrimarySelected) { invariant(!nodes.empty()); set seeds; seeds.insert(nodes.front().host); SetState set("name", seeds); set.nodes = nodes; set.latencyThresholdMicros = latencyThresholdMillis * 1000; ReadPreferenceSetting criteria(pref, tagSet); HostAndPort out = set.getMatchingHost(criteria); if (isPrimarySelected) *isPrimarySelected = !out.empty() && set.findNode(out)->isMaster; return out; } 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(ReplSetMonitorNode, SimpleGoodMatch) { Node node(((HostAndPort()))); node.tags = BSON("dc" << "sf"); ASSERT(node.matches(BSON("dc" << "sf"))); } TEST(ReplSetMonitorNode, SimpleBadMatch) { Node node((HostAndPort())); node.tags = BSON("dc" << "nyc"); ASSERT(!node.matches(BSON("dc" << "sf"))); } TEST(ReplSetMonitorNode, ExactMatch) { Node node((HostAndPort())); node.tags = SampleTags; ASSERT(node.matches(SampleIsMasterDoc["tags"].Obj())); } TEST(ReplSetMonitorNode, EmptyTag) { Node node((HostAndPort())); node.tags = SampleTags; ASSERT(node.matches(BSONObj())); } TEST(ReplSetMonitorNode, MemberNoTagMatchesEmptyTag) { Node node((HostAndPort())); node.tags = NoTags; ASSERT(node.matches(BSONObj())); } TEST(ReplSetMonitorNode, MemberNoTagDoesNotMatch) { Node node((HostAndPort())); node.tags = NoTags; ASSERT(!node.matches(BSON("dc" << "NYC"))); } TEST(ReplSetMonitorNode, IncompleteMatch) { Node node((HostAndPort())); node.tags = SampleTags; ASSERT(!node.matches(BSON("dc" << "NYC" << "p" << "2" << "hello" << "world"))); } TEST(ReplSetMonitorNode, PartialMatch) { Node node((HostAndPort())); node.tags = SampleTags; ASSERT(node.matches(BSON("dc" << "NYC" << "p" << "2"))); } TEST(ReplSetMonitorNode, SingleTagCrit) { Node node((HostAndPort())); node.tags = SampleTags; ASSERT(node.matches(BSON("p" << "2"))); } TEST(ReplSetMonitorNode, BadSingleTagCrit) { Node node((HostAndPort())); node.tags = SampleTags; ASSERT(!node.matches(BSON("dc" << "SF"))); } TEST(ReplSetMonitorNode, NonExistingFieldTag) { Node node((HostAndPort())); node.tags = SampleTags; ASSERT(!node.matches(BSON("noSQL" << "Mongo"))); } TEST(ReplSetMonitorNode, UnorederedMatching) { Node node((HostAndPort())); node.tags = SampleTags; ASSERT(node.matches(BSON("p" << "2" << "dc" << "NYC"))); } TEST(ReplSetMonitorNode, SameValueDiffKey) { Node node((HostAndPort())); node.tags = SampleTags; ASSERT(!node.matches(BSON("datacenter" << "NYC"))); } TEST(ReplSetMonitorNode, 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(ReplSetMonitorNode, 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(ReplSetMonitorNode, 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(ReplSetMonitorNode, 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(ReplSetMonitorNode, 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(ReplSetMonitorNode, 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(ReplSetMonitorNode, 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(ReplSetMonitorNode, 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)); } class NodeSetFixtures { public: static vector getThreeMemberWithTags(); }; vector NodeSetFixtures::getThreeMemberWithTags() { vector 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; } class TagSetFixtures { public: static BSONArray getDefaultSet(); static BSONArray getP2Tag(); static BSONArray getSingleNoMatchTag(); static BSONArray getMultiNoMatchTag(); }; BSONArray TagSetFixtures::getDefaultSet() { BSONArrayBuilder arrayBuilder; arrayBuilder.append(BSONObj()); return arrayBuilder.arr(); } BSONArray TagSetFixtures::getP2Tag() { BSONArrayBuilder arrayBuilder; arrayBuilder.append(BSON("p" << "2")); return arrayBuilder.arr(); } BSONArray TagSetFixtures::getSingleNoMatchTag() { BSONArrayBuilder arrayBuilder; arrayBuilder.append(BSON("k" << "x")); return arrayBuilder.arr(); } BSONArray TagSetFixtures::getMultiNoMatchTag() { BSONArrayBuilder arrayBuilder; arrayBuilder.append(BSON("mongo" << "db")); arrayBuilder.append(BSON("by" << "10gen")); return arrayBuilder.arr(); } TEST(ReplSetMonitorReadPref, PrimaryOnly) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getDefaultSet()); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryOnly, tags, 3, &isPrimarySelected); ASSERT(isPrimarySelected); ASSERT_EQUALS("b", host.host()); } TEST(ReplSetMonitorReadPref, PrimaryOnlyPriNotOk) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getDefaultSet()); nodes[1].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryOnly, tags, 3, &isPrimarySelected); ASSERT(host.empty()); } TEST(ReplSetMonitorReadPref, PrimaryMissing) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getDefaultSet()); nodes[1].isMaster = false; bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryOnly, tags, 3, &isPrimarySelected); ASSERT(host.empty()); } TEST(ReplSetMonitorReadPref, PriPrefWithPriOk) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getDefaultSet()); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryPreferred, tags, 1, &isPrimarySelected); ASSERT(isPrimarySelected); ASSERT_EQUALS("b", host.host()); } TEST(ReplSetMonitorReadPref, PriPrefWithPriNotOk) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getDefaultSet()); nodes[1].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryPreferred, tags, 1, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT(host.host() == "a" || host.host() == "c"); } TEST(ReplSetMonitorReadPref, SecOnly) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getDefaultSet()); nodes[2].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryOnly, tags, 1, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST(ReplSetMonitorReadPref, SecOnlyOnlyPriOk) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getDefaultSet()); nodes[0].markFailed(); nodes[2].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryOnly, tags, 1, &isPrimarySelected); ASSERT(host.empty()); } TEST(ReplSetMonitorReadPref, SecPref) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getDefaultSet()); nodes[2].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, tags, 1, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST(ReplSetMonitorReadPref, SecPrefWithNoSecOk) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getDefaultSet()); nodes[0].markFailed(); nodes[2].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, tags, 1, &isPrimarySelected); ASSERT(isPrimarySelected); ASSERT_EQUALS("b", host.host()); } TEST(ReplSetMonitorReadPref, SecPrefWithNoNodeOk) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getDefaultSet()); nodes[0].markFailed(); nodes[1].markFailed(); nodes[2].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, tags, 1, &isPrimarySelected); ASSERT(host.empty()); } TEST(ReplSetMonitorReadPref, NearestAllLocal) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getDefaultSet()); 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(ReplSetMonitorReadPref, NearestOneLocal) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getDefaultSet()); 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(ReplSetMonitorReadPref, PriOnlyWithTagsNoMatch) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getP2Tag()); 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(ReplSetMonitorReadPref, PriPrefPriNotOkWithTags) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getP2Tag()); nodes[1].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryPreferred, tags, 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("c", host.host()); } TEST(ReplSetMonitorReadPref, PriPrefPriOkWithTagsNoMatch) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getSingleNoMatchTag()); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryPreferred, tags, 3, &isPrimarySelected); ASSERT(isPrimarySelected); ASSERT_EQUALS("b", host.host()); } TEST(ReplSetMonitorReadPref, PriPrefPriNotOkWithTagsNoMatch) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getSingleNoMatchTag()); nodes[1].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryPreferred, tags, 3, &isPrimarySelected); ASSERT(host.empty()); } TEST(ReplSetMonitorReadPref, SecOnlyWithTags) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getP2Tag()); bool isPrimarySelected; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryOnly, tags, 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("c", host.host()); } TEST(ReplSetMonitorReadPref, SecOnlyWithTagsMatchOnlyPri) { vector nodes = NodeSetFixtures::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(ReplSetMonitorReadPref, SecPrefWithTags) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getP2Tag()); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, tags, 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("c", host.host()); } TEST(ReplSetMonitorReadPref, SecPrefSecNotOkWithTags) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); BSONArrayBuilder arrayBuilder; arrayBuilder.append(BSON("dc" << "nyc")); TagSet tags(arrayBuilder.arr()); nodes[2].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, tags, 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST(ReplSetMonitorReadPref, SecPrefPriOkWithTagsNoMatch) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getSingleNoMatchTag()); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, tags, 3, &isPrimarySelected); ASSERT(isPrimarySelected); ASSERT_EQUALS("b", host.host()); } TEST(ReplSetMonitorReadPref, SecPrefPriNotOkWithTagsNoMatch) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getSingleNoMatchTag()); nodes[1].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, tags, 3, &isPrimarySelected); ASSERT(host.empty()); } TEST(ReplSetMonitorReadPref, SecPrefPriOkWithSecNotMatchTag) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getSingleNoMatchTag()); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, tags, 3, &isPrimarySelected); ASSERT(isPrimarySelected); ASSERT_EQUALS("b", host.host()); } TEST(ReplSetMonitorReadPref, NearestWithTags) { vector nodes = NodeSetFixtures::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(ReplSetMonitorReadPref, NearestWithTagsNoMatch) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getSingleNoMatchTag()); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_Nearest, tags, 3, &isPrimarySelected); ASSERT(host.empty()); } TEST(ReplSetMonitorReadPref, MultiPriOnlyTag) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getMultiNoMatchTag()); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryOnly, tags, 3, &isPrimarySelected); ASSERT(isPrimarySelected); ASSERT_EQUALS("b", host.host()); } TEST(ReplSetMonitorReadPref, MultiPriOnlyPriNotOkTag) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getMultiNoMatchTag()); nodes[1].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryOnly, tags, 3, &isPrimarySelected); ASSERT(host.empty()); } TEST(ReplSetMonitorReadPref, PriPrefPriOk) { vector nodes = NodeSetFixtures::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 MultiTags: public mongo::unittest::Test { public: vector getNodes() const { return NodeSetFixtures::getThreeMemberWithTags(); } const TagSet& getMatchesFirstTagSet() { if (matchFirstTags.get() != NULL) { 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() != NULL) { 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& getMatchesLastTagSet() { if (matchLastTags.get() != NULL) { return *matchLastTags; } 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")); matchLastTags.reset(new TagSet(arrayBuilder.arr())); return *matchLastTags; } const TagSet& getMatchesPriTagSet() { if (matchPriTags.get() != NULL) { return *matchPriTags; } BSONArrayBuilder arrayBuilder; arrayBuilder.append(BSON("dc" << "sf")); arrayBuilder.append(BSON("p" << "1")); matchPriTags.reset(new TagSet(arrayBuilder.arr())); return *matchPriTags; } private: scoped_ptr matchFirstTags; scoped_ptr matchSecondTags; scoped_ptr matchLastTags; scoped_ptr matchPriTags; }; TEST_F(MultiTags, MultiTagsMatchesFirst) { vector nodes = getNodes(); nodes[1].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryPreferred, getMatchesFirstTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST_F(MultiTags, PriPrefPriNotOkMatchesFirstNotOk) { vector nodes = getNodes(); nodes[0].markFailed(); nodes[1].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryPreferred, getMatchesFirstTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("c", host.host()); } TEST_F(MultiTags, PriPrefPriNotOkMatchesSecondTest) { vector nodes = getNodes(); nodes[1].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryPreferred, getMatchesSecondTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("c", host.host()); } TEST_F(MultiTags, PriPrefPriNotOkMatchesSecondNotOkTest) { vector nodes = getNodes(); nodes[1].markFailed(); nodes[2].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryPreferred, getMatchesSecondTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST_F(MultiTags, PriPrefPriNotOkMatchesLastTest) { vector nodes = getNodes(); nodes[1].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryPreferred, getMatchesLastTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST_F(MultiTags, PriPrefPriNotOkMatchesLastNotOkTest) { vector nodes = getNodes(); nodes[0].markFailed(); nodes[1].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryPreferred, getMatchesLastTagSet(), 3, &isPrimarySelected); ASSERT(host.empty()); } TEST(MultiTags, PriPrefPriOkNoMatch) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getMultiNoMatchTag()); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryPreferred, tags, 3, &isPrimarySelected); ASSERT(isPrimarySelected); ASSERT_EQUALS("b", host.host()); } TEST(MultiTags, PriPrefPriNotOkNoMatch) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getMultiNoMatchTag()); nodes[1].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_PrimaryPreferred, tags, 3, &isPrimarySelected); ASSERT(host.empty()); } TEST_F(MultiTags, SecOnlyMatchesFirstTest) { vector nodes = getNodes(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryOnly, getMatchesFirstTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST_F(MultiTags, SecOnlyMatchesFirstNotOk) { vector nodes = getNodes(); nodes[0].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryOnly, getMatchesFirstTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("c", host.host()); } TEST_F(MultiTags, SecOnlyMatchesSecond) { vector nodes = getNodes(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryOnly, getMatchesSecondTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("c", host.host()); } TEST_F(MultiTags, SecOnlyMatchesSecondNotOk) { vector nodes = getNodes(); nodes[2].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryOnly, getMatchesSecondTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST_F(MultiTags, SecOnlyMatchesLast) { vector nodes = getNodes(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryOnly, getMatchesLastTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST_F(MultiTags, SecOnlyMatchesLastNotOk) { vector nodes = getNodes(); nodes[0].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryOnly, getMatchesLastTagSet(), 3, &isPrimarySelected); ASSERT(host.empty()); } TEST_F(MultiTags, SecOnlyMultiTagsWithPriMatch) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryOnly, getMatchesPriTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST_F(MultiTags, SecOnlyMultiTagsNoMatch) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getMultiNoMatchTag()); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryOnly, tags, 3, &isPrimarySelected); ASSERT(host.empty()); } TEST_F(MultiTags, SecPrefMatchesFirst) { vector nodes = getNodes(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, getMatchesFirstTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST_F(MultiTags, SecPrefMatchesFirstNotOk) { vector nodes = getNodes(); nodes[0].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, getMatchesFirstTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("c", host.host()); } TEST_F(MultiTags, SecPrefMatchesSecond) { vector nodes = getNodes(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, getMatchesSecondTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("c", host.host()); } TEST_F(MultiTags, SecPrefMatchesSecondNotOk) { vector nodes = getNodes(); nodes[2].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, getMatchesSecondTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST_F(MultiTags, SecPrefMatchesLast) { vector nodes = getNodes(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, getMatchesLastTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST_F(MultiTags, SecPrefMatchesLastNotOk) { vector nodes = getNodes(); nodes[0].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, getMatchesLastTagSet(), 3, &isPrimarySelected); ASSERT(isPrimarySelected); ASSERT_EQUALS("b", host.host()); } TEST_F(MultiTags, SecPrefMultiTagsWithPriMatch) { vector nodes = getNodes(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, getMatchesPriTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST(MultiTags, SecPrefMultiTagsNoMatch) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getMultiNoMatchTag()); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, tags, 3, &isPrimarySelected); ASSERT(isPrimarySelected); ASSERT_EQUALS("b", host.host()); } TEST(MultiTags, SecPrefMultiTagsNoMatchPriNotOk) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getMultiNoMatchTag()); nodes[1].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_SecondaryPreferred, tags, 3, &isPrimarySelected); ASSERT(host.empty()); } TEST_F(MultiTags, NearestMatchesFirst) { vector nodes = getNodes(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_Nearest, getMatchesFirstTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST(MultiTags, NearestMatchesFirstNotOk) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); BSONArrayBuilder arrayBuilder; arrayBuilder.append(BSON("p" << "1")); arrayBuilder.append(BSON("dc" << "sf")); TagSet tags(arrayBuilder.arr()); nodes[0].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_Nearest, tags, 3, &isPrimarySelected); ASSERT(isPrimarySelected); ASSERT_EQUALS("b", host.host()); } TEST_F(MultiTags, NearestMatchesSecond) { vector nodes = getNodes(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_Nearest, getMatchesSecondTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("c", host.host()); } TEST_F(MultiTags, NearestMatchesSecondNotOk) { vector nodes = NodeSetFixtures::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(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_Nearest, tags, 3, &isPrimarySelected); ASSERT(isPrimarySelected); ASSERT_EQUALS("b", host.host()); } TEST_F(MultiTags, NearestMatchesLast) { vector nodes = getNodes(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_Nearest, getMatchesLastTagSet(), 3, &isPrimarySelected); ASSERT(!isPrimarySelected); ASSERT_EQUALS("a", host.host()); } TEST_F(MultiTags, NeatestMatchesLastNotOk) { vector nodes = getNodes(); nodes[0].markFailed(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_Nearest, getMatchesLastTagSet(), 3, &isPrimarySelected); ASSERT(host.empty()); } TEST_F(MultiTags, NearestMultiTagsWithPriMatch) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_Nearest, getMatchesPriTagSet(), 3, &isPrimarySelected); ASSERT(isPrimarySelected); ASSERT_EQUALS("b", host.host()); } TEST(MultiTags, NearestMultiTagsNoMatch) { vector nodes = NodeSetFixtures::getThreeMemberWithTags(); TagSet tags(TagSetFixtures::getMultiNoMatchTag()); bool isPrimarySelected = false; HostAndPort host = selectNode(nodes, mongo::ReadPreference_Nearest, tags, 3, &isPrimarySelected); ASSERT(host.empty()); } TEST(TagSet, DefaultConstructorMatchesAll) { TagSet tags; ASSERT_EQUALS(tags.getTagBSON(), BSON_ARRAY(BSONObj())); } // TODO: Port these existing tests here: replmonitor_bad_seed.js, repl_monitor_refresh.js /** * Warning: Tests running this fixture cannot be run in parallel with other tests * that uses ConnectionString::setConnectionHook */ class ReplicaSetMonitorTest: public mongo::unittest::Test { protected: void setUp() { _replSet.reset(new MockReplicaSet("test", 3)); _originalConnectionHook = ConnectionString::getConnectionHook(); ConnectionString::setConnectionHook( mongo::MockConnRegistry::get()->getConnStrHook()); } void tearDown() { ConnectionString::setConnectionHook(_originalConnectionHook); ReplicaSetMonitor::cleanup(); _replSet.reset(); mongo::ScopedDbConnection::clearPool(); } MockReplicaSet* getReplSet() { return _replSet.get(); } private: ConnectionString::ConnectionHook* _originalConnectionHook; boost::scoped_ptr _replSet; }; TEST_F(ReplicaSetMonitorTest, SeedWithPriOnlySecDown) { // Test to make sure that the monitor doesn't crash when // ConnectionString::connect returns NULL MockReplicaSet* replSet = getReplSet(); replSet->kill(replSet->getSecondaries()); // Create a monitor with primary as the only seed list and the two secondaries // down so a NULL connection object will be stored for these secondaries in // the _nodes vector. const string replSetName(replSet->getSetName()); set seedList; seedList.insert(HostAndPort(replSet->getPrimary())); ReplicaSetMonitor::createIfNeeded(replSetName, seedList); replSet->kill(replSet->getPrimary()); ReplicaSetMonitorPtr monitor = ReplicaSetMonitor::get(replSet->getSetName()); // Trigger calls to Node::getConnWithRefresh monitor->startOrContinueRefresh().refreshAll(); } namespace { /** * Takes a ReplicaSetConfig and a node to remove and returns a new config with equivalent * members minus the one specified to be removed. NOTE: Does not copy over properties of the * members other than their id and host. */ ReplicaSetConfig _getConfigWithMemberRemoved( const ReplicaSetConfig& oldConfig, const HostAndPort& toRemove) { BSONObjBuilder newConfigBuilder; newConfigBuilder.append("_id", oldConfig.getReplSetName()); newConfigBuilder.append("version", oldConfig.getConfigVersion()); BSONArrayBuilder membersBuilder(newConfigBuilder.subarrayStart("members")); for (ReplicaSetConfig::MemberIterator member = oldConfig.membersBegin(); member != oldConfig.membersEnd(); ++member) { if (member->getHostAndPort() == toRemove) { continue; } membersBuilder.append(BSON("_id" << member->getId() << "host" << member->getHostAndPort().toString())); } membersBuilder.done(); ReplicaSetConfig newConfig; ASSERT_OK(newConfig.initialize(newConfigBuilder.obj())); ASSERT_OK(newConfig.validate()); return newConfig; } } // namespace // Stress test case for a node that is previously a primary being removed from the set. // This test goes through configurations with different positions for the primary node // in the host list returned from the isMaster command. The test here is to make sure // that the ReplicaSetMonitor will not crash under these situations. TEST(ReplicaSetMonitorTest, PrimaryRemovedFromSetStress) { const size_t NODE_COUNT = 5; MockReplicaSet replSet("test", NODE_COUNT); ConnectionString::ConnectionHook* originalConnHook = ConnectionString::getConnectionHook(); ConnectionString::setConnectionHook(mongo::MockConnRegistry::get()->getConnStrHook()); const string replSetName(replSet.getSetName()); set seedList; seedList.insert(HostAndPort(replSet.getPrimary())); ReplicaSetMonitor::createIfNeeded(replSetName, seedList); const ReplicaSetConfig& origConfig = replSet.getReplConfig(); mongo::ReplicaSetMonitorPtr replMonitor = ReplicaSetMonitor::get(replSetName); for (size_t idxToRemove = 0; idxToRemove < NODE_COUNT; idxToRemove++) { replSet.setConfig(origConfig); // Make sure the monitor sees the change replMonitor->startOrContinueRefresh().refreshAll(); string hostToRemove; { BSONObjBuilder monitorStateBuilder; replMonitor->appendInfo(monitorStateBuilder); BSONObj monitorState = monitorStateBuilder.done(); BSONElement hostsElem = monitorState["hosts"]; BSONElement addrElem = hostsElem[mongo::str::stream() << idxToRemove]["addr"]; hostToRemove = addrElem.String(); } replSet.setPrimary(hostToRemove); // Make sure the monitor sees the new primary replMonitor->startOrContinueRefresh().refreshAll(); mongo::repl::ReplicaSetConfig newConfig = _getConfigWithMemberRemoved( origConfig, HostAndPort(hostToRemove)); replSet.setConfig(newConfig); replSet.setPrimary(newConfig.getMemberAt(0).getHostAndPort().toString()); // Force refresh -> should not crash replMonitor->startOrContinueRefresh().refreshAll(); } ReplicaSetMonitor::cleanup(); ConnectionString::setConnectionHook(originalConnHook); mongo::ScopedDbConnection::clearPool(); } /** * Warning: Tests running this fixture cannot be run in parallel with other tests * that use ConnectionString::setConnectionHook. */ class TwoNodeWithTags: public mongo::unittest::Test { protected: void setUp() { _replSet.reset(new MockReplicaSet("test", 2)); _originalConnectionHook = ConnectionString::getConnectionHook(); ConnectionString::setConnectionHook( mongo::MockConnRegistry::get()->getConnStrHook()); mongo::repl::ReplicaSetConfig oldConfig = _replSet->getReplConfig(); mongo::BSONObjBuilder newConfigBuilder; newConfigBuilder.append("_id", oldConfig.getReplSetName()); newConfigBuilder.append("version", oldConfig.getConfigVersion()); mongo::BSONArrayBuilder membersBuilder(newConfigBuilder.subarrayStart("members")); { const string host(_replSet->getPrimary()); const mongo::repl::MemberConfig* member = oldConfig.findMemberByHostAndPort(HostAndPort(host)); membersBuilder.append(BSON("_id" << member->getId() << "host" << host << "tags" << BSON("dc" << "ny" << "num" << "1"))); } { const string host(_replSet->getSecondaries().front()); const mongo::repl::MemberConfig* member = oldConfig.findMemberByHostAndPort(HostAndPort(host)); membersBuilder.append(BSON("_id" << member->getId() << "host" << host << "tags" << BSON("dc" << "ny" << "num" << "2"))); } membersBuilder.done(); mongo::repl::ReplicaSetConfig newConfig; fassert(28572, newConfig.initialize(newConfigBuilder.done())); fassert(28571, newConfig.validate()); _replSet->setConfig(newConfig); } void tearDown() { ConnectionString::setConnectionHook(_originalConnectionHook); ReplicaSetMonitor::cleanup(); _replSet.reset(); } MockReplicaSet* getReplSet() { return _replSet.get(); } private: ConnectionString::ConnectionHook* _originalConnectionHook; boost::scoped_ptr _replSet; }; // Tests the case where the connection to secondary went bad and the replica set // monitor needs to perform a refresh of it's local view then retry the node selection // again after the refresh. TEST_F(TwoNodeWithTags, SecDownRetryNoTag) { MockReplicaSet* replSet = getReplSet(); set seedList; seedList.insert(HostAndPort(replSet->getPrimary())); ReplicaSetMonitor::createIfNeeded(replSet->getSetName(), seedList); const string secHost(replSet->getSecondaries().front()); replSet->kill(secHost); ReplicaSetMonitorPtr monitor = ReplicaSetMonitor::get(replSet->getSetName()); // Make sure monitor sees the dead secondary monitor->startOrContinueRefresh().refreshAll(); replSet->restore(secHost); HostAndPort node = monitor->getHostOrRefresh( ReadPreferenceSetting(mongo::ReadPreference_SecondaryOnly, TagSet())); ASSERT_FALSE(monitor->isPrimary(node)); ASSERT_EQUALS(secHost, node.toString()); } // Tests the case where the connection to secondary went bad and the replica set // monitor needs to perform a refresh of it's local view then retry the node selection // with tags again after the refresh. TEST_F(TwoNodeWithTags, SecDownRetryWithTag) { MockReplicaSet* replSet = getReplSet(); set seedList; seedList.insert(HostAndPort(replSet->getPrimary())); ReplicaSetMonitor::createIfNeeded(replSet->getSetName(), seedList); const string secHost(replSet->getSecondaries().front()); replSet->kill(secHost); ReplicaSetMonitorPtr monitor = ReplicaSetMonitor::get(replSet->getSetName()); // Make sure monitor sees the dead secondary monitor->startOrContinueRefresh().refreshAll(); replSet->restore(secHost); TagSet tags(BSON_ARRAY(BSON("dc" << "ny"))); HostAndPort node = monitor->getHostOrRefresh( ReadPreferenceSetting(mongo::ReadPreference_SecondaryOnly, tags)); ASSERT_FALSE(monitor->isPrimary(node)); ASSERT_EQUALS(secHost, node.toString()); } }