diff options
author | Jess Balint <jbalint@gmail.com> | 2022-04-21 20:34:27 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-04-21 21:56:15 +0000 |
commit | a253db72b08fd159332ad121e644fcea651d10db (patch) | |
tree | 18afd5147999ef65a602905f52a585321ad672a6 | |
parent | 85c39ef1591fdacdf221d168a0ae5a8b08d8b5ce (diff) | |
download | mongo-a253db72b08fd159332ad121e644fcea651d10db.tar.gz |
SERVER-65271 serverStatus should allow fine-grained metrics exclusion
(cherry picked from commit d964e2b912e26a96ee61cf4272102c8b4415fd2e)
-rw-r--r-- | jstests/noPassthrough/server_status_metrics_exclusion.js | 65 | ||||
-rw-r--r-- | src/mongo/db/commands/server_status_command.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/commands/server_status_internal.cpp | 52 | ||||
-rw-r--r-- | src/mongo/db/commands/server_status_internal.h | 9 |
4 files changed, 127 insertions, 9 deletions
diff --git a/jstests/noPassthrough/server_status_metrics_exclusion.js b/jstests/noPassthrough/server_status_metrics_exclusion.js new file mode 100644 index 00000000000..2127882c9a3 --- /dev/null +++ b/jstests/noPassthrough/server_status_metrics_exclusion.js @@ -0,0 +1,65 @@ +/** + * Tests that serverStatus metrics are filtered when requested. + */ +(function() { +"use strict"; +const mongod = MongoRunner.runMongod(); +const dbName = jsTestName(); +const db = mongod.getDB(dbName); + +// Verify some assumptions about metrics structure for later tests. +let serverStatusMetrics = db.serverStatus().metrics; + +assert(serverStatusMetrics.document.hasOwnProperty("deleted")); +assert(serverStatusMetrics.document.hasOwnProperty("inserted")); +assert(serverStatusMetrics.query.hasOwnProperty("planCacheTotalSizeEstimateBytes")); + +// Exclude the "document.deleted" field. +serverStatusMetrics = db.serverStatus({metrics: {document: {deleted: false}}}).metrics; + +assert(!serverStatusMetrics.document.hasOwnProperty("deleted")); +assert(serverStatusMetrics.document.hasOwnProperty("inserted")); + +// Exclude the "document.deleted" and "document.inserted" fields. +serverStatusMetrics = + db.serverStatus({metrics: {document: {deleted: false, inserted: false}}}).metrics; + +assert(!serverStatusMetrics.document.hasOwnProperty("deleted")); +assert(!serverStatusMetrics.document.hasOwnProperty("inserted")); + +// Exclude the "document.deleted" and "query.planCacheTotalSizeEstimateBytes" fields. +serverStatusMetrics = + db.serverStatus({ + metrics: {document: {deleted: false}, query: {planCacheTotalSizeEstimateBytes: false}} + }).metrics; + +assert(!serverStatusMetrics.document.hasOwnProperty("deleted")); +assert(!serverStatusMetrics.query.hasOwnProperty("planCacheTotalSizeEstimateBytes")); + +// Include a "true" value for the "document.deleted" field. It should be included (ie, no-op). +serverStatusMetrics = db.serverStatus({metrics: {document: {deleted: true}}}).metrics; + +assert(serverStatusMetrics.document.hasOwnProperty("deleted")); +assert(serverStatusMetrics.document.hasOwnProperty("inserted")); + +// Attempt a non-boolean values which should be rejected (uassert). +assert.commandFailedWithCode(db.serverStatus({metrics: {document: "Non-boolean"}}), + [ErrorCodes.TypeMismatch]); + +assert.commandFailedWithCode(db.serverStatus({metrics: {document: {deleted: "Non-boolean"}}}), + [ErrorCodes.TypeMismatch]); + +assert.commandFailedWithCode(db.serverStatus({metrics: {document: {deleted: ["Non-boolean"]}}}), + [ErrorCodes.TypeMismatch]); + +assert.commandFailedWithCode( + db.serverStatus({metrics: {document: {deleted: {invalidObjectAtLeftLevel: 1}}}}), + [ErrorCodes.TypeMismatch]); + +// Exclude the "document" subtree. +serverStatusMetrics = db.serverStatus({metrics: {document: false}}).metrics; + +assert(!serverStatusMetrics.hasOwnProperty("document")); + +MongoRunner.stopMongod(mongod); +})(); diff --git a/src/mongo/db/commands/server_status_command.cpp b/src/mongo/db/commands/server_status_command.cpp index c5691a7b4ad..482ad472fb2 100644 --- a/src/mongo/db/commands/server_status_command.cpp +++ b/src/mongo/db/commands/server_status_command.cpp @@ -128,11 +128,17 @@ public: // --- counters bool includeMetricTree = MetricTree::theMetricTree != nullptr; - if (cmdObj["metrics"].type() && !cmdObj["metrics"].trueValue()) + auto metricsEl = cmdObj["metrics"_sd]; + if (metricsEl.type() && !metricsEl.trueValue()) includeMetricTree = false; if (includeMetricTree) { - MetricTree::theMetricTree->appendTo(result); + if (metricsEl.type() == BSONType::Object) { + MetricTree::theMetricTree->appendTo(BSON("metrics" << metricsEl.embeddedObject()), + result); + } else { + MetricTree::theMetricTree->appendTo(result); + } } // --- some hard coded global things hard to pull out diff --git a/src/mongo/db/commands/server_status_internal.cpp b/src/mongo/db/commands/server_status_internal.cpp index c981a65fd4b..82698b5e59a 100644 --- a/src/mongo/db/commands/server_status_internal.cpp +++ b/src/mongo/db/commands/server_status_internal.cpp @@ -31,6 +31,7 @@ #include <iostream> +#include "mongo/bson/bsontypes.h" #include "mongo/db/commands/server_status_metric.h" #include "mongo/util/str.h" @@ -71,16 +72,53 @@ void MetricTree::_add(const string& path, ServerStatusMetric* metric) { } void MetricTree::appendTo(BSONObjBuilder& b) const { - for (map<string, ServerStatusMetric*>::const_iterator i = _metrics.begin(); i != _metrics.end(); - ++i) { - i->second->appendAtLeaf(b); + for (const auto& i : _metrics) { + i.second->appendAtLeaf(b); } - for (map<string, MetricTree*>::const_iterator i = _subtrees.begin(); i != _subtrees.end(); - ++i) { - BSONObjBuilder bb(b.subobjStart(i->first)); - i->second->appendTo(bb); + for (const auto& i : _subtrees) { + BSONObjBuilder bb(b.subobjStart(i.first)); + i.second->appendTo(bb); bb.done(); } } + +void MetricTree::appendTo(const BSONObj& excludePaths, BSONObjBuilder& b) const { + auto fieldNamesInExclude = excludePaths.getFieldNames<stdx::unordered_set<std::string>>(); + for (const auto& i : _metrics) { + auto key = i.first; + auto el = fieldNamesInExclude.contains(key) ? excludePaths.getField(key) : BSONElement(); + if (el) { + uassert(ErrorCodes::TypeMismatch, + "Exclusion value for a leaf must be a boolean.", + el.type() == Bool); + if (el.boolean() == false) { + continue; + } + } + i.second->appendAtLeaf(b); + } + + for (const auto& i : _subtrees) { + auto key = i.first; + auto el = fieldNamesInExclude.contains(key) ? excludePaths.getField(key) : BSONElement(); + if (el) { + uassert(ErrorCodes::TypeMismatch, + "Exclusion value must be a boolean or a nested object.", + el.type() == Bool || el.type() == Object); + if (el.isBoolean() && el.boolean() == false) { + continue; + } + } + + BSONObjBuilder bb(b.subobjStart(key)); + if (el.type() == Object) { + i.second->appendTo(el.embeddedObject(), bb); + } else { + i.second->appendTo(bb); + } + bb.done(); + } +} + } // namespace mongo diff --git a/src/mongo/db/commands/server_status_internal.h b/src/mongo/db/commands/server_status_internal.h index f9bde775db3..b59b0b36762 100644 --- a/src/mongo/db/commands/server_status_internal.h +++ b/src/mongo/db/commands/server_status_internal.h @@ -42,8 +42,17 @@ class MetricTree { public: void add(ServerStatusMetric* metric); + /** + * Append the metrics tree to the given BSON builder. + */ void appendTo(BSONObjBuilder& b) const; + /** + * Implementation of appendTo which allows tree of exclude paths. The alternative overload is + * preferred to avoid overhead when no excludes are present. + */ + void appendTo(const BSONObj& excludePaths, BSONObjBuilder& b) const; + static MetricTree* theMetricTree; private: |