From 4e6da587564d8ac2861d7edfc980edc99bb31861 Mon Sep 17 00:00:00 2001 From: Spencer T Brody Date: Mon, 7 Sep 2020 16:10:06 -0400 Subject: SERVER-49242 Add serverStatus counters for number of running PrimaryOnlyService instances --- src/mongo/db/repl/SConscript | 1 + src/mongo/db/repl/primary_only_service.cpp | 12 +++++++++ src/mongo/db/repl/primary_only_service.h | 11 ++++++++ src/mongo/db/repl/primary_only_service_test.cpp | 34 +++++++++++++++++++++++++ src/mongo/db/repl/replication_info.cpp | 24 +++++++++++------ 5 files changed, 74 insertions(+), 8 deletions(-) diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript index eeddbed7948..0ef88b0a410 100644 --- a/src/mongo/db/repl/SConscript +++ b/src/mongo/db/repl/SConscript @@ -1232,6 +1232,7 @@ env.Library( '$BUILD_DIR/mongo/db/repl/speculative_authenticate', '$BUILD_DIR/mongo/db/stats/counters', '$BUILD_DIR/mongo/transport/message_compressor', + 'primary_only_service', 'replication_auth', 'split_horizon', ], diff --git a/src/mongo/db/repl/primary_only_service.cpp b/src/mongo/db/repl/primary_only_service.cpp index acf79190d1b..ab7f25ec861 100644 --- a/src/mongo/db/repl/primary_only_service.cpp +++ b/src/mongo/db/repl/primary_only_service.cpp @@ -227,9 +227,21 @@ void PrimaryOnlyServiceRegistry::onStepDown() { } } +void PrimaryOnlyServiceRegistry::reportServiceInfo(BSONObjBuilder* result) { + BSONObjBuilder subBuilder(result->subobjStart("primaryOnlyServices")); + for (auto& service : _servicesByName) { + subBuilder.appendNumber(service.first, service.second->getNumberOfInstances()); + } +} + PrimaryOnlyService::PrimaryOnlyService(ServiceContext* serviceContext) : _serviceContext(serviceContext) {} +size_t PrimaryOnlyService::getNumberOfInstances() { + stdx::lock_guard lk(_mutex); + return _instances.size(); +} + bool PrimaryOnlyService::isRunning() const { stdx::lock_guard lk(_mutex); return _state == State::kRunning; diff --git a/src/mongo/db/repl/primary_only_service.h b/src/mongo/db/repl/primary_only_service.h index 0d57cf96d9b..664ecd9fa11 100644 --- a/src/mongo/db/repl/primary_only_service.h +++ b/src/mongo/db/repl/primary_only_service.h @@ -217,6 +217,11 @@ public: */ bool isRunning() const; + /** + * Returns the number of currently running Instances of this service. + */ + size_t getNumberOfInstances(); + protected: /** * Constructs a new Instance object with the given initial state. @@ -328,6 +333,12 @@ public: */ PrimaryOnlyService* lookupServiceByNamespace(const NamespaceString& ns); + /** + * Adds a 'primaryOnlyServices' sub-obj to the 'result' BSONObjBuilder containing a count of the + * number of active instances for each registered service. + */ + void reportServiceInfo(BSONObjBuilder* result); + void onStartup(OperationContext*) final; void onShutdown() final; void onStepUpBegin(OperationContext*, long long term) final {} diff --git a/src/mongo/db/repl/primary_only_service_test.cpp b/src/mongo/db/repl/primary_only_service_test.cpp index d89005e8b1d..d7c76b6b7cb 100644 --- a/src/mongo/db/repl/primary_only_service_test.cpp +++ b/src/mongo/db/repl/primary_only_service_test.cpp @@ -367,6 +367,40 @@ TEST_F(PrimaryOnlyServiceTest, DoubleCreateInstance) { TestServiceHangDuringInitialization.setMode(FailPoint::off); } +TEST_F(PrimaryOnlyServiceTest, ReportServiceInfo) { + { + BSONObjBuilder resultBuilder; + _registry->reportServiceInfo(&resultBuilder); + + ASSERT_BSONOBJ_EQ(BSON("primaryOnlyServices" << BSON("TestService" << 0)), + resultBuilder.obj()); + } + + // Make sure the instance doesn't complete. + TestServiceHangDuringInitialization.setMode(FailPoint::alwaysOn); + auto instance = TestService::Instance::getOrCreate(_service, BSON("_id" << 0 << "state" << 0)); + + { + BSONObjBuilder resultBuilder; + _registry->reportServiceInfo(&resultBuilder); + + ASSERT_BSONOBJ_EQ(BSON("primaryOnlyServices" << BSON("TestService" << 1)), + resultBuilder.obj()); + } + + auto instance2 = TestService::Instance::getOrCreate(_service, BSON("_id" << 1 << "state" << 0)); + + { + BSONObjBuilder resultBuilder; + _registry->reportServiceInfo(&resultBuilder); + + ASSERT_BSONOBJ_EQ(BSON("primaryOnlyServices" << BSON("TestService" << 2)), + resultBuilder.obj()); + } + + TestServiceHangDuringInitialization.setMode(FailPoint::off); +} + TEST_F(PrimaryOnlyServiceTest, CreateWhenNotPrimary) { _registry->onStepDown(); diff --git a/src/mongo/db/repl/replication_info.cpp b/src/mongo/db/repl/replication_info.cpp index ec551d390ea..188de5e8d16 100644 --- a/src/mongo/db/repl/replication_info.cpp +++ b/src/mongo/db/repl/replication_info.cpp @@ -50,6 +50,7 @@ #include "mongo/db/ops/write_ops.h" #include "mongo/db/query/internal_plans.h" #include "mongo/db/repl/is_master_response.h" +#include "mongo/db/repl/primary_only_service.h" #include "mongo/db/repl/replication_auth.h" #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/repl/replication_process.h" @@ -86,12 +87,17 @@ constexpr auto kHelloString = "hello"_sd; constexpr auto kCamelCaseIsMasterString = "isMaster"_sd; constexpr auto kLowerCaseIsMasterString = "ismaster"_sd; +void appendPrimaryOnlyServiceInfo(ServiceContext* serviceContext, BSONObjBuilder* result) { + auto registry = PrimaryOnlyServiceRegistry::get(serviceContext); + registry->reportServiceInfo(result); +} + /** * Appends replication-related fields to the isMaster response. Returns the topology version that * was included in the response. */ TopologyVersion appendReplicationInfo(OperationContext* opCtx, - BSONObjBuilder& result, + BSONObjBuilder* result, bool appendReplicationProcess, bool useLegacyResponseFields, boost::optional clientTopologyVersion, @@ -108,9 +114,9 @@ TopologyVersion appendReplicationInfo(OperationContext* opCtx, } auto isMasterResponse = replCoord->awaitIsMasterResponse(opCtx, horizonParams, clientTopologyVersion, deadline); - result.appendElements(isMasterResponse->toBSON(useLegacyResponseFields)); + result->appendElements(isMasterResponse->toBSON(useLegacyResponseFields)); if (appendReplicationProcess) { - replCoord->appendSlaveInfoData(&result); + replCoord->appendSlaveInfoData(result); } invariant(isMasterResponse->getTopologyVersion()); return isMasterResponse->getTopologyVersion().get(); @@ -142,10 +148,10 @@ TopologyVersion appendReplicationInfo(OperationContext* opCtx, opCtx->sleepFor(Milliseconds(*maxAwaitTimeMS)); } - result.appendBool((useLegacyResponseFields ? "ismaster" : "isWritablePrimary"), - ReplicationCoordinator::get(opCtx)->isMasterForReportingPurposes()); + result->appendBool((useLegacyResponseFields ? "ismaster" : "isWritablePrimary"), + ReplicationCoordinator::get(opCtx)->isMasterForReportingPurposes()); - BSONObjBuilder topologyVersionBuilder(result.subobjStart("topologyVersion")); + BSONObjBuilder topologyVersionBuilder(result->subobjStart("topologyVersion")); currentTopologyVersion.serialize(&topologyVersionBuilder); return currentTopologyVersion; @@ -171,12 +177,14 @@ public: // TODO SERVER-50219: Change useLegacyResponseFields to false once the serverStatus changes // to remove master-slave terminology are merged. appendReplicationInfo(opCtx, - result, + &result, appendReplicationProcess, true /* useLegacyResponseFields */, boost::none /* clientTopologyVersion */, boost::none /* maxAwaitTimeMS */); + appendPrimaryOnlyServiceInfo(opCtx->getServiceContext(), &result); + auto rbid = ReplicationProcess::get(opCtx)->getRollbackID(); if (ReplicationProcess::kUninitializedRollbackId != rbid) { result.append("rbid", rbid); @@ -426,7 +434,7 @@ public: auto result = replyBuilder->getBodyBuilder(); auto currentTopologyVersion = appendReplicationInfo( - opCtx, result, 0, useLegacyResponseFields, clientTopologyVersion, maxAwaitTimeMS); + opCtx, &result, 0, useLegacyResponseFields, clientTopologyVersion, maxAwaitTimeMS); if (serverGlobalParams.clusterRole == ClusterRole::ConfigServer) { const int configServerModeNumber = 2; -- cgit v1.2.1