diff options
author | matt dannenberg <matt.dannenberg@10gen.com> | 2015-06-11 09:47:34 -0400 |
---|---|---|
committer | matt dannenberg <matt.dannenberg@10gen.com> | 2015-06-16 11:56:16 -0400 |
commit | 64573bc33c058231bed22bd5fd44c609431b5a62 (patch) | |
tree | 6daab898e0eb28cb37ac68621f7f24baeb8297af /src | |
parent | 22dd61cfe60e05b13f9bb92aee3cb4660392435b (diff) | |
download | mongo-64573bc33c058231bed22bd5fd44c609431b5a62.tar.gz |
SERVER-18385 add DryRun support to RequestVotes, VoteRequester, and TopologyCoordinator
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/repl/repl_set_request_votes_args.cpp | 11 | ||||
-rw-r--r-- | src/mongo/db/repl/repl_set_request_votes_args.h | 2 | ||||
-rw-r--r-- | src/mongo/db/repl/replication_coordinator_impl.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/repl/replication_coordinator_impl_elect_v1.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/repl/topology_coordinator_impl.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/repl/topology_coordinator_impl_test.cpp | 194 | ||||
-rw-r--r-- | src/mongo/db/repl/vote_requester.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/repl/vote_requester.h | 3 | ||||
-rw-r--r-- | src/mongo/db/repl/vote_requester_test.cpp | 119 |
9 files changed, 342 insertions, 6 deletions
diff --git a/src/mongo/db/repl/repl_set_request_votes_args.cpp b/src/mongo/db/repl/repl_set_request_votes_args.cpp index da8123ab9f6..8c29e37b29d 100644 --- a/src/mongo/db/repl/repl_set_request_votes_args.cpp +++ b/src/mongo/db/repl/repl_set_request_votes_args.cpp @@ -39,6 +39,7 @@ namespace { const std::string kCandidateIdFieldName = "candidateId"; const std::string kCommandName = "replSetRequestVotes"; const std::string kConfigVersionFieldName = "configVersion"; + const std::string kDryRunFieldName = "dryRun"; const std::string kLastCommittedOpFieldName = "lastCommittedOp"; const std::string kOkFieldName = "ok"; const std::string kOpTimeFieldName = "ts"; @@ -51,6 +52,7 @@ namespace { kCandidateIdFieldName, kCommandName, kConfigVersionFieldName, + kDryRunFieldName, kLastCommittedOpFieldName, kOpTimeFieldName, kSetNameFieldName, @@ -90,6 +92,10 @@ namespace { if (!status.isOK()) return status; + status = bsonExtractBooleanField(argsObj, kDryRunFieldName, &_dryRun); + if (!status.isOK()) + return status; + // extracting the lastCommittedOp is a bit of a process BSONObj lastCommittedOp = argsObj[kLastCommittedOpFieldName].Obj(); Timestamp ts; @@ -126,9 +132,14 @@ namespace { return _lastCommittedOp; } + bool ReplSetRequestVotesArgs::isADryRun() const { + return _dryRun; + } + void ReplSetRequestVotesArgs::addToBSON(BSONObjBuilder* builder) const { builder->append(kCommandName, 1); builder->append(kSetNameFieldName, _setName); + builder->append(kDryRunFieldName, _dryRun); builder->append(kTermFieldName, _term); builder->appendIntOrLL(kCandidateIdFieldName, _candidateId); builder->appendIntOrLL(kConfigVersionFieldName, _cfgver); diff --git a/src/mongo/db/repl/repl_set_request_votes_args.h b/src/mongo/db/repl/repl_set_request_votes_args.h index f7152fe838e..a0f2dc2157b 100644 --- a/src/mongo/db/repl/repl_set_request_votes_args.h +++ b/src/mongo/db/repl/repl_set_request_votes_args.h @@ -47,6 +47,7 @@ namespace repl { long long getCandidateId() const; long long getConfigVersion() const; OpTime getLastCommittedOp() const; + bool isADryRun() const; void addToBSON(BSONObjBuilder* builder) const; @@ -56,6 +57,7 @@ namespace repl { long long _candidateId = -1; // replSet id of the member who sent the replSetRequestVotesCmd long long _cfgver = -1; // replSet config version known to the command issuer OpTime _lastCommittedOp; // The last known committed op of the command issuer + bool _dryRun = false; // Indicates this is a pre-election check when true }; class ReplSetRequestVotesResponse { diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp index 23e15702299..a225b7f454d 100644 --- a/src/mongo/db/repl/replication_coordinator_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl.cpp @@ -2563,7 +2563,9 @@ namespace { return {ErrorCodes::BadValue, "not using election protocol v1"}; } - updateTerm(args.getTerm()); + if (!args.isADryRun()) { + updateTerm(args.getTerm()); + } Status result{ErrorCodes::InternalError, "didn't set status in processReplSetRequestVotes"}; CBHStatus cbh = _replExecutor.scheduleWork( diff --git a/src/mongo/db/repl/replication_coordinator_impl_elect_v1.cpp b/src/mongo/db/repl/replication_coordinator_impl_elect_v1.cpp index 25559eb5987..d774b1ee820 100644 --- a/src/mongo/db/repl/replication_coordinator_impl_elect_v1.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl_elect_v1.cpp @@ -147,6 +147,7 @@ namespace { _rsConfig, _rsConfig.getMemberAt(_selfIndex).getId(), _topCoord->getTerm(), + false, // TODO(dannenberg): make this the second election stage by adding a dryrun getMyLastOptime(), stdx::bind(&ReplicationCoordinatorImpl::_onVoteRequestComplete, this)); if (nextPhaseEvh.getStatus() == ErrorCodes::ShutdownInProgress) { diff --git a/src/mongo/db/repl/topology_coordinator_impl.cpp b/src/mongo/db/repl/topology_coordinator_impl.cpp index f600e266001..08d7ac7198c 100644 --- a/src/mongo/db/repl/topology_coordinator_impl.cpp +++ b/src/mongo/db/repl/topology_coordinator_impl.cpp @@ -2272,13 +2272,15 @@ namespace { response->setVoteGranted(false); response->setReason("candidate's data is staler than mine"); } - else if (_lastVote.getTerm() == args.getTerm()) { + else if (!args.isADryRun() && _lastVote.getTerm() == args.getTerm()) { response->setVoteGranted(false); response->setReason("already voted for another candidate this term"); } else { - _lastVote.setTerm(args.getTerm()); - _lastVote.setCandidateId(args.getCandidateId()); + if (!args.isADryRun()) { + _lastVote.setTerm(args.getTerm()); + _lastVote.setCandidateId(args.getCandidateId()); + } response->setVoteGranted(true); } diff --git a/src/mongo/db/repl/topology_coordinator_impl_test.cpp b/src/mongo/db/repl/topology_coordinator_impl_test.cpp index 49179bd4f49..7378feb4067 100644 --- a/src/mongo/db/repl/topology_coordinator_impl_test.cpp +++ b/src/mongo/db/repl/topology_coordinator_impl_test.cpp @@ -4600,6 +4600,83 @@ namespace { } + TEST_F(TopoCoordTest, ProcessRequestVotesDryRunsDoNotDisallowFutureRequestVotes) { + updateConfig(BSON("_id" << "rs0" << + "version" << 1 << + "members" << BSON_ARRAY( + BSON("_id" << 10 << "host" << "hself") << + BSON("_id" << 20 << "host" << "h2") << + BSON("_id" << 30 << "host" << "h3"))), + 0); + setSelfMemberState(MemberState::RS_SECONDARY); + + // dry run + ReplSetRequestVotesArgs args; + args.initialize(BSON("replSetRequestVotes" << 1 + << "setName" << "rs0" + << "dryRun" << true + << "term" << 1LL + << "candidateId" << 10LL + << "configVersion" << 1LL + << "lastCommittedOp" << BSON ("ts" << Timestamp(10, 0) + << "term" << 0LL))); + ReplSetRequestVotesResponse response; + OpTime lastAppliedOpTime; + + getTopoCoord().processReplSetRequestVotes(args, &response, lastAppliedOpTime); + ASSERT_EQUALS("", response.getReason()); + ASSERT_TRUE(response.getVoteGranted()); + + // second dry run fine + ReplSetRequestVotesArgs args2; + args2.initialize(BSON("replSetRequestVotes" << 1 + << "setName" << "rs0" + << "dryRun" << true + << "term" << 1LL + << "candidateId" << 10LL + << "configVersion" << 1LL + << "lastCommittedOp" << BSON ("ts" << Timestamp(10, 0) + << "term" << 0LL))); + ReplSetRequestVotesResponse response2; + + getTopoCoord().processReplSetRequestVotes(args2, &response2, lastAppliedOpTime); + ASSERT_EQUALS("", response2.getReason()); + ASSERT_TRUE(response2.getVoteGranted()); + + // real request fine + ReplSetRequestVotesArgs args3; + args3.initialize(BSON("replSetRequestVotes" << 1 + << "setName" << "rs0" + << "dryRun" << false + << "term" << 1LL + << "candidateId" << 10LL + << "configVersion" << 1LL + << "lastCommittedOp" << BSON ("ts" << Timestamp(10, 0) + << "term" << 0LL))); + ReplSetRequestVotesResponse response3; + + getTopoCoord().processReplSetRequestVotes(args3, &response3, lastAppliedOpTime); + ASSERT_EQUALS("", response3.getReason()); + ASSERT_TRUE(response3.getVoteGranted()); + + // dry post real, fails + ReplSetRequestVotesArgs args4; + args4.initialize(BSON("replSetRequestVotes" << 1 + << "setName" << "rs0" + << "dryRun" << false + << "term" << 1LL + << "candidateId" << 10LL + << "configVersion" << 1LL + << "lastCommittedOp" << BSON ("ts" << Timestamp(10, 0) + << "term" << 0LL))); + ReplSetRequestVotesResponse response4; + + getTopoCoord().processReplSetRequestVotes(args4, &response4, lastAppliedOpTime); + ASSERT_EQUALS("already voted for another candidate this term", response4.getReason()); + ASSERT_FALSE(response4.getVoteGranted()); + + } + TEST_F(TopoCoordTest, ProcessRequestVotesBadCommands) { updateConfig(BSON("_id" << "rs0" << "version" << 1 << @@ -4685,6 +4762,123 @@ namespace { ASSERT_FALSE(response4.getVoteGranted()); } + TEST_F(TopoCoordTest, ProcessRequestVotesBadCommandsDryRun) { + updateConfig(BSON("_id" << "rs0" << + "version" << 1 << + "members" << BSON_ARRAY( + BSON("_id" << 10 << "host" << "hself") << + BSON("_id" << 20 << "host" << "h2") << + BSON("_id" << 30 << "host" << "h3"))), + 0); + setSelfMemberState(MemberState::RS_SECONDARY); + // set term to 1 + ASSERT(getTopoCoord().updateTerm(1)); + // and make sure we voted in term 1 + ReplSetRequestVotesArgs argsForRealVote; + argsForRealVote.initialize(BSON("replSetRequestVotes" << 1 + << "setName" << "rs0" + << "term" << 1LL + << "candidateId" << 10LL + << "configVersion" << 1LL + << "lastCommittedOp" << BSON ("ts" << Timestamp(10, 0) + << "term" << 0LL))); + ReplSetRequestVotesResponse responseForRealVote; + OpTime lastAppliedOpTime; + + getTopoCoord().processReplSetRequestVotes(argsForRealVote, + &responseForRealVote, + lastAppliedOpTime); + ASSERT_EQUALS("", responseForRealVote.getReason()); + ASSERT_TRUE(responseForRealVote.getVoteGranted()); + + + // mismatched setName + ReplSetRequestVotesArgs args; + args.initialize(BSON("replSetRequestVotes" << 1 + << "setName" << "wrongName" + << "dryRun" << true + << "term" << 2LL + << "candidateId" << 10LL + << "configVersion" << 1LL + << "lastCommittedOp" << BSON ("ts" << Timestamp(10, 0) + << "term" << 0LL))); + ReplSetRequestVotesResponse response; + + getTopoCoord().processReplSetRequestVotes(args, &response, lastAppliedOpTime); + ASSERT_EQUALS("candidate's set name differs from mine", response.getReason()); + ASSERT_EQUALS(1, response.getTerm()); + ASSERT_FALSE(response.getVoteGranted()); + + // mismatched configVersion + ReplSetRequestVotesArgs args2; + args2.initialize(BSON("replSetRequestVotes" << 1 + << "setName" << "rs0" + << "dryRun" << true + << "term" << 2LL + << "candidateId" << 20LL + << "configVersion" << 0LL + << "lastCommittedOp" << BSON ("ts" << Timestamp(10, 0) + << "term" << 0LL))); + ReplSetRequestVotesResponse response2; + + getTopoCoord().processReplSetRequestVotes(args2, &response2, lastAppliedOpTime); + ASSERT_EQUALS("candidate's config version differs from mine", response2.getReason()); + ASSERT_EQUALS(1, response2.getTerm()); + ASSERT_FALSE(response2.getVoteGranted()); + + // stale term + ReplSetRequestVotesArgs args3; + args3.initialize(BSON("replSetRequestVotes" << 1 + << "setName" << "rs0" + << "dryRun" << true + << "term" << 0LL + << "candidateId" << 20LL + << "configVersion" << 1LL + << "lastCommittedOp" << BSON ("ts" << Timestamp(10, 0) + << "term" << 0LL))); + ReplSetRequestVotesResponse response3; + + getTopoCoord().processReplSetRequestVotes(args3, &response3, lastAppliedOpTime); + ASSERT_EQUALS("candidate's term is lower than mine", response3.getReason()); + ASSERT_EQUALS(1, response3.getTerm()); + ASSERT_FALSE(response3.getVoteGranted()); + + // repeat term + ReplSetRequestVotesArgs args4; + args4.initialize(BSON("replSetRequestVotes" << 1 + << "setName" << "rs0" + << "dryRun" << true + << "term" << 1LL + << "candidateId" << 20LL + << "configVersion" << 1LL + << "lastCommittedOp" << BSON ("ts" << Timestamp(10, 0) + << "term" << 0LL))); + ReplSetRequestVotesResponse response4; + + getTopoCoord().processReplSetRequestVotes(args4, &response4, lastAppliedOpTime); + ASSERT_EQUALS("", response4.getReason()); + ASSERT_EQUALS(1, response4.getTerm()); + ASSERT_TRUE(response4.getVoteGranted()); + + // stale OpTime + ReplSetRequestVotesArgs args5; + args5.initialize(BSON("replSetRequestVotes" << 1 + << "setName" << "rs0" + << "dryRun" << true + << "term" << 3LL + << "candidateId" << 20LL + << "configVersion" << 1LL + << "lastCommittedOp" << BSON ("ts" << Timestamp(10, 0) + << "term" << 0LL))); + ReplSetRequestVotesResponse response5; + OpTime lastAppliedOpTime2 = {Timestamp(20, 0), 0}; + + getTopoCoord().processReplSetRequestVotes(args5, &response5, lastAppliedOpTime2); + ASSERT_EQUALS("candidate's data is staler than mine", response5.getReason()); + ASSERT_EQUALS(1, response5.getTerm()); + ASSERT_FALSE(response5.getVoteGranted()); + } + TEST_F(TopoCoordTest, ProcessDeclareElectionWinner) { updateConfig(BSON("_id" << "rs0" << "version" << 1 << diff --git a/src/mongo/db/repl/vote_requester.cpp b/src/mongo/db/repl/vote_requester.cpp index 5c51df02eb1..81691edf5d7 100644 --- a/src/mongo/db/repl/vote_requester.cpp +++ b/src/mongo/db/repl/vote_requester.cpp @@ -45,10 +45,12 @@ namespace repl { VoteRequester::Algorithm::Algorithm(const ReplicaSetConfig& rsConfig, long long candidateId, long long term, + bool dryRun, OpTime lastOplogEntry) : _rsConfig(rsConfig), _candidateId(candidateId), _term(term), + _dryRun(dryRun), _lastOplogEntry(lastOplogEntry) { // populate targets with all voting members that aren't this node @@ -66,6 +68,7 @@ namespace repl { BSONObjBuilder requestVotesCmdBuilder; requestVotesCmdBuilder.append("replSetRequestVotes", 1); requestVotesCmdBuilder.append("setName", _rsConfig.getReplSetName()); + requestVotesCmdBuilder.append("dryRun", _dryRun); requestVotesCmdBuilder.append("term", _term); requestVotesCmdBuilder.append("candidateId", _candidateId); requestVotesCmdBuilder.append("configVersion", _rsConfig.getConfigVersion()); @@ -112,7 +115,6 @@ namespace repl { _staleTerm = true; } } - } bool VoteRequester::Algorithm::hasReceivedSufficientResponses() const { @@ -141,12 +143,14 @@ namespace repl { const ReplicaSetConfig& rsConfig, long long candidateId, long long term, + bool dryRun, OpTime lastOplogEntry, const stdx::function<void ()>& onCompletion) { _algorithm.reset(new Algorithm(rsConfig, candidateId, term, + dryRun, lastOplogEntry)); _runner.reset(new ScatterGatherRunner(_algorithm.get())); return _runner->start(executor, onCompletion); diff --git a/src/mongo/db/repl/vote_requester.h b/src/mongo/db/repl/vote_requester.h index a1659349bea..a5102192ebd 100644 --- a/src/mongo/db/repl/vote_requester.h +++ b/src/mongo/db/repl/vote_requester.h @@ -63,6 +63,7 @@ namespace repl { Algorithm(const ReplicaSetConfig& rsConfig, long long candidateId, long long term, + bool dryRun, OpTime lastOplogEntry); virtual ~Algorithm(); virtual std::vector<RemoteCommandRequest> getRequests() const; @@ -82,6 +83,7 @@ namespace repl { const ReplicaSetConfig _rsConfig; const long long _candidateId; const long long _term; + bool _dryRun = false; // this bool indicates this is a mock election when true const OpTime _lastOplogEntry; std::vector<HostAndPort> _targets; bool _staleTerm = false; @@ -106,6 +108,7 @@ namespace repl { const ReplicaSetConfig& rsConfig, long long candidateId, long long term, + bool dryRun, OpTime lastOplogEntry, const stdx::function<void ()>& onCompletion = stdx::function<void ()>()); diff --git a/src/mongo/db/repl/vote_requester_test.cpp b/src/mongo/db/repl/vote_requester_test.cpp index 90d0843d843..d5b9d9ce3d5 100644 --- a/src/mongo/db/repl/vote_requester_test.cpp +++ b/src/mongo/db/repl/vote_requester_test.cpp @@ -75,6 +75,7 @@ namespace { _requester.reset(new VoteRequester::Algorithm(config, candidateId, term, + false, // not a dryRun lastOplogEntry)); } @@ -173,10 +174,36 @@ namespace { Milliseconds(10))); } - private: std::unique_ptr<VoteRequester::Algorithm> _requester; }; + class VoteRequesterDryRunTest : public VoteRequesterTest { + public: + virtual void setUp() { + ReplicaSetConfig config; + ASSERT_OK(config.initialize( + BSON("_id" << "rs0" << + "version" << 2 << + "members" << BSON_ARRAY( + BSON("_id" << 0 << "host" << "host0") << + BSON("_id" << 1 << "host" << "host1") << + BSON("_id" << 2 << "host" << "host2") << + BSON("_id" << 3 << "host" << "host3" << "votes" << 0) << + BSON("_id" << 4 << "host" << "host4" << "votes" << 0))))); + ASSERT_OK(config.validate()); + long long candidateId = 0; + long long term = 2; + OpTime lastOplogEntry = OpTime(Timestamp(999,0), 1); + + _requester.reset(new VoteRequester::Algorithm(config, + candidateId, + term, + true, // dryRun + lastOplogEntry)); + } + + }; + TEST_F(VoteRequesterTest, ImmediateGoodResponseWinElection) { ASSERT_FALSE(hasReceivedSufficientResponses()); processResponse(requestFrom("host1"), votedYes()); @@ -267,6 +294,96 @@ namespace { stopCapturingLogMessages(); } + TEST_F(VoteRequesterDryRunTest, ImmediateGoodResponseWinElection) { + ASSERT_FALSE(hasReceivedSufficientResponses()); + processResponse(requestFrom("host1"), votedYes()); + ASSERT_TRUE(hasReceivedSufficientResponses()); + ASSERT_EQUALS(VoteRequester::SuccessfullyElected, getResult()); + } + + TEST_F(VoteRequesterDryRunTest, BadConfigVersionWinElection) { + startCapturingLogMessages(); + ASSERT_FALSE(hasReceivedSufficientResponses()); + processResponse(requestFrom("host1"), votedNoBecauseConfigVersionDoesNotMatch()); + ASSERT_FALSE(hasReceivedSufficientResponses()); + ASSERT_EQUALS(1, countLogLinesContaining("Got no vote from host1")); + processResponse(requestFrom("host2"), votedYes()); + ASSERT_TRUE(hasReceivedSufficientResponses()); + ASSERT_EQUALS(VoteRequester::SuccessfullyElected, getResult()); + stopCapturingLogMessages(); + } + + TEST_F(VoteRequesterDryRunTest, SetNameDiffersWinElection) { + startCapturingLogMessages(); + ASSERT_FALSE(hasReceivedSufficientResponses()); + processResponse(requestFrom("host1"), votedNoBecauseSetNameDiffers()); + ASSERT_FALSE(hasReceivedSufficientResponses()); + ASSERT_EQUALS(1, countLogLinesContaining("Got no vote from host1")); + processResponse(requestFrom("host2"), votedYes()); + ASSERT_TRUE(hasReceivedSufficientResponses()); + ASSERT_EQUALS(VoteRequester::SuccessfullyElected, getResult()); + stopCapturingLogMessages(); + } + + TEST_F(VoteRequesterDryRunTest, LastOpTimeIsGreaterWinElection) { + startCapturingLogMessages(); + ASSERT_FALSE(hasReceivedSufficientResponses()); + processResponse(requestFrom("host1"), votedNoBecauseLastOpTimeIsGreater()); + ASSERT_FALSE(hasReceivedSufficientResponses()); + ASSERT_EQUALS(1, countLogLinesContaining("Got no vote from host1")); + processResponse(requestFrom("host2"), votedYes()); + ASSERT_TRUE(hasReceivedSufficientResponses()); + ASSERT_EQUALS(VoteRequester::SuccessfullyElected, getResult()); + stopCapturingLogMessages(); + } + + TEST_F(VoteRequesterDryRunTest, FailedToContactWinElection) { + startCapturingLogMessages(); + ASSERT_FALSE(hasReceivedSufficientResponses()); + processResponse(requestFrom("host1"), badResponseStatus()); + ASSERT_FALSE(hasReceivedSufficientResponses()); + ASSERT_EQUALS(1, countLogLinesContaining("Got failed response from host1")); + processResponse(requestFrom("host2"), votedYes()); + ASSERT_TRUE(hasReceivedSufficientResponses()); + ASSERT_EQUALS(VoteRequester::SuccessfullyElected, getResult()); + stopCapturingLogMessages(); + } + + TEST_F(VoteRequesterDryRunTest, AlreadyVotedWinElection) { + startCapturingLogMessages(); + ASSERT_FALSE(hasReceivedSufficientResponses()); + processResponse(requestFrom("host1"), votedNoBecauseAlreadyVoted()); + ASSERT_FALSE(hasReceivedSufficientResponses()); + ASSERT_EQUALS(1, countLogLinesContaining("Got no vote from host1")); + processResponse(requestFrom("host2"), votedYes()); + ASSERT_TRUE(hasReceivedSufficientResponses()); + ASSERT_EQUALS(VoteRequester::SuccessfullyElected, getResult()); + stopCapturingLogMessages(); + } + + TEST_F(VoteRequesterDryRunTest, StaleTermLoseElection) { + startCapturingLogMessages(); + ASSERT_FALSE(hasReceivedSufficientResponses()); + processResponse(requestFrom("host1"), votedNoBecauseTermIsGreater()); + ASSERT_EQUALS(1, countLogLinesContaining("Got no vote from host1")); + ASSERT_TRUE(hasReceivedSufficientResponses()); + ASSERT_EQUALS(VoteRequester::StaleTerm, getResult()); + stopCapturingLogMessages(); + } + + TEST_F(VoteRequesterDryRunTest, NotEnoughVotesLoseElection) { + startCapturingLogMessages(); + ASSERT_FALSE(hasReceivedSufficientResponses()); + processResponse(requestFrom("host1"), votedNoBecauseSetNameDiffers()); + ASSERT_FALSE(hasReceivedSufficientResponses()); + ASSERT_EQUALS(1, countLogLinesContaining("Got no vote from host1")); + processResponse(requestFrom("host2"), badResponseStatus()); + ASSERT_EQUALS(1, countLogLinesContaining("Got failed response from host2")); + ASSERT_TRUE(hasReceivedSufficientResponses()); + ASSERT_EQUALS(VoteRequester::InsufficientVotes, getResult()); + stopCapturingLogMessages(); + } + } // namespace } // namespace repl } // namespace mongo |