diff options
author | Gil Alon <gil.alon@mongodb.com> | 2023-02-17 14:08:24 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-02-17 14:56:08 +0000 |
commit | a0adc8d5bad1b20d45802da6a0fc1c28d3b2c15b (patch) | |
tree | ea738583568174a0fcfc76624a4140535d95b831 | |
parent | f4050044d6521d3d4f4b5873addd7a9b234df1a7 (diff) | |
download | mongo-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.js | 175 | ||||
-rw-r--r-- | src/mongo/db/catalog/coll_mod.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/catalog/database_impl.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/stats/counters.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/stats/counters.h | 46 |
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. |