summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/ftdc/ftdc_server.cpp5
-rw-r--r--src/mongo/db/stats/api_version_metrics.cpp21
-rw-r--r--src/mongo/db/stats/api_version_metrics.h12
-rw-r--r--src/mongo/db/stats/api_version_metrics_test.cpp104
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