diff options
author | Suganthi Mani <suganthi.mani@mongodb.com> | 2018-05-21 23:04:31 -0400 |
---|---|---|
committer | Suganthi Mani <suganthi.mani@mongodb.com> | 2018-05-22 00:10:04 -0400 |
commit | a5dc50015eb113aecb62f22af67b40241da14e27 (patch) | |
tree | 5f501aaba07e9551c02968cfbd38401b6174ab07 | |
parent | 95ced845fb7bb0aa9344bc95309de562aad1dc48 (diff) | |
download | mongo-a5dc50015eb113aecb62f22af67b40241da14e27.tar.gz |
SERVER-27230 Always emit string status fields in replica set status
(cherry picked from commit d01dde5b3cca80a5db8c07c6d3f029c60f3b332e)
-rw-r--r-- | src/mongo/db/repl/topology_coordinator_impl.cpp | 24 | ||||
-rw-r--r-- | src/mongo/db/repl/topology_coordinator_impl_v1_test.cpp | 177 |
2 files changed, 192 insertions, 9 deletions
diff --git a/src/mongo/db/repl/topology_coordinator_impl.cpp b/src/mongo/db/repl/topology_coordinator_impl.cpp index f5302008206..a416dbcad49 100644 --- a/src/mongo/db/repl/topology_coordinator_impl.cpp +++ b/src/mongo/db/repl/topology_coordinator_impl.cpp @@ -1622,9 +1622,10 @@ void TopologyCoordinatorImpl::prepareStatusResponse(const ReplSetStatusArgs& rsS if (_maintenanceModeCalls) { response->append("maintenanceMode", _maintenanceModeCalls); } - std::string s = _getHbmsg(now); - if (!s.empty()) - response->append("infoMessage", s); + response->append("lastHeartbeatMessage", ""); + response->append("syncingTo", ""); + + response->append("infoMessage", _getHbmsg(now)); *result = Status(ErrorCodes::InvalidReplicaSetConfig, "Our replica set config is invalid or we are not a member of it"); return; @@ -1650,15 +1651,15 @@ void TopologyCoordinatorImpl::prepareStatusResponse(const ReplSetStatusArgs& rsS if (!_syncSource.empty() && !_iAmPrimary()) { bb.append("syncingTo", _syncSource.toString()); + } else { + bb.append("syncingTo", ""); } if (_maintenanceModeCalls) { bb.append("maintenanceMode", _maintenanceModeCalls); } - std::string s = _getHbmsg(now); - if (!s.empty()) - bb.append("infoMessage", s); + bb.append("infoMessage", _getHbmsg(now)); if (myState.primary()) { bb.append("electionTime", _electionTime); @@ -1667,6 +1668,7 @@ void TopologyCoordinatorImpl::prepareStatusResponse(const ReplSetStatusArgs& rsS } bb.appendIntOrLL("configVersion", _rsConfig.getConfigVersion()); bb.append("self", true); + bb.append("lastHeartbeatMessage", ""); membersOut.push_back(bb.obj()); } else { // add non-self member @@ -1706,9 +1708,7 @@ void TopologyCoordinatorImpl::prepareStatusResponse(const ReplSetStatusArgs& rsS Milliseconds ping = _getPing(itConfig.getHostAndPort()); if (ping != UninitializedPing) { bb.append("pingMs", durationCount<Milliseconds>(ping)); - std::string s = it->getLastHeartbeatMsg(); - if (!s.empty()) - bb.append("lastHeartbeatMessage", s); + bb.append("lastHeartbeatMessage", it->getLastHeartbeatMsg()); } if (it->hasAuthIssue()) { bb.append("authenticated", false); @@ -1716,8 +1716,12 @@ void TopologyCoordinatorImpl::prepareStatusResponse(const ReplSetStatusArgs& rsS const HostAndPort& syncSource = it->getSyncSource(); if (!syncSource.empty() && !state.primary()) { bb.append("syncingTo", syncSource.toString()); + } else { + bb.append("syncingTo", ""); } + bb.append("infoMessage", ""); + if (state == MemberState::RS_PRIMARY) { bb.append("electionTime", it->getElectionTime()); bb.appendDate( @@ -1740,6 +1744,8 @@ void TopologyCoordinatorImpl::prepareStatusResponse(const ReplSetStatusArgs& rsS // Add sync source info if (!_syncSource.empty() && !myState.primary() && !myState.removed()) { response->append("syncingTo", _syncSource.toString()); + } else { + response->append("syncingTo", ""); } if (_rsConfig.isConfigServer()) { diff --git a/src/mongo/db/repl/topology_coordinator_impl_v1_test.cpp b/src/mongo/db/repl/topology_coordinator_impl_v1_test.cpp index 8f60269cd71..264aba8a357 100644 --- a/src/mongo/db/repl/topology_coordinator_impl_v1_test.cpp +++ b/src/mongo/db/repl/topology_coordinator_impl_v1_test.cpp @@ -3988,6 +3988,183 @@ TEST_F(HeartbeatResponseTestV1, UpdateHeartbeatDataTermPreventsPriorityTakeover) ASSERT_EQUALS(2, getCurrentPrimaryIndex()); } +TEST_F(TopoCoordTest, + StatusResponseAlwaysIncludesStringStatusFieldsForReplicaSetMembersNoHeartbeats) { + + Date_t heartbeatTime = Date_t::fromMillisSinceEpoch(5000); + Seconds uptimeSecs(10); + Date_t curTime = heartbeatTime + uptimeSecs; + OpTime oplogProgress(Timestamp(3, 4), 0); + + ASSERT_TRUE(TopologyCoordinator::Role::follower == getTopoCoord().getRole()); + ASSERT_EQUALS(MemberState::RS_STARTUP, getTopoCoord().getMemberState().s); + updateConfig(BSON("_id" + << "rs0" + << "version" + << 5 + << "members" + << BSON_ARRAY(BSON("_id" << 0 << "host" + << "host0:27017") + << BSON("_id" << 1 << "host" + << "host1:27017")) + << "protocolVersion" + << 1), + 0); + { + BSONObjBuilder statusBuilder; + Status resultStatus(ErrorCodes::InternalError, "prepareStatusResponse didn't set result"); + getTopoCoord().prepareStatusResponse( + TopologyCoordinator::ReplSetStatusArgs{ + curTime, + static_cast<unsigned>(durationCount<Seconds>(uptimeSecs)), + oplogProgress, + oplogProgress, + oplogProgress, + OpTime(), + BSONObj()}, + &statusBuilder, + &resultStatus); + + ASSERT_OK(resultStatus); + BSONObj rsStatus = statusBuilder.obj(); + BSONObj member0Status = rsStatus["members"].Array()[0].Obj(); + BSONObj member1Status = rsStatus["members"].Array()[1].Obj(); + + // These fields should all be empty, since this node has not received heartbeats and has + // no sync source yet. + ASSERT_EQUALS("", rsStatus["syncingTo"].String()); + ASSERT_EQUALS("", member0Status["syncingTo"].String()); + ASSERT_EQUALS("", member0Status["lastHeartbeatMessage"].String()); + ASSERT_EQUALS("", member0Status["infoMessage"].String()); + ASSERT_EQUALS("", member1Status["syncingTo"].String()); + ASSERT_EQUALS("", member1Status["lastHeartbeatMessage"].String()); + ASSERT_EQUALS("", member1Status["infoMessage"].String()); + } +} + +TEST_F(TopoCoordTest, + StatusResponseAlwaysIncludesStringStatusFieldsForReplicaSetMembersWithHeartbeats) { + + Date_t heartbeatTime = Date_t::fromMillisSinceEpoch(5000); + Seconds uptimeSecs(10); + Date_t curTime = heartbeatTime + uptimeSecs; + OpTime oplogProgress(Timestamp(3, 4), 0); + + ASSERT_TRUE(TopologyCoordinator::Role::follower == getTopoCoord().getRole()); + ASSERT_EQUALS(MemberState::RS_STARTUP, getTopoCoord().getMemberState().s); + updateConfig(BSON("_id" + << "rs0" + << "version" + << 5 + << "members" + << BSON_ARRAY(BSON("_id" << 0 << "host" + << "host0:27017") + << BSON("_id" << 1 << "host" + << "host1:27017")) + << "protocolVersion" + << 1), + 0); + + ASSERT(getTopoCoord().getSyncSourceAddress().empty()); + + // Receive heartbeats and choose a sync source. + setSelfMemberState(MemberState::RS_SECONDARY); + + OpTime election = OpTime(); + + // Record two rounds of pings so the node can pick a sync source. + receiveUpHeartbeat(HostAndPort("host1"), + "rs0", + MemberState::RS_PRIMARY, + election, + oplogProgress, + oplogProgress); + receiveUpHeartbeat(HostAndPort("host1"), + "rs0", + MemberState::RS_PRIMARY, + election, + oplogProgress, + oplogProgress); + + getTopoCoord().chooseNewSyncSource( + now(), OpTime(), TopologyCoordinator::ChainingPreference::kUseConfiguration); + ASSERT_EQUALS(HostAndPort("host1"), getTopoCoord().getSyncSourceAddress()); + + { + BSONObjBuilder statusBuilder; + Status resultStatus(ErrorCodes::InternalError, "prepareStatusResponse didn't set result"); + getTopoCoord().prepareStatusResponse( + TopologyCoordinator::ReplSetStatusArgs{ + curTime, + static_cast<unsigned>(durationCount<Seconds>(uptimeSecs)), + oplogProgress, + oplogProgress, + oplogProgress, + OpTime(), + BSONObj()}, + &statusBuilder, + &resultStatus); + + ASSERT_OK(resultStatus); + BSONObj rsStatus = statusBuilder.obj(); + BSONObj member0Status = rsStatus["members"].Array()[0].Obj(); + BSONObj member1Status = rsStatus["members"].Array()[1].Obj(); + + // Node 0 (self) has received heartbeats and has a sync source. + ASSERT_EQUALS("host1:27017", rsStatus["syncingTo"].String()); + ASSERT_EQUALS("host1:27017", member0Status["syncingTo"].String()); + ASSERT_EQUALS("syncing from: host1:27017", member0Status["infoMessage"].String()); + ASSERT_EQUALS("", member0Status["lastHeartbeatMessage"].String()); + ASSERT_EQUALS("", member1Status["syncingTo"].String()); + ASSERT_EQUALS("", member1Status["infoMessage"].String()); + ASSERT_EQUALS("", member1Status["lastHeartbeatMessage"].String()); + } +} + +TEST_F(TopoCoordTest, StatusResponseAlwaysIncludesStringStatusFieldsForNonMembers) { + Date_t heartbeatTime = Date_t::fromMillisSinceEpoch(5000); + Seconds uptimeSecs(10); + Date_t curTime = heartbeatTime + uptimeSecs; + OpTime oplogProgress(Timestamp(3, 4), 0); + + ASSERT_TRUE(TopologyCoordinator::Role::follower == getTopoCoord().getRole()); + ASSERT_EQUALS(MemberState::RS_STARTUP, getTopoCoord().getMemberState().s); + updateConfig(BSON("_id" + << "rs0" + << "version" + << 5 + << "members" + << BSON_ARRAY(BSON("_id" << 0 << "host" + << "host0:27017")) + << "protocolVersion" + << 1), + -1); // This node is no longer part of this replica set. + + BSONObjBuilder statusBuilder; + Status resultStatus(ErrorCodes::InternalError, "prepareStatusResponse didn't set result"); + getTopoCoord().prepareStatusResponse( + TopologyCoordinator::ReplSetStatusArgs{ + curTime, + static_cast<unsigned>(durationCount<Seconds>(uptimeSecs)), + oplogProgress, + oplogProgress, + oplogProgress, + OpTime(), + BSONObj()}, + &statusBuilder, + &resultStatus); + + ASSERT_NOT_OK(resultStatus); + ASSERT_EQUALS(ErrorCodes::InvalidReplicaSetConfig, resultStatus); + + BSONObj rsStatus = statusBuilder.obj(); + + // These fields should all be empty, since this node is not a member of a replica set. + ASSERT_EQUALS("", rsStatus["lastHeartbeatMessage"].String()); + ASSERT_EQUALS("", rsStatus["syncingTo"].String()); + ASSERT_EQUALS("", rsStatus["infoMessage"].String()); +} + TEST_F(HeartbeatResponseTestV1, ScheduleElectionIfAMajorityOfVotersIsVisibleEvenThoughATrueMajorityIsNot) { updateConfig(BSON("_id" |