diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/ftdc/ftdc_server.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/stats/api_version_metrics.cpp | 21 | ||||
-rw-r--r-- | src/mongo/db/stats/api_version_metrics.h | 12 | ||||
-rw-r--r-- | src/mongo/db/stats/api_version_metrics_test.cpp | 104 |
4 files changed, 132 insertions, 10 deletions
diff --git a/src/mongo/db/ftdc/ftdc_server.cpp b/src/mongo/db/ftdc/ftdc_server.cpp index 558fce926f2..e5b605b0d51 100644 --- a/src/mongo/db/ftdc/ftdc_server.cpp +++ b/src/mongo/db/ftdc/ftdc_server.cpp @@ -238,9 +238,10 @@ public: // frequent schema changes. commandBuilder.append("transactions", BSON("includeLastCommitted" << false)); - // Exclude detailed query planning statistics. + // Exclude detailed query planning statistics and apiVersions. commandBuilder.append("metrics", - BSON("query" << BSON("multiPlanner" << BSON("histograms" << false)))); + BSON("query" << BSON("multiPlanner" << BSON("histograms" << false)) + << "apiVersions" << false)); if (gDiagnosticDataCollectionEnableLatencyHistograms.load()) { BSONObjBuilder subObjBuilder(commandBuilder.subobjStart("opLatencies")); diff --git a/src/mongo/db/stats/api_version_metrics.cpp b/src/mongo/db/stats/api_version_metrics.cpp index 79821a6bfeb..345cacaeddf 100644 --- a/src/mongo/db/stats/api_version_metrics.cpp +++ b/src/mongo/db/stats/api_version_metrics.cpp @@ -43,6 +43,11 @@ APIVersionMetrics& APIVersionMetrics::get(ServiceContext* svc) { void APIVersionMetrics::update(const std::string& appName, const APIParameters& apiParams) { Date_t now = getGlobalServiceContext()->getFastClockSource()->now(); stdx::lock_guard<Latch> lk(_mutex); + // Ensure that the number of saved app names does not exceed the limit. + if (!_apiVersionMetrics.count(appName) && _apiVersionMetrics.size() >= KMaxNumOfSavedAppNames) { + return; + } + if (apiParams.getAPIVersion()) { _apiVersionMetrics[appName][*apiParams.getAPIVersion()] = now; } else { @@ -81,8 +86,16 @@ void APIVersionMetrics::appendAPIVersionMetricsInfo(BSONObjBuilder* b) { stdx::lock_guard<Latch> lk(_mutex); _removeStaleTimestamps(lk, now); + _appendAPIVersionData(b); +} +void APIVersionMetrics::_appendAPIVersionData(BSONObjBuilder* b) { + int numOfEntries = 0; for (const auto& [appName, versionTimestamps] : _apiVersionMetrics) { + if (numOfEntries++ == KMaxNumOfOutputAppNames) { + break; + } + BSONArrayBuilder subArrBuilder(b->subarrayStart(appName)); if (versionTimestamps.find("default") != versionTimestamps.end()) { @@ -102,6 +115,14 @@ void APIVersionMetrics::appendAPIVersionMetricsInfo(BSONObjBuilder* b) { } } +void APIVersionMetrics::appendAPIVersionMetricsInfo_forTest(BSONObjBuilder* b) { + Date_t now = getGlobalServiceContext()->getFastClockSource()->now(); + stdx::lock_guard<Latch> lk(_mutex); + + _removeStaleTimestamps(lk, now); + _appendAPIVersionData(b); +} + APIVersionMetrics::APIVersionMetricsMap APIVersionMetrics::getAPIVersionMetrics_forTest() { Date_t now = getGlobalServiceContext()->getFastClockSource()->now(); stdx::lock_guard<Latch> lk(_mutex); diff --git a/src/mongo/db/stats/api_version_metrics.h b/src/mongo/db/stats/api_version_metrics.h index 12288ecdf08..6d2b7eb6ce4 100644 --- a/src/mongo/db/stats/api_version_metrics.h +++ b/src/mongo/db/stats/api_version_metrics.h @@ -44,6 +44,14 @@ namespace mongo { */ class APIVersionMetrics { public: + // To ensure that the BSONObject doesn't exceed the size limit, the 'appName' field has a limit + // of 128 bytes, which results in an output of approximately 128KB for app names. + static constexpr int KMaxNumOfOutputAppNames = 1000; + + // To prevent unbounded memory usage, we limit the size of the saved app name to approximately + // 384KB, as it is stored for 24 hours. + static constexpr int KMaxNumOfSavedAppNames = KMaxNumOfOutputAppNames * 3; + using APIVersionMetricsMap = stdx::unordered_map<std::string, stdx::unordered_map<std::string, Date_t>>; @@ -58,11 +66,15 @@ public: APIVersionMetricsMap getAPIVersionMetrics_forTest(); + void appendAPIVersionMetricsInfo_forTest(BSONObjBuilder* b); + class APIVersionMetricsSSM; private: void _removeStaleTimestamps(WithLock lk, Date_t now); + void _appendAPIVersionData(BSONObjBuilder* b); + mutable Mutex _mutex = MONGO_MAKE_LATCH("APIVersionMetrics::_mutex"); // Map of maps for API version metrics. For every application, for each API version, we store diff --git a/src/mongo/db/stats/api_version_metrics_test.cpp b/src/mongo/db/stats/api_version_metrics_test.cpp index 53c952f0269..e5f099a44c4 100644 --- a/src/mongo/db/stats/api_version_metrics_test.cpp +++ b/src/mongo/db/stats/api_version_metrics_test.cpp @@ -68,6 +68,7 @@ protected: } void assertShouldExistInMap(APIVersionMetrics::APIVersionMetricsMap metricsMap, + std::string appName, std::string target, bool shouldExist = true) { auto metricsIter = metricsMap.find(appName); @@ -93,7 +94,7 @@ TEST_F(APIVersionMetricsTest, StoresDefaultMetrics) { auto metricsMap = getMetrics().getAPIVersionMetrics_forTest(); // Verify that the metric was inserted with API version set to 'default'. - assertShouldExistInMap(metricsMap, "default"); + assertShouldExistInMap(metricsMap, appName, "default"); } TEST_F(APIVersionMetricsTest, StoresNonDefaultMetrics) { @@ -104,7 +105,7 @@ TEST_F(APIVersionMetricsTest, StoresNonDefaultMetrics) { auto metricsMap = getMetrics().getAPIVersionMetrics_forTest(); // Verify that the metric was inserted with API version set to '1'. - assertShouldExistInMap(metricsMap, "1"); + assertShouldExistInMap(metricsMap, appName, "1"); } TEST_F(APIVersionMetricsTest, RemovesStaleMetrics) { @@ -113,8 +114,8 @@ TEST_F(APIVersionMetricsTest, RemovesStaleMetrics) { // Verify that the default metric was inserted correctly. auto metricsMap = getMetrics().getAPIVersionMetrics_forTest(); - assertShouldExistInMap(metricsMap, "default"); - assertShouldExistInMap(metricsMap, "1", false /* shouldExist */); + assertShouldExistInMap(metricsMap, appName, "default"); + assertShouldExistInMap(metricsMap, appName, "1", false /* shouldExist */); // Advance the clock by more than a half day. auto timeToAdvance = Milliseconds(1005 * 60 * 60 * 12); @@ -126,8 +127,8 @@ TEST_F(APIVersionMetricsTest, RemovesStaleMetrics) { // Verify that both metrics are still within the map. metricsMap = getMetrics().getAPIVersionMetrics_forTest(); - assertShouldExistInMap(metricsMap, "default"); - assertShouldExistInMap(metricsMap, "1"); + assertShouldExistInMap(metricsMap, appName, "default"); + assertShouldExistInMap(metricsMap, appName, "1"); // Advance the clock by more than a half day. timeToAdvance = Milliseconds(1005 * 60 * 60 * 12); @@ -135,8 +136,8 @@ TEST_F(APIVersionMetricsTest, RemovesStaleMetrics) { // Verify that the default metric was removed, but the metric with API version 1 was not. metricsMap = getMetrics().getAPIVersionMetrics_forTest(); - assertShouldExistInMap(metricsMap, "default", false /* shouldExist */); - assertShouldExistInMap(metricsMap, "1"); + assertShouldExistInMap(metricsMap, appName, "default", false /* shouldExist */); + assertShouldExistInMap(metricsMap, appName, "1"); // Advance the clock by more than a half day. timeToAdvance = Milliseconds(1005 * 60 * 60 * 12); @@ -147,5 +148,92 @@ TEST_F(APIVersionMetricsTest, RemovesStaleMetrics) { ASSERT(metricsMap.find(appName) == metricsMap.end()); } +TEST_F(APIVersionMetricsTest, TestOutputBSONSizeLimit) { + apiParams.setAPIVersion("1"); + ASSERT_TRUE(apiParams.getParamsPassed()); + APIParameters defaultApiParams; + ASSERT_FALSE(defaultApiParams.getParamsPassed()); + // Note that an additional entry will be added to the data, but it will not be included in the + // output that is displayed. + for (auto i = -1; i < APIVersionMetrics::KMaxNumOfOutputAppNames; i++) { + auto appNameStr = appName + ((i > -1) ? "_" + std::to_string(i) : ""); + getMetrics().update(appNameStr, defaultApiParams); + getMetrics().update(appNameStr, apiParams); + } + + // Verify that the metric was inserted correctly. + auto metricsMap = getMetrics().getAPIVersionMetrics_forTest(); + for (auto i = -1; i < APIVersionMetrics::KMaxNumOfOutputAppNames; i++) { + auto appNameStr = appName + ((i > -1) ? "_" + std::to_string(i) : ""); + assertShouldExistInMap(metricsMap, appNameStr, "1"); + assertShouldExistInMap(metricsMap, appNameStr, "default"); + } + + // Verify that output is capped. + BSONObjBuilder bob; + getMetrics().appendAPIVersionMetricsInfo_forTest(&bob); + auto outputObj = bob.obj(); + int notInOutput = 0; + for (auto i = -1; i < APIVersionMetrics::KMaxNumOfOutputAppNames; i++) { + auto appNameStr = appName + ((i > -1) ? "_" + std::to_string(i) : ""); + + if (!outputObj.hasField(appNameStr)) { + notInOutput++; + } else { + // Verify that output is correct. + BSONObj sub = outputObj.getObjectField(appNameStr); + std::set<std::string> apiVersions; + for (const auto& element : sub) { + apiVersions.insert(element.str()); + } + ASSERT_TRUE(apiVersions.size() == 2); + ASSERT_TRUE(apiVersions.count("default")); + ASSERT_TRUE(apiVersions.count("1")); + } + + ASSERT_TRUE(notInOutput < 2); + } +} + +TEST_F(APIVersionMetricsTest, TestSavedAppNamesLimit) { + // Note that an additional entry will be added to the data, but it will not be included in the + // output that is displayed. + apiParams.setAPIVersion("1"); + ASSERT_TRUE(apiParams.getParamsPassed()); + APIParameters defaultApiParams; + ASSERT_FALSE(defaultApiParams.getParamsPassed()); + for (auto i = 0; i < APIVersionMetrics::KMaxNumOfSavedAppNames; i++) { + auto appNameStr = appName + "_" + std::to_string(i); + getMetrics().update(appNameStr, defaultApiParams); + } + + // Verify that the metric was inserted correctly. + auto metricsMap = getMetrics().getAPIVersionMetrics_forTest(); + for (auto i = 0; i < APIVersionMetrics::KMaxNumOfSavedAppNames; i++) { + auto appNameStr = appName + "_" + std::to_string(i); + assertShouldExistInMap(metricsMap, appNameStr, "default"); + } + + // Attempting to add another entry beyond the limit will not succeed. + getMetrics().update(appName, defaultApiParams); + metricsMap = getMetrics().getAPIVersionMetrics_forTest(); + auto metricsIter = metricsMap.find(appName); + ASSERT(metricsIter == metricsMap.end()); + + // Modifying existing elements is permitted. + for (auto i = 0; i < APIVersionMetrics::KMaxNumOfSavedAppNames; i++) { + auto appNameStr = appName + "_" + std::to_string(i); + getMetrics().update(appNameStr, apiParams); + } + + // Verify that the metric got updated. + metricsMap = getMetrics().getAPIVersionMetrics_forTest(); + for (auto i = 0; i < APIVersionMetrics::KMaxNumOfSavedAppNames; i++) { + auto appNameStr = appName + "_" + std::to_string(i); + assertShouldExistInMap(metricsMap, appNameStr, "default"); + assertShouldExistInMap(metricsMap, appNameStr, "1"); + } +} + } // namespace } // namespace mongo |