summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorMilena Ivanova <milena.ivanova@mongodb.com>2021-05-28 09:24:11 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-08-05 08:24:59 +0000
commit98b4a0a2b2c4a230efe1c2ddc3da487a575d670d (patch)
tree7269ee48a69bfaea61b77a8d3f78bc072735b3b0 /src/mongo
parent85976973ce08a8d7dedbcc108d4ace00c25c9b04 (diff)
downloadmongo-98b4a0a2b2c4a230efe1c2ddc3da487a575d670d.tar.gz
SERVER-56602 Track usage of match expressions in serverStatus
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/commands/find_cmd.cpp2
-rw-r--r--src/mongo/db/commands/run_aggregate.cpp2
-rw-r--r--src/mongo/db/curop.h11
-rw-r--r--src/mongo/db/matcher/SConscript1
-rw-r--r--src/mongo/db/matcher/expression_leaf.cpp28
-rw-r--r--src/mongo/db/matcher/expression_leaf.h2
-rw-r--r--src/mongo/db/matcher/expression_parser.cpp39
-rw-r--r--src/mongo/db/pipeline/expression_context.cpp21
-rw-r--r--src/mongo/db/pipeline/expression_context.h28
-rw-r--r--src/mongo/db/query/canonical_query.cpp5
-rw-r--r--src/mongo/db/stats/counters.cpp1
-rw-r--r--src/mongo/db/stats/counters.h33
12 files changed, 156 insertions, 17 deletions
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index 9dd3393360e..31faa7c7bb3 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -158,6 +158,8 @@ boost::intrusive_ptr<ExpressionContext> makeExpressionContext(
CurOp::get(opCtx)->dbProfileLevel() > 0 // mayDbProfile
);
expCtx->tempDir = storageGlobalParams.dbpath + "/_tmp";
+ expCtx->startExpressionCounters();
+
return expCtx;
}
diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp
index 789581c7337..a25075c2839 100644
--- a/src/mongo/db/commands/run_aggregate.cpp
+++ b/src/mongo/db/commands/run_aggregate.cpp
@@ -764,7 +764,9 @@ Status runAggregate(OperationContext* opCtx,
expCtx = makeExpressionContext(
opCtx, request, std::move(*collatorToUse), uuid, collatorToUseMatchesDefault);
+ expCtx->startExpressionCounters();
auto pipeline = Pipeline::parse(request.getPipeline(), expCtx);
+ expCtx->stopExpressionCounters();
// Check that the view's collation matches the collation of any views involved in the
// pipeline.
diff --git a/src/mongo/db/curop.h b/src/mongo/db/curop.h
index 8c3adec3a91..afb48de2440 100644
--- a/src/mongo/db/curop.h
+++ b/src/mongo/db/curop.h
@@ -775,6 +775,16 @@ public:
_tickSource = tickSource;
}
+ /**
+ * Merge match counters from the current operation into the global map and stop counting.
+ */
+ void stopMatchExprCounter();
+
+ /**
+ * Increment the counter for the match expression with given name in the current operation.
+ */
+ void incrementMatchExprCounter(StringData name);
+
private:
class CurOpStack;
@@ -846,4 +856,5 @@ private:
TickSource* _tickSource = nullptr;
};
+
} // namespace mongo
diff --git a/src/mongo/db/matcher/SConscript b/src/mongo/db/matcher/SConscript
index aa1badd3cb7..f7b42816cb8 100644
--- a/src/mongo/db/matcher/SConscript
+++ b/src/mongo/db/matcher/SConscript
@@ -70,6 +70,7 @@ env.Library(
'$BUILD_DIR/mongo/db/pipeline/expression_context',
'$BUILD_DIR/mongo/db/query/collation/collator_interface',
'$BUILD_DIR/mongo/db/query/query_knobs',
+ '$BUILD_DIR/mongo/db/stats/counters',
'$BUILD_DIR/mongo/idl/idl_parser',
'$BUILD_DIR/mongo/util/regex_util',
'$BUILD_DIR/third_party/shim_pcrecpp',
diff --git a/src/mongo/db/matcher/expression_leaf.cpp b/src/mongo/db/matcher/expression_leaf.cpp
index c1f9a9ebb44..8f000b52728 100644
--- a/src/mongo/db/matcher/expression_leaf.cpp
+++ b/src/mongo/db/matcher/expression_leaf.cpp
@@ -778,29 +778,29 @@ bool BitTestMatchExpression::matchesSingleElement(const BSONElement& e,
return performBitTest(eValue);
}
-void BitTestMatchExpression::debugString(StringBuilder& debug, int indentationLevel) const {
- _debugAddSpace(debug, indentationLevel);
-
- debug << path() << " ";
-
+std::string BitTestMatchExpression::name() const {
switch (matchType()) {
case BITS_ALL_SET:
- debug << "$bitsAllSet:";
- break;
+ return "$bitsAllSet";
+
case BITS_ALL_CLEAR:
- debug << "$bitsAllClear:";
- break;
+ return "$bitsAllClear";
+
case BITS_ANY_SET:
- debug << "$bitsAnySet:";
- break;
+ return "$bitsAnySet";
+
case BITS_ANY_CLEAR:
- debug << "$bitsAnyClear:";
- break;
+ return "$bitsAnyClear";
+
default:
MONGO_UNREACHABLE;
}
+}
+
+void BitTestMatchExpression::debugString(StringBuilder& debug, int indentationLevel) const {
+ _debugAddSpace(debug, indentationLevel);
- debug << " [";
+ debug << path() << " " << name() << ": [";
for (size_t i = 0; i < _bitPositions.size(); i++) {
debug << _bitPositions[i];
if (i != _bitPositions.size() - 1) {
diff --git a/src/mongo/db/matcher/expression_leaf.h b/src/mongo/db/matcher/expression_leaf.h
index 248e68f1f44..f2c3a5f13a3 100644
--- a/src/mongo/db/matcher/expression_leaf.h
+++ b/src/mongo/db/matcher/expression_leaf.h
@@ -693,6 +693,8 @@ public:
return _bitMask;
}
+ std::string name() const;
+
private:
ExpressionOptimizerFunc getOptimizer() const final {
return [](std::unique_ptr<MatchExpression> expression) { return expression; };
diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp
index 8839b537f4c..06016d8c3a4 100644
--- a/src/mongo/db/matcher/expression_parser.cpp
+++ b/src/mongo/db/matcher/expression_parser.cpp
@@ -69,6 +69,7 @@
#include "mongo/db/namespace_string.h"
#include "mongo/db/query/dbref.h"
#include "mongo/db/query/query_knobs_gen.h"
+#include "mongo/db/stats/counters.h"
#include "mongo/util/str.h"
#include "mongo/util/string_map.h"
@@ -173,6 +174,7 @@ StatusWithMatchExpression parseRegexElement(StringData name,
if (e.type() != BSONType::RegEx)
return {Status(ErrorCodes::BadValue, "not a regex")};
+ expCtx->incrementMatchExprCounter("$regex");
return {std::make_unique<RegexMatchExpression>(
name,
e.regex(),
@@ -319,6 +321,7 @@ StatusWithMatchExpression parse(const BSONObj& obj,
if (auto&& expr = parsedExpression.getValue())
root->add(std::move(expr));
+ expCtx->incrementMatchExprCounter(e.fieldNameStringData());
continue;
}
@@ -345,7 +348,6 @@ StatusWithMatchExpression parse(const BSONObj& obj,
auto result = parseRegexElement(e.fieldNameStringData(), e, expCtx);
if (!result.isOK())
return result;
-
addExpressionToRoot(expCtx, root.get(), std::move(result.getValue()));
continue;
}
@@ -361,7 +363,7 @@ StatusWithMatchExpression parse(const BSONObj& obj,
allowedFeatures);
if (!eq.isOK())
return eq;
-
+ expCtx->incrementMatchExprCounter("$eq");
addExpressionToRoot(expCtx, root.get(), std::move(eq.getValue()));
}
@@ -423,7 +425,9 @@ StatusWithMatchExpression parseSampleRate(StringData name,
// DeMorgan's law here you will be suprised that $sampleRate will accept NaN as a valid
// argument.
return {Status(ErrorCodes::BadValue, "numeric argument to $sampleRate must be in [0, 1]")};
- } else if (x == kRandomMinValue) {
+ }
+
+ if (x == kRandomMinValue) {
return std::make_unique<ExprMatchExpression>(
ExpressionConstant::create(expCtx.get(), Value(false)),
expCtx,
@@ -1202,6 +1206,7 @@ StatusWithMatchExpression parseGeo(StringData name,
return status;
}
expCtx->sbeCompatible = false;
+ expCtx->incrementMatchExprCounter(section.firstElementFieldName());
return {std::make_unique<GeoNearMatchExpression>(name, nq.release(), section)};
}
}
@@ -2017,6 +2022,7 @@ Status parseSub(StringData name,
if (!s.isOK())
return s.getStatus();
+ expCtx->incrementMatchExprCounter(deep.fieldNameStringData());
if (s.getValue()) {
addExpressionToRoot(expCtx, root, std::move(s.getValue()));
}
@@ -2179,6 +2185,33 @@ retrievePathlessParser(StringData name) {
}
return func->second;
}
+
+MONGO_INITIALIZER_WITH_PREREQUISITES(MatchExpressionCounters,
+ ("PathlessOperatorMap", "MatchExpressionParser"))
+(InitializerContext* context) {
+ static const std::set<std::string> exceptionsSet{"within", // deprecated
+ "geoNear", // aggregation stage
+ "db", // $-prefixed field names
+ "id",
+ "ref",
+ "options"};
+
+ for (auto&& [name, keyword] : *queryOperatorMap) {
+ if (name[0] == '_' || exceptionsSet.count(name) > 0) {
+ continue;
+ }
+ operatorCountersMatchExpressions.addMatchExprCounter("$" + name);
+ }
+ for (auto&& [name, fn] : *pathlessOperatorMap) {
+ if (name[0] == '_' || exceptionsSet.count(name) > 0) {
+ continue;
+ }
+ operatorCountersMatchExpressions.addMatchExprCounter("$" + name);
+ }
+ operatorCountersMatchExpressions.addMatchExprCounter("$not");
+}
+
+
} // namespace
boost::optional<PathAcceptingKeyword> MatchExpressionParser::parsePathAcceptingKeyword(
diff --git a/src/mongo/db/pipeline/expression_context.cpp b/src/mongo/db/pipeline/expression_context.cpp
index c4cb0f6e411..69698ab0289 100644
--- a/src/mongo/db/pipeline/expression_context.cpp
+++ b/src/mongo/db/pipeline/expression_context.cpp
@@ -36,6 +36,7 @@
#include "mongo/db/pipeline/process_interface/stub_mongo_process_interface.h"
#include "mongo/db/query/collation/collation_spec.h"
#include "mongo/db/query/collation/collator_factory_interface.h"
+#include "mongo/db/stats/counters.h"
#include "mongo/util/intrusive_counter.h"
namespace mongo {
@@ -223,4 +224,24 @@ intrusive_ptr<ExpressionContext> ExpressionContext::copyWith(
return expCtx;
}
+void ExpressionContext::startExpressionCounters() {
+ if (!_expressionCounters) {
+ _expressionCounters = boost::make_optional<ExpressionCounters>({});
+ }
+}
+
+void ExpressionContext::incrementMatchExprCounter(StringData name) {
+ if (_expressionCounters) {
+ ++_expressionCounters.get().matchExprCountersMap[name];
+ }
+}
+
+void ExpressionContext::stopExpressionCounters() {
+ if (_expressionCounters) {
+ operatorCountersMatchExpressions.mergeCounters(
+ _expressionCounters.get().matchExprCountersMap);
+ }
+ _expressionCounters = boost::none;
+}
+
} // namespace mongo
diff --git a/src/mongo/db/pipeline/expression_context.h b/src/mongo/db/pipeline/expression_context.h
index b40028dc30a..398a162b283 100644
--- a/src/mongo/db/pipeline/expression_context.h
+++ b/src/mongo/db/pipeline/expression_context.h
@@ -58,6 +58,15 @@ namespace mongo {
class AggregateCommandRequest;
+/**
+ * The structure ExpressionCounters encapsulates counters for match, aggregate, and other
+ * expression types as seen in the end-user queries.
+ */
+struct ExpressionCounters {
+ StringMap<uint64_t> aggExprCountersMap;
+ StringMap<uint64_t> matchExprCountersMap;
+};
+
class ExpressionContext : public RefCountable {
public:
static constexpr size_t kMaxSubPipelineViewDepth = 20;
@@ -316,6 +325,22 @@ public:
return JsExecution::get(opCtx, scopeObj, ns.db(), loadStoredProcedures, jsHeapLimitMB);
}
+ /**
+ * Create optional internal expression counters and start counting.
+ */
+ void startExpressionCounters();
+
+ /**
+ * Increment the counter for the match expression with a given name.
+ */
+ void incrementMatchExprCounter(StringData name);
+
+ /**
+ * Merge expression counters from the current expression context into the global maps
+ * and stop counting.
+ */
+ void stopExpressionCounters();
+
// The explain verbosity requested by the user, or boost::none if no explain was requested.
boost::optional<ExplainOptions::Verbosity> explain;
@@ -413,6 +438,9 @@ protected:
StringMap<ResolvedNamespace> _resolvedNamespaces;
int _interruptCounter = kInterruptCheckPeriod;
+
+private:
+ boost::optional<ExpressionCounters> _expressionCounters = boost::none;
};
} // namespace mongo
diff --git a/src/mongo/db/query/canonical_query.cpp b/src/mongo/db/query/canonical_query.cpp
index de2e39895ff..87efc4cc259 100644
--- a/src/mongo/db/query/canonical_query.cpp
+++ b/src/mongo/db/query/canonical_query.cpp
@@ -126,6 +126,11 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize(
if (!statusWithMatcher.isOK()) {
return statusWithMatcher.getStatus();
}
+
+ // Stop counting match expressions after they have been parsed to exclude expressions created
+ // during optimization and other processing steps.
+ newExpCtx->stopExpressionCounters();
+
std::unique_ptr<MatchExpression> me = std::move(statusWithMatcher.getValue());
Status initStatus =
diff --git a/src/mongo/db/stats/counters.cpp b/src/mongo/db/stats/counters.cpp
index 1cf3e4f4f72..2f9b070b888 100644
--- a/src/mongo/db/stats/counters.cpp
+++ b/src/mongo/db/stats/counters.cpp
@@ -314,4 +314,5 @@ AuthCounter authCounter;
AggStageCounters aggStageCounters;
DotsAndDollarsFieldsCounters dotsAndDollarsFieldsCounters;
OperatorCountersExpressions operatorCountersExpressions;
+OperatorCountersMatchExpressions operatorCountersMatchExpressions;
} // namespace mongo
diff --git a/src/mongo/db/stats/counters.h b/src/mongo/db/stats/counters.h
index 397951bdd7b..18e39b1366a 100644
--- a/src/mongo/db/stats/counters.h
+++ b/src/mongo/db/stats/counters.h
@@ -363,4 +363,37 @@ private:
};
extern OperatorCountersExpressions operatorCountersExpressions;
+
+/**
+ * Global counters for match expressions.
+ */
+class OperatorCountersMatchExpressions {
+private:
+ struct MatchExprCounter {
+ MatchExprCounter(StringData name) : metric("operatorCounters.match." + name, &counter) {}
+
+ Counter64 counter;
+ ServerStatusMetricField<Counter64> metric;
+ };
+
+public:
+ void addMatchExprCounter(StringData name) {
+ operatorCountersMatchExprMap[name] = std::make_unique<MatchExprCounter>(name);
+ }
+
+ void mergeCounters(StringMap<uint64_t>& toMerge) {
+ for (auto&& [name, cnt] : toMerge) {
+ if (auto it = operatorCountersMatchExprMap.find(name);
+ it != operatorCountersMatchExprMap.end()) {
+ it->second->counter.increment(cnt);
+ }
+ }
+ }
+
+private:
+ // Map of match expressions to the number of occurrences in queries.
+ StringMap<std::unique_ptr<MatchExprCounter>> operatorCountersMatchExprMap = {};
+};
+
+extern OperatorCountersMatchExpressions operatorCountersMatchExpressions;
} // namespace mongo