summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGil Alon <gil.alon@mongodb.com>2023-02-22 14:02:55 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-02-22 14:20:24 +0000
commit2edb32d4516e89807d19c94bb2f57762839edbe4 (patch)
tree80802985305206bd6a2239bfb0f8090e4647e3bc
parent64fab8c494aa7287347b155d4ffee1454483c4ff (diff)
downloadmongo-2edb32d4516e89807d19c94bb2f57762839edbe4.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.cpp5
-rw-r--r--src/mongo/db/catalog/database_impl.cpp5
-rw-r--r--src/mongo/db/stats/SConscript1
-rw-r--r--src/mongo/db/stats/counters.cpp2
-rw-r--r--src/mongo/db/stats/counters.h44
6 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..519ca29e331
--- /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}, {expireAfterSeconds: 1800}));
+ 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 6105dee9c60..1ccff89abd1 100644
--- a/src/mongo/db/catalog/coll_mod.cpp
+++ b/src/mongo/db/catalog/coll_mod.cpp
@@ -55,6 +55,7 @@
#include "mongo/db/s/sharding_state.h"
#include "mongo/db/server_options.h"
#include "mongo/db/service_context.h"
+#include "mongo/db/stats/counters.h"
#include "mongo/db/storage/durable_catalog.h"
#include "mongo/db/storage/recovery_unit.h"
#include "mongo/db/views/view_catalog.h"
@@ -246,6 +247,10 @@ StatusWith<CollModRequest> parseCollModRequest(OperationContext* opCtx,
e.Obj().getOwned(),
MatchExpressionParser::kDefaultSpecialFeatures,
maxFeatureCompatibilityVersion);
+ // Increment counters to track the usage of schema validators.
+ validatorCounters.incrementCounters(
+ "collMod", cmr.collValidator->validatorDoc, cmr.collValidator->isOK());
+
if (!cmr.collValidator->isOK()) {
return cmr.collValidator->getStatus();
}
diff --git a/src/mongo/db/catalog/database_impl.cpp b/src/mongo/db/catalog/database_impl.cpp
index bc532f3319d..51ba1de6dfb 100644
--- a/src/mongo/db/catalog/database_impl.cpp
+++ b/src/mongo/db/catalog/database_impl.cpp
@@ -68,6 +68,7 @@
#include "mongo/db/s/operation_sharding_state.h"
#include "mongo/db/server_options.h"
#include "mongo/db/service_context.h"
+#include "mongo/db/stats/counters.h"
#include "mongo/db/stats/top.h"
#include "mongo/db/storage/durable_catalog.h"
#include "mongo/db/storage/recovery_unit.h"
@@ -944,6 +945,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/SConscript b/src/mongo/db/stats/SConscript
index 218fa7d81d8..6dba2071782 100644
--- a/src/mongo/db/stats/SConscript
+++ b/src/mongo/db/stats/SConscript
@@ -34,6 +34,7 @@ env.Library(
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/db/commands/server_status_core',
'$BUILD_DIR/mongo/util/concurrency/spin_lock',
],
)
diff --git a/src/mongo/db/stats/counters.cpp b/src/mongo/db/stats/counters.cpp
index 725741dde85..b934f602c74 100644
--- a/src/mongo/db/stats/counters.cpp
+++ b/src/mongo/db/stats/counters.cpp
@@ -302,4 +302,6 @@ OperatorCountersMatchExpressions operatorCountersMatchExpressions;
Counter64 updateManyCount;
Counter64 deleteManyCount;
+ValidatorCounters validatorCounters;
+
} // namespace mongo
diff --git a/src/mongo/db/stats/counters.h b/src/mongo/db/stats/counters.h
index 2bb6870a7b0..d27763230c1 100644
--- a/src/mongo/db/stats/counters.h
+++ b/src/mongo/db/stats/counters.h
@@ -316,6 +316,50 @@ private:
extern OperatorCountersMatchExpressions operatorCountersMatchExpressions;
+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);
+ invariant(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(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;
+
// Track the number of {multi:true} updates.
extern Counter64 updateManyCount;
// Track the number of deleteMany calls.