diff options
author | Gil Alon <gil.alon@mongodb.com> | 2023-02-22 14:02:55 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-02-22 14:20:24 +0000 |
commit | 2edb32d4516e89807d19c94bb2f57762839edbe4 (patch) | |
tree | 80802985305206bd6a2239bfb0f8090e4647e3bc | |
parent | 64fab8c494aa7287347b155d4ffee1454483c4ff (diff) | |
download | mongo-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.js | 175 | ||||
-rw-r--r-- | src/mongo/db/catalog/coll_mod.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/catalog/database_impl.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/stats/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/stats/counters.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/stats/counters.h | 44 |
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. |