summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGil Alon <gil.alon@mongodb.com>2023-02-17 14:08:24 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-02-17 14:56:08 +0000
commita0adc8d5bad1b20d45802da6a0fc1c28d3b2c15b (patch)
treeea738583568174a0fcfc76624a4140535d95b831
parentf4050044d6521d3d4f4b5873addd7a9b234df1a7 (diff)
downloadmongo-a0adc8d5bad1b20d45802da6a0fc1c28d3b2c15b.tar.gz
SERVER-71392 Add counters to track jsonSchema usage in create and collMod
-rw-r--r--jstests/noPassthrough/server_status_metric_validator_and_json_schema.js175
-rw-r--r--src/mongo/db/catalog/coll_mod.cpp6
-rw-r--r--src/mongo/db/catalog/database_impl.cpp4
-rw-r--r--src/mongo/db/stats/counters.cpp1
-rw-r--r--src/mongo/db/stats/counters.h46
5 files changed, 232 insertions, 0 deletions
diff --git a/jstests/noPassthrough/server_status_metric_validator_and_json_schema.js b/jstests/noPassthrough/server_status_metric_validator_and_json_schema.js
new file mode 100644
index 00000000000..a1ed8bf91da
--- /dev/null
+++ b/jstests/noPassthrough/server_status_metric_validator_and_json_schema.js
@@ -0,0 +1,175 @@
+/**
+ * Tests for serverStatus metrics.commands.<name>.validator stats.
+ * @tags: [requires_sharding]
+ *
+ */
+(function() {
+"use strict";
+
+function runCommandAndCheckValidatorCount({cmdToRun, cmdName, countDict, error, notFirst}) {
+ let metricsBeforeCommandInvoked = {failed: 0, jsonSchema: 0, total: 0};
+ if (notFirst) {
+ metricsBeforeCommandInvoked = db.serverStatus().metrics.commands[cmdName].validator;
+ }
+ if (error) {
+ cmdToRun();
+ } else {
+ assert.commandWorked(cmdToRun());
+ }
+ const metricsAfterCommandInvoked = db.serverStatus().metrics.commands[cmdName].validator;
+ assert.eq(metricsAfterCommandInvoked.total - metricsBeforeCommandInvoked.total,
+ countDict.total,
+ metricsAfterCommandInvoked);
+ assert.eq(metricsAfterCommandInvoked.failed - metricsBeforeCommandInvoked.failed,
+ countDict.failed,
+ metricsAfterCommandInvoked);
+ assert.eq(metricsAfterCommandInvoked.jsonSchema - metricsBeforeCommandInvoked.jsonSchema,
+ countDict.jsonSchema,
+ metricsAfterCommandInvoked);
+}
+
+function runTests(db, collName, collCount) {
+ // Test that validator count is 0 if no validator specified.
+ runCommandAndCheckValidatorCount({
+ cmdToRun: () => db.createCollection(collName),
+ cmdName: "create",
+ countDict: {failed: NumberLong(0), jsonSchema: NumberLong(0), total: NumberLong(0)},
+ error: false,
+ notFirst: false
+ });
+ collCount++;
+ assert.commandWorked(db[collName].createIndex({a: 1}));
+ runCommandAndCheckValidatorCount({
+ cmdToRun: () => db.runCommand(
+ {collMod: collName, index: {keyPattern: {a: 1}, expireAfterSeconds: 3600}}),
+ cmdName: "collMod",
+ countDict: {failed: NumberLong(0), jsonSchema: NumberLong(0), total: NumberLong(0)},
+ error: false,
+ notFirst: false
+ });
+
+ // Test that validator total and failed count increments when a $jsonSchema validation error is
+ // raised.
+ let schema = {
+ '$jsonSchema': {
+ 'bsonType': "object",
+ 'properties': {
+ 'a': "",
+ 'b': {
+ 'bsonType': "number",
+ },
+ 'c': {
+ 'bsonType': "number",
+ },
+ }
+ }
+ };
+ runCommandAndCheckValidatorCount({
+ cmdToRun: () => db.runCommand({"create": collName + collCount, validator: schema}),
+ cmdName: "create",
+ countDict: {failed: NumberLong(1), jsonSchema: NumberLong(1), total: NumberLong(1)},
+ error: true,
+ notFirst: true
+ });
+ collCount++;
+
+ runCommandAndCheckValidatorCount({
+ cmdToRun: () => db.runCommand({collMod: collName, validator: schema}),
+ cmdName: "collMod",
+ countDict: {failed: NumberLong(1), jsonSchema: NumberLong(1), total: NumberLong(1)},
+ error: true,
+ notFirst: true
+ });
+
+ // Test that validator total count increments, but not failed count for valid $jsonSchema.
+ schema = {
+ '$jsonSchema': {
+ 'bsonType': "object",
+ 'properties': {
+ 'a': {
+ 'bsonType': "number",
+ },
+ 'b': {
+ 'bsonType': "number",
+ },
+ 'c': {
+ 'bsonType': "number",
+ },
+ }
+ }
+ };
+
+ runCommandAndCheckValidatorCount({
+ cmdToRun: () => db.createCollection(collName + collCount, {validator: schema}),
+ cmdName: "create",
+ countDict: {failed: NumberLong(0), jsonSchema: NumberLong(1), total: NumberLong(1)},
+ error: false,
+ notFirst: true
+ });
+ collCount++;
+ runCommandAndCheckValidatorCount({
+ cmdToRun: () => db.runCommand({collMod: collName, validator: schema}),
+ cmdName: "collMod",
+ countDict: {failed: NumberLong(0), jsonSchema: NumberLong(1), total: NumberLong(1)},
+ error: false,
+ notFirst: true
+ });
+
+ // Test that only the validator 'total' count gets incremented with match expression validator.
+ // Neither the 'find' nor 'jsonSchema' fields should be incremented.
+ schema = {$expr: {$eq: ["$a", {$multiply: ["$v", {$sum: [1, "$c"]}]}]}};
+ runCommandAndCheckValidatorCount({
+ cmdToRun: () => db.createCollection(collName + collCount, {validator: schema}),
+ cmdName: "create",
+ countDict: {failed: NumberLong(0), jsonSchema: NumberLong(0), total: NumberLong(1)},
+ error: false,
+ notFirst: true
+ });
+ collCount++;
+
+ runCommandAndCheckValidatorCount({
+ cmdToRun: () => db.runCommand({collMod: collName, validator: schema}),
+ cmdName: "collMod",
+ countDict: {failed: NumberLong(0), jsonSchema: NumberLong(0), total: NumberLong(1)},
+ error: false,
+ notFirst: true
+ });
+
+ // Test that validator count does not increment with empty validator object.
+ runCommandAndCheckValidatorCount({
+ cmdToRun: () => db.createCollection(collName + collCount, {validator: {}}),
+ cmdName: "create",
+ countDict: {failed: NumberLong(0), jsonSchema: NumberLong(0), total: NumberLong(0)},
+ error: false,
+ notFirst: true
+ });
+ collCount++;
+ runCommandAndCheckValidatorCount({
+ cmdToRun: () => db.runCommand({collMod: collName, validator: {}}),
+ cmdName: "collMod",
+ countDict: {failed: NumberLong(0), jsonSchema: NumberLong(0), total: NumberLong(0)},
+ error: false,
+ notFirst: true
+ });
+}
+
+// Standalone
+const conn = MongoRunner.runMongod({});
+assert.neq(conn, null, "mongod failed to start");
+let db = conn.getDB(jsTestName());
+let collCount = 0;
+const collName = jsTestName();
+runTests(db, collName, collCount);
+
+MongoRunner.stopMongod(conn);
+
+// Sharded cluster
+const st = new ShardingTest({shards: 2});
+assert.commandWorked(st.s.adminCommand({enableSharding: jsTestName()}));
+st.ensurePrimaryShard(jsTestName(), st.shard0.shardName);
+db = st.rs0.getPrimary().getDB(jsTestName());
+collCount = 0;
+runTests(db, collName, collCount);
+
+st.stop();
+}());
diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp
index cd8bfb6b868..d899d0bf187 100644
--- a/src/mongo/db/catalog/coll_mod.cpp
+++ b/src/mongo/db/catalog/coll_mod.cpp
@@ -33,6 +33,7 @@
#include "mongo/db/catalog/coll_mod.h"
+#include "mongo/db/stats/counters.h"
#include <boost/optional.hpp>
#include <memory>
@@ -473,6 +474,11 @@ StatusWith<std::pair<ParsedCollModRequest, BSONObj>> parseCollModRequest(Operati
validatorObj.getOwned(),
MatchExpressionParser::kDefaultSpecialFeatures,
maxFeatureCompatibilityVersion);
+
+ // Increment counters to track the usage of schema validators.
+ validatorCounters.incrementCounters(
+ cmd.kCommandName, parsed.collValidator->validatorDoc, parsed.collValidator->isOK());
+
if (!parsed.collValidator->isOK()) {
return parsed.collValidator->getStatus();
}
diff --git a/src/mongo/db/catalog/database_impl.cpp b/src/mongo/db/catalog/database_impl.cpp
index 47001b96eee..62f789ad003 100644
--- a/src/mongo/db/catalog/database_impl.cpp
+++ b/src/mongo/db/catalog/database_impl.cpp
@@ -1090,6 +1090,10 @@ Status DatabaseImpl::userCreateNS(OperationContext* opCtx,
ExtensionsCallbackNoop(),
allowedFeatures);
+ // Increment counters to track the usage of schema validators.
+ validatorCounters.incrementCounters(
+ "create", collectionOptions.validator, statusWithMatcher.isOK());
+
// We check the status of the parse to see if there are any banned features, but we don't
// actually need the result for now.
if (!statusWithMatcher.isOK()) {
diff --git a/src/mongo/db/stats/counters.cpp b/src/mongo/db/stats/counters.cpp
index 87f2f217afe..e344141052d 100644
--- a/src/mongo/db/stats/counters.cpp
+++ b/src/mongo/db/stats/counters.cpp
@@ -336,6 +336,7 @@ AuthCounter authCounter;
AggStageCounters aggStageCounters;
DotsAndDollarsFieldsCounters dotsAndDollarsFieldsCounters;
QueryFrameworkCounters queryFrameworkCounters;
+ValidatorCounters validatorCounters;
OperatorCounters operatorCountersAggExpressions{"operatorCounters.expressions."};
OperatorCounters operatorCountersMatchExpressions{"operatorCounters.match."};
diff --git a/src/mongo/db/stats/counters.h b/src/mongo/db/stats/counters.h
index 0353ac4e3f2..e41de1e31b0 100644
--- a/src/mongo/db/stats/counters.h
+++ b/src/mongo/db/stats/counters.h
@@ -444,6 +444,52 @@ private:
StringMap<std::unique_ptr<ExprCounter>> operatorCountersExprMap = {};
};
+class ValidatorCounters {
+public:
+ ValidatorCounters() {
+ _validatorCounterMap["create"] = std::make_unique<ValidatorCounter>("create");
+ _validatorCounterMap["collMod"] = std::make_unique<ValidatorCounter>("collMod");
+ }
+
+ void incrementCounters(const StringData cmdName,
+ const BSONObj& validator,
+ bool parsingSucceeded) {
+ if (!validator.isEmpty()) {
+ auto validatorCounter = _validatorCounterMap.find(cmdName);
+ tassert(7139200,
+ str::stream() << "The validator counters are not support for the command: "
+ << cmdName,
+ validatorCounter != _validatorCounterMap.end());
+ validatorCounter->second->total.increment();
+
+ if (!parsingSucceeded) {
+ validatorCounter->second->failed.increment();
+ }
+ if (validator.hasField("$jsonSchema")) {
+ validatorCounter->second->jsonSchema.increment();
+ }
+ }
+ }
+
+private:
+ struct ValidatorCounter {
+ ValidatorCounter(const StringData name)
+ : totalMetric("commands." + name + ".validator.total", &total),
+ failedMetric("commands." + name + ".validator.failed", &failed),
+ jsonSchemaMetric("commands." + name + ".validator.jsonSchema", &jsonSchema) {}
+ Counter64 total;
+ Counter64 failed;
+ Counter64 jsonSchema;
+ ServerStatusMetricField<Counter64> totalMetric;
+ ServerStatusMetricField<Counter64> failedMetric;
+ ServerStatusMetricField<Counter64> jsonSchemaMetric;
+ };
+
+ StringMap<std::unique_ptr<ValidatorCounter>> _validatorCounterMap = {};
+};
+
+extern ValidatorCounters validatorCounters;
+
// Global counters for expressions inside aggregation pipelines.
extern OperatorCounters operatorCountersAggExpressions;
// Global counters for match expressions.