From 9e4b6e5c2f9e266ed05ce7c913631f12548e4160 Mon Sep 17 00:00:00 2001 From: Pavi Vetriselvan Date: Tue, 4 Aug 2020 12:51:12 -0400 Subject: SERVER-50097 isMaster aliases should correctly populate exhaustIsMasterMetrics --- src/mongo/rpc/op_msg_integration_test.cpp | 59 +++++++++++++++++++++++++++++++ src/mongo/s/commands/strategy.cpp | 23 ++++++------ 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/mongo/rpc/op_msg_integration_test.cpp b/src/mongo/rpc/op_msg_integration_test.cpp index 1c6400e2cba..0b2b4de6c31 100644 --- a/src/mongo/rpc/op_msg_integration_test.cpp +++ b/src/mongo/rpc/op_msg_integration_test.cpp @@ -767,6 +767,65 @@ TEST(OpMsg, ServerStatusCorrectlyShowsExhaustIsMasterMetrics) { // The exhaust stream would continue indefinitely. } +TEST(OpMsg, ServerStatusCorrectlyShowsExhaustIsMasterMetricsWithIsMasterAlias) { + std::string errMsg; + auto conn = std::unique_ptr( + unittest::getFixtureConnectionString().connect("integration_test", errMsg)); + uassert(ErrorCodes::SocketException, errMsg, conn); + + if (conn->isReplicaSetMember()) { + // Don't run on replica sets as the RSM will use the streamable isMaster protocol by + // default. This can cause inconsistencies in our metrics tests. + return; + } + + // Wait for stale exhuast streams to finish closing before testing the exhaust isMaster metrics. + ASSERT(waitForCondition([&] { + auto serverStatusCmd = BSON("serverStatus" << 1); + BSONObj serverStatusReply; + ASSERT(conn->runCommand("admin", serverStatusCmd, serverStatusReply)); + return serverStatusReply["connections"]["exhaustIsMaster"].numberInt() == 0; + })); + + // Issue an isMaster command with the "ismaster" alias without a topology version. + auto lowerCaseIsMasterCmd = BSON("ismaster" << 1); + auto opMsgRequest = OpMsgRequest::fromDBAndBody("admin", lowerCaseIsMasterCmd); + auto request = opMsgRequest.serialize(); + + Message reply; + ASSERT(conn->call(request, reply)); + auto res = OpMsg::parse(reply).body; + ASSERT_OK(getStatusFromCommandResult(res)); + auto topologyVersion = res["topologyVersion"].Obj().getOwned(); + ASSERT(!OpMsg::isFlagSet(reply, OpMsg::kMoreToCome)); + + lowerCaseIsMasterCmd = + BSON("ismaster" << 1 << "topologyVersion" << topologyVersion << "maxAwaitTimeMS" << 100); + opMsgRequest = OpMsgRequest::fromDBAndBody("admin", lowerCaseIsMasterCmd); + request = opMsgRequest.serialize(); + OpMsg::setFlag(&request, OpMsg::kExhaustSupported); + + // Run the isMaster command with the "ismaster" alias. The aliased command should work + // identically to the default "isMaster" command name and initiate the the exhaust stream. + ASSERT(conn->call(request, reply)); + ASSERT(OpMsg::isFlagSet(reply, OpMsg::kMoreToCome)); + res = OpMsg::parse(reply).body; + ASSERT_OK(getStatusFromCommandResult(res)); + + // Start a new connection to the server to check the serverStatus metrics. + std::string newErrMsg; + auto conn2 = std::unique_ptr( + unittest::getFixtureConnectionString().connect("integration_test", newErrMsg)); + uassert(ErrorCodes::SocketException, newErrMsg, conn2); + + auto serverStatusCmd = BSON("serverStatus" << 1); + BSONObj serverStatusReply; + ASSERT(conn2->runCommand("admin", serverStatusCmd, serverStatusReply)); + ASSERT_EQUALS(1, serverStatusReply["connections"]["exhaustIsMaster"].numberInt()); + + // The exhaust stream would continue indefinitely. +} + TEST(OpMsg, ExhaustIsMasterMetricDecrementsOnNewOpAfterTerminatingExhaustStream) { std::string errMsg; const auto conn1AppName = "integration_test"; diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp index 81eee3ef4bc..f90d6d1beb6 100644 --- a/src/mongo/s/commands/strategy.cpp +++ b/src/mongo/s/commands/strategy.cpp @@ -296,9 +296,10 @@ MONGO_FAIL_POINT_DEFINE(doNotRefreshShardsOnRetargettingError); */ void runCommand(OperationContext* opCtx, const OpMsgRequest& request, - const NetworkOp opType, + const Message& m, rpc::ReplyBuilderInterface* replyBuilder, BSONObjBuilder* errorBuilder) { + auto const opType = m.operation(); auto const commandName = request.getCommandName(); auto const command = CommandHelpers::findCommand(commandName); if (!command) { @@ -311,6 +312,14 @@ void runCommand(OperationContext* opCtx, return; } + opCtx->setExhaust(OpMsg::isFlagSet(m, OpMsg::kExhaustSupported)); + const auto session = opCtx->getClient()->session(); + if (session) { + if (!opCtx->isExhaust() || command->getName() != "isMaster"_sd) { + InExhaustIsMaster::get(session.get())->setInExhaustIsMaster(false); + } + } + CommandHelpers::uassertShouldAttemptParse(opCtx, command, request); // Parse the 'maxTimeMS' command option, and use it to set a deadline for the operation on @@ -1023,14 +1032,6 @@ DbResponse Strategy::clientCommand(OperationContext* opCtx, const Message& m) { } }(); - opCtx->setExhaust(OpMsg::isFlagSet(m, OpMsg::kExhaustSupported)); - const auto session = opCtx->getClient()->session(); - if (session) { - if (!opCtx->isExhaust() || request.getCommandName() != "isMaster"_sd) { - InExhaustIsMaster::get(session.get())->setInExhaustIsMaster(false); - } - } - // Execute. std::string db = request.getDatabase().toString(); try { @@ -1040,7 +1041,7 @@ DbResponse Strategy::clientCommand(OperationContext* opCtx, const Message& m) { "Command begin", "db"_attr = db, "headerId"_attr = m.header().getId()); - runCommand(opCtx, request, m.operation(), reply.get(), &errorBuilder); + runCommand(opCtx, request, m, reply.get(), &errorBuilder); LOGV2_DEBUG(22771, 3, "Command end db: {db} msg id: {headerId}", @@ -1232,7 +1233,7 @@ void Strategy::writeOp(OperationContext* opCtx, DbMessage* dbm) { MONGO_UNREACHABLE; } }(), - msg.operation(), + msg, &reply, &errorBuilder); // built objects are ignored } -- cgit v1.2.1