summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSuganthi Mani <suganthi.mani@mongodb.com>2018-05-21 23:04:31 -0400
committerSuganthi Mani <suganthi.mani@mongodb.com>2018-05-22 00:10:04 -0400
commita5dc50015eb113aecb62f22af67b40241da14e27 (patch)
tree5f501aaba07e9551c02968cfbd38401b6174ab07
parent95ced845fb7bb0aa9344bc95309de562aad1dc48 (diff)
downloadmongo-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.cpp24
-rw-r--r--src/mongo/db/repl/topology_coordinator_impl_v1_test.cpp177
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"