summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLingzhi Deng <lingzhi.deng@mongodb.com>2020-04-28 21:20:45 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-04-29 23:05:43 +0000
commitf674117136f9b2699ab42a32f6f08f6c0b5a84b3 (patch)
tree35bb9b210eafa2aff85568cd245100b3a8d4e245
parentf950ce6ca8e360becb502a09b7f371b2f2c807da (diff)
downloadmongo-f674117136f9b2699ab42a32f6f08f6c0b5a84b3.tar.gz
SERVER-47577: readConcernCounters metric in serverStatus
-rw-r--r--jstests/noPassthrough/server_read_concern_metrics.js515
-rw-r--r--src/mongo/db/commands.h8
-rw-r--r--src/mongo/db/commands/count_cmd.cpp4
-rw-r--r--src/mongo/db/commands/distinct.cpp4
-rw-r--r--src/mongo/db/commands/find_cmd.cpp6
-rw-r--r--src/mongo/db/commands/haystack.cpp4
-rw-r--r--src/mongo/db/commands/map_reduce_command_base.h4
-rw-r--r--src/mongo/db/commands/pipeline_command.cpp4
-rw-r--r--src/mongo/db/repl/read_concern_args.h12
-rw-r--r--src/mongo/db/service_entry_point_common.cpp25
-rw-r--r--src/mongo/db/stats/read_concern_stats.idl37
-rw-r--r--src/mongo/db/stats/server_read_concern_metrics.cpp59
-rw-r--r--src/mongo/db/stats/server_read_concern_metrics.h19
13 files changed, 377 insertions, 324 deletions
diff --git a/jstests/noPassthrough/server_read_concern_metrics.js b/jstests/noPassthrough/server_read_concern_metrics.js
index 0c33d81d879..1314d3fcc82 100644
--- a/jstests/noPassthrough/server_read_concern_metrics.js
+++ b/jstests/noPassthrough/server_read_concern_metrics.js
@@ -5,38 +5,113 @@
// Verifies that the server status response has the fields that we expect.
function verifyServerStatusFields(serverStatusResponse) {
- assert(serverStatusResponse.hasOwnProperty("opReadConcernCounters"),
- "Expected the serverStatus response to have a 'opReadConcernCounters' field\n" +
- tojson(serverStatusResponse));
- assert(
- serverStatusResponse.opReadConcernCounters.hasOwnProperty("available"),
- "The 'opReadConcernCounters' field in serverStatus did not have the 'available' field\n" +
- tojson(serverStatusResponse.opReadConcernCounters));
+ assert(serverStatusResponse.hasOwnProperty("readConcernCounters"),
+ "Missing 'readConcernCounters' from serverStatus\n" + tojson(serverStatusResponse));
+
+ assert(serverStatusResponse.readConcernCounters.hasOwnProperty("nonTransactionOps"),
+ "Missing 'nonTransactionOps' from 'readConcernCounters'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
+ assert(serverStatusResponse.readConcernCounters.nonTransactionOps.hasOwnProperty("none"),
+ "Missing 'none' from 'readConcernCounters.nonTransactionOps'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
+ assert(serverStatusResponse.readConcernCounters.nonTransactionOps.hasOwnProperty("local"),
+ "Missing 'local' from 'readConcernCounters.nonTransactionOps'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
+ assert(serverStatusResponse.readConcernCounters.nonTransactionOps.hasOwnProperty("available"),
+ "Missing 'available' from 'readConcernCounters.nonTransactionOps'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
+ assert(serverStatusResponse.readConcernCounters.nonTransactionOps.hasOwnProperty("majority"),
+ "Missing 'majority' from 'readConcernCounters.nonTransactionOps'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
+ assert(serverStatusResponse.readConcernCounters.nonTransactionOps.hasOwnProperty("snapshot"),
+ "Missing 'snapshot' from 'readConcernCounters.nonTransactionOps'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
+ assert(serverStatusResponse.readConcernCounters.nonTransactionOps.snapshot.hasOwnProperty(
+ "withClusterTime"),
+ "Missing 'withClusterTime' from 'readConcernCounters.nonTransactionOps.snapshot'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
+ assert(serverStatusResponse.readConcernCounters.nonTransactionOps.snapshot.hasOwnProperty(
+ "withoutClusterTime"),
+ "Missing 'withoutClusterTime' from 'readConcernCounters.nonTransactionOps.snapshot'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
assert(
- serverStatusResponse.opReadConcernCounters.hasOwnProperty("linearizable"),
- "The 'opReadConcernCounters' field in serverStatus did not have the 'linearizable' field\n" +
- tojson(serverStatusResponse.opReadConcernCounters));
- assert(serverStatusResponse.opReadConcernCounters.hasOwnProperty("local"),
- "The 'opReadConcernCounters' field in serverStatus did not have the 'local' field\n" +
- tojson(serverStatusResponse.opReadConcernCounters));
- assert(serverStatusResponse.opReadConcernCounters.hasOwnProperty("majority"),
- "The 'opReadConcernCounters' field in serverStatus did not have the 'majority' field\n" +
- tojson(serverStatusResponse.opReadConcernCounters));
- assert(serverStatusResponse.opReadConcernCounters.hasOwnProperty("snapshot"),
- "The 'opReadConcernCounters' field in serverStatus did not have the 'snapshot' field\n" +
- tojson(serverStatusResponse.opReadConcernCounters));
- assert(serverStatusResponse.opReadConcernCounters.hasOwnProperty("none"),
- "The 'opReadConcernCounters' field in serverStatus did not have the 'none' field\n" +
- tojson(serverStatusResponse.opReadConcernCounters));
+ serverStatusResponse.readConcernCounters.nonTransactionOps.hasOwnProperty("linearizable"),
+ "Missing 'linearizable' from 'readConcernCounters.nonTransactionOps'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
+
+ assert(serverStatusResponse.readConcernCounters.hasOwnProperty("transactionOps"),
+ "Missing 'transactionOps' from 'readConcernCounters'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
+ assert(serverStatusResponse.readConcernCounters.transactionOps.hasOwnProperty("none"),
+ "Missing 'none' from 'readConcernCounters.transactionOps'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
+ assert(serverStatusResponse.readConcernCounters.transactionOps.hasOwnProperty("local"),
+ "Missing 'local' from 'readConcernCounters.transactionOps'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
+ assert(serverStatusResponse.readConcernCounters.transactionOps.hasOwnProperty("majority"),
+ "Missing 'majority' from 'readConcernCounters.transactionOps'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
+ assert(serverStatusResponse.readConcernCounters.transactionOps.hasOwnProperty("snapshot"),
+ "Missing 'snapshot' from 'readConcernCounters.transactionOps'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
+ assert(serverStatusResponse.readConcernCounters.transactionOps.snapshot.hasOwnProperty(
+ "withClusterTime"),
+ "Missing 'withClusterTime' from 'readConcernCounters.transactionOps.snapshot'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
+ assert(serverStatusResponse.readConcernCounters.transactionOps.snapshot.hasOwnProperty(
+ "withoutClusterTime"),
+ "Missing 'withoutClusterTime' from 'readConcernCounters.transactionOps.snapshot'\n" +
+ tojson(serverStatusResponse.readConcernCounters));
}
// Verifies that the given value of the server status response is incremented in the way
// we expect.
-function verifyServerStatusChange(initialStats, newStats, valueName, expectedIncrement) {
- assert.eq(initialStats[valueName] + expectedIncrement,
- newStats[valueName],
- "expected " + valueName + " to increase by " + expectedIncrement +
- ", initialStats: " + tojson(initialStats) + ", newStats: " + tojson(newStats));
+function verifyServerStatusChange(initialStatus,
+ newStatus,
+ field,
+ expectedIncrement,
+ {isTransaction = false, atClusterTime = false} = {}) {
+ verifyServerStatusFields(newStatus);
+ let initialCounters = initialStatus.readConcernCounters;
+ let newCounters = newStatus.readConcernCounters;
+ let initialOps, newOps;
+ let fieldPath = "serverStatus.readConcernCounters";
+ if (isTransaction) {
+ initialOps = initialCounters.transactionOps;
+ newOps = newCounters.transactionOps;
+ fieldPath = fieldPath + ".transactionOps";
+ } else {
+ initialOps = initialCounters.nonTransactionOps;
+ newOps = newCounters.nonTransactionOps;
+ fieldPath = fieldPath + ".nonTransactionOps";
+ }
+
+ if (field === "snapshot") {
+ initialOps = initialOps.snapshot;
+ newOps = newOps.snapshot;
+ fieldPath = fieldPath + ".snapshot";
+ if (atClusterTime) {
+ field = "withClusterTime";
+ } else {
+ field = "withoutClusterTime";
+ }
+ }
+
+ fieldPath = fieldPath + "." + field;
+ assert.eq(initialOps[field] + expectedIncrement,
+ newOps[field],
+ "expected " + fieldPath + " to increase by " + expectedIncrement +
+ ", initialStats: " + tojson(initialCounters) +
+ ", newStats: " + tojson(newCounters));
+
+ // Update the value of the field to the new value so we can compare the rest of the fields using
+ // assert.docEq.
+ initialOps[field] = newOps[field];
+
+ assert.docEq(initialCounters,
+ newCounters,
+ "Expected docEq after updating " + fieldPath + ", initialCounters: " +
+ tojson(initialCounters + ", newCounters: " + tojson(newCounters)));
}
const rst = new ReplSetTest({nodes: 1});
@@ -53,7 +128,14 @@ const sessionOptions = {
const session = testDB.getMongo().startSession(sessionOptions);
const sessionDb = session.getDatabase(dbName);
const sessionColl = sessionDb[collName];
-testDB.runCommand({drop: collName, writeConcern: {w: "majority"}});
+
+testDB.runCommand({drop: collName});
+assert.commandWorked(testDB.createCollection(collName));
+assert.commandWorked(testDB.runCommand({
+ createIndexes: collName,
+ indexes: [{key: {haystack: "geoHaystack", a: 1}, name: "haystack_geo", bucketSize: 1}],
+ writeConcern: {w: "majority"}
+}));
assert.commandWorked(testColl.insert({_id: 0}));
// Run an initial transaction to get config.transactions state into memory.
@@ -71,293 +153,152 @@ function getServerStatus(conn) {
let serverStatus = getServerStatus(testDB);
verifyServerStatusFields(serverStatus);
-// Run a find with no readConcern.
-assert.eq(testColl.find().itcount(), 1);
-let newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 1);
-serverStatus = newStatus;
-
-// Run a find with a readConcern with no level.
-assert.commandWorked(
- testDB.runCommand({find: collName, readConcern: {afterClusterTime: Timestamp(1, 1)}}));
-newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 1);
-serverStatus = newStatus;
+let newStatus;
// Run a legacy query.
primary.forceReadMode("legacy");
assert.eq(testColl.find().itcount(), 1);
newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 1);
+verifyServerStatusChange(serverStatus, newStatus, "none", 1);
primary.forceReadMode("commands");
serverStatus = newStatus;
-// Run a find with a readConcern level available.
-assert.eq(testColl.find().readConcern("available").itcount(), 1);
+// Run a command without a readConcern.
+assert.eq(testColl.find().itcount(), 1);
newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 1);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 0);
+verifyServerStatusChange(serverStatus, newStatus, "none", 1);
serverStatus = newStatus;
-// Run a find with a readConcern level linearizable.
-assert.eq(testColl.find().readConcern("linearizable").itcount(), 1);
-newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 1);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 0);
-serverStatus = newStatus;
+// Non-transaction reads.
+for (let level of ["none", "local", "available", "snapshot", "majority", "linearizable"]) {
+ jsTestLog("Testing non-transaction reads with readConcern " + level);
+ let readConcern = {};
+ if (level !== "none") {
+ readConcern = {level: level};
+ }
-// Run a find with a readConcern level local.
-assert.eq(testColl.find().readConcern("local").itcount(), 1);
-newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 1);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 0);
-serverStatus = newStatus;
+ assert.commandWorked(testDB.runCommand({find: collName, readConcern: readConcern}));
+ newStatus = getServerStatus(testDB);
+ verifyServerStatusChange(serverStatus, newStatus, level, 1);
+ serverStatus = newStatus;
-// Run a find with a readConcern level majority.
-assert.eq(testColl.find().readConcern("majority").itcount(), 1);
-newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 1);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 0);
-serverStatus = newStatus;
+ assert.commandWorked(testDB.runCommand(
+ {aggregate: collName, pipeline: [], cursor: {}, readConcern: readConcern}));
+ newStatus = getServerStatus(testDB);
+ verifyServerStatusChange(serverStatus, newStatus, level, 1);
+ serverStatus = newStatus;
-// Run a find in a transaction with readConcern level snapshot.
-session.startTransaction({readConcern: {level: "snapshot"}});
-assert.eq(sessionColl.find().itcount(), 1);
-newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 1);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 0);
-assert.commandWorked(session.abortTransaction_forTesting());
-serverStatus = newStatus;
+ assert.commandWorked(testDB.runCommand({count: collName, readConcern: readConcern}));
+ newStatus = getServerStatus(testDB);
+ verifyServerStatusChange(serverStatus, newStatus, level, 1);
+ serverStatus = newStatus;
-// Run a find in a transaction with no specified readConcern level.
-session.startTransaction();
-assert.eq(sessionColl.find().itcount(), 1);
-newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 1);
-assert.commandWorked(session.abortTransaction_forTesting());
-serverStatus = newStatus;
+ assert.commandWorked(
+ testDB.runCommand({distinct: collName, key: "_id", readConcern: readConcern}));
+ newStatus = getServerStatus(testDB);
+ verifyServerStatusChange(serverStatus, newStatus, level, 1);
+ serverStatus = newStatus;
-// Run a find in a transaction with readConcern level local.
-session.startTransaction({readConcern: {level: "local"}});
-assert.eq(sessionColl.find().itcount(), 1);
-newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 1);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 0);
-assert.commandWorked(session.abortTransaction_forTesting());
-serverStatus = newStatus;
+ assert.commandWorked(testDB.runCommand({
+ geoSearch: collName,
+ near: [0, 0],
+ maxDistance: 1,
+ search: {a: 1},
+ readConcern: readConcern
+ }));
+ newStatus = getServerStatus(testDB);
+ verifyServerStatusChange(serverStatus, newStatus, level, 1);
+ serverStatus = newStatus;
-// Run a find in a transaction with readConcern level majority.
-session.startTransaction({readConcern: {level: "majority"}});
-assert.eq(sessionColl.find().itcount(), 1);
-newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 1);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 0);
-serverStatus = newStatus;
+ if (level in ["none", "local", "available"]) {
+ // mapReduce only support local and available.
+ assert.commandWorked(testDB.runCommand({
+ mapReduce: collName,
+ map: function() {
+ emit(this.a, this.a);
+ },
+ reduce: function(key, vals) {
+ return 1;
+ },
+ out: {inline: 1},
+ readConcern: readConcern
+ }));
+ newStatus = getServerStatus(testDB);
+ verifyServerStatusChange(serverStatus, newStatus, level, 1);
+ serverStatus = newStatus;
+ }
-// Run a second find in the same transaction. It will inherit the readConcern from the
-// transaction.
-assert.eq(sessionColl.find().itcount(), 1);
-newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 1);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 0);
-assert.commandWorked(session.abortTransaction_forTesting());
-serverStatus = newStatus;
+ // getMore does not count toward readConcern metrics. getMore inherits the readConcern of the
+ // originating command.
+ let res = assert.commandWorked(
+ testDB.runCommand({find: collName, batchSize: 0, readConcern: readConcern}));
+ serverStatus = getServerStatus(testDB);
+ assert.commandWorked(testDB.runCommand({getMore: res.cursor.id, collection: collName}));
+ newStatus = getServerStatus(testDB);
+ verifyServerStatusChange(serverStatus, newStatus, level, 0);
+}
-// Aggregation does not count toward readConcern metrics. Aggregation is counted as a 'command'
-// in the 'opCounters' serverStatus section, and we only track the readConcern of queries
-// tracked in 'opCounters.query'.
-assert.eq(testColl.aggregate([]).itcount(), 1);
+// Test non-transaction snapshot with atClusterTime.
+let insertTimestamp =
+ assert.commandWorked(testDB.runCommand({insert: "atClusterTime", documents: [{_id: 0}]}))
+ .operationTime;
+jsTestLog("Testing non-transaction reads with atClusterTime");
+serverStatus = getServerStatus(testDB);
+assert.commandWorked(testDB.runCommand(
+ {find: "atClusterTime", readConcern: {level: "snapshot", atClusterTime: insertTimestamp}}));
newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 0);
+verifyServerStatusChange(serverStatus, newStatus, "snapshot", 1, {atClusterTime: true});
serverStatus = newStatus;
-// The count command does not count toward readConcern metrics. The count command is counted as
-// a 'command' in the 'opCounters' serverStatus section, and we only track the readConcern of
-// queries tracked in 'opCounters.query'.
-assert.eq(testColl.count({_id: 0}), 1);
+// Transaction reads.
+for (let level of ["none", "local", "snapshot", "majority"]) {
+ jsTestLog("Testing transaction reads with readConcern " + level);
+ if (level === "none") {
+ session.startTransaction();
+ } else {
+ session.startTransaction({readConcern: {level: level}});
+ }
+ assert.eq(sessionColl.find().itcount(), 1);
+ newStatus = getServerStatus(testDB);
+ verifyServerStatusChange(serverStatus, newStatus, level, 1, {isTransaction: true});
+ serverStatus = newStatus;
+
+ // Run a second find in the same transaction. It will inherit the readConcern from the
+ // transaction.
+ assert.eq(sessionColl.find().itcount(), 1);
+ newStatus = getServerStatus(testDB);
+ verifyServerStatusChange(serverStatus, newStatus, level, 1, {isTransaction: true});
+ serverStatus = newStatus;
+
+ // Run an insert in the same transaction. This should not count toward the readConcern metrics.
+ assert.commandWorked(
+ sessionDb.runCommand({insert: "transaction", documents: [{level: level}]}));
+ newStatus = getServerStatus(testDB);
+ verifyServerStatusChange(serverStatus, newStatus, level, 0, {isTransaction: true});
+ assert.commandWorked(session.abortTransaction_forTesting());
+ serverStatus = newStatus;
+}
+
+// Test transaction snapshot with atClusterTime.
+insertTimestamp =
+ assert.commandWorked(testDB.runCommand({insert: "atClusterTime", documents: [{_id: 1}]}))
+ .operationTime;
+jsTestLog("Testing transaction reads with atClusterTime");
+session.startTransaction({readConcern: {level: "snapshot", atClusterTime: insertTimestamp}});
+serverStatus = getServerStatus(testDB);
+
+assert.eq(sessionColl.find().itcount(), 1);
newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 0);
verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 0);
+ serverStatus, newStatus, "snapshot", 1, {isTransaction: true, atClusterTime: true});
serverStatus = newStatus;
-// getMore does not count toward readConcern metrics. getMore inherits the readConcern of the
-// originating command. It is not counted in 'opCounters.query'.
-let res = assert.commandWorked(testDB.runCommand({find: collName, batchSize: 0}));
-serverStatus = getServerStatus(testDB);
-assert.commandWorked(testDB.runCommand({getMore: res.cursor.id, collection: collName}));
+// Perform another read in the same transaction.
+assert.eq(sessionColl.find().itcount(), 1);
newStatus = getServerStatus(testDB);
-verifyServerStatusFields(newStatus);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "available", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "linearizable", 0);
verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "local", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "majority", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "snapshot", 0);
-verifyServerStatusChange(
- serverStatus.opReadConcernCounters, newStatus.opReadConcernCounters, "none", 0);
+ serverStatus, newStatus, "snapshot", 1, {isTransaction: true, atClusterTime: true});
+assert.commandWorked(session.abortTransaction_forTesting());
session.endSession();
rst.stopSet();
diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h
index 37f85f0bd05..609371c6071 100644
--- a/src/mongo/db/commands.h
+++ b/src/mongo/db/commands.h
@@ -382,6 +382,14 @@ public:
}
/**
+ * Override and return true if the readConcernCounters in serverStatus should not be incremented
+ * on behalf of this command.
+ */
+ virtual bool shouldAffectReadConcernCounter() const {
+ return false;
+ }
+
+ /**
* Return true if the command requires auth.
*/
virtual bool requiresAuth() const {
diff --git a/src/mongo/db/commands/count_cmd.cpp b/src/mongo/db/commands/count_cmd.cpp
index 3063da6955b..aab21671ba9 100644
--- a/src/mongo/db/commands/count_cmd.cpp
+++ b/src/mongo/db/commands/count_cmd.cpp
@@ -94,6 +94,10 @@ public:
return ReadConcernSupportResult::allSupportedAndDefaultPermitted();
}
+ bool shouldAffectReadConcernCounter() const override {
+ return true;
+ }
+
bool supportsReadMirroring(const BSONObj&) const override {
return true;
}
diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp
index 0d3c2318bff..964edb9dcb6 100644
--- a/src/mongo/db/commands/distinct.cpp
+++ b/src/mongo/db/commands/distinct.cpp
@@ -93,6 +93,10 @@ public:
return ReadConcernSupportResult::allSupportedAndDefaultPermitted();
}
+ bool shouldAffectReadConcernCounter() const override {
+ return true;
+ }
+
bool supportsReadMirroring(const BSONObj&) const override {
return true;
}
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index d83db1ac6e2..de42810ba9b 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -169,6 +169,10 @@ public:
return false;
}
+ bool shouldAffectReadConcernCounter() const override {
+ return true;
+ }
+
class Invocation final : public CommandInvocation {
public:
Invocation(const FindCmd* definition, const OpMsgRequest& request, StringData dbName)
@@ -301,8 +305,6 @@ public:
CommandHelpers::handleMarkKillOnClientDisconnect(opCtx);
// Although it is a command, a find command gets counted as a query.
globalOpCounters.gotQuery();
- ServerReadConcernMetrics::get(opCtx)->recordReadConcern(
- repl::ReadConcernArgs::get(opCtx));
// Parse the command BSON to a QueryRequest. Pass in the parsedNss in case _request.body
// does not have a UUID.
diff --git a/src/mongo/db/commands/haystack.cpp b/src/mongo/db/commands/haystack.cpp
index 93d3df3ff65..2346e048772 100644
--- a/src/mongo/db/commands/haystack.cpp
+++ b/src/mongo/db/commands/haystack.cpp
@@ -85,6 +85,10 @@ public:
return ReadConcernSupportResult::allSupportedAndDefaultPermitted();
}
+ bool shouldAffectReadConcernCounter() const override {
+ return true;
+ }
+
ReadWriteType getReadWriteType() const {
return ReadWriteType::kRead;
}
diff --git a/src/mongo/db/commands/map_reduce_command_base.h b/src/mongo/db/commands/map_reduce_command_base.h
index a1c45276634..bcc0870dfe5 100644
--- a/src/mongo/db/commands/map_reduce_command_base.h
+++ b/src/mongo/db/commands/map_reduce_command_base.h
@@ -62,6 +62,10 @@ public:
{kDefaultReadConcernNotPermitted}};
}
+ bool shouldAffectReadConcernCounter() const override {
+ return true;
+ }
+
virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
return map_reduce_common::mrSupportsWriteConcern(cmd);
}
diff --git a/src/mongo/db/commands/pipeline_command.cpp b/src/mongo/db/commands/pipeline_command.cpp
index ebc251e65ea..915457281fe 100644
--- a/src/mongo/db/commands/pipeline_command.cpp
+++ b/src/mongo/db/commands/pipeline_command.cpp
@@ -78,6 +78,10 @@ public:
this, opMsgRequest, std::move(aggregationRequest), std::move(privileges));
}
+ bool shouldAffectReadConcernCounter() const override {
+ return true;
+ }
+
class Invocation final : public CommandInvocation {
public:
Invocation(Command* cmd,
diff --git a/src/mongo/db/repl/read_concern_args.h b/src/mongo/db/repl/read_concern_args.h
index 04a2c3e131a..bdc3c5bf4eb 100644
--- a/src/mongo/db/repl/read_concern_args.h
+++ b/src/mongo/db/repl/read_concern_args.h
@@ -185,8 +185,18 @@ public:
void setArgsAtClusterTimeForSnapshot(Timestamp ts) {
invariant(_level && _level == ReadConcernLevel::kSnapshotReadConcern);
invariant(!_atClusterTime);
+ invariant(!_atClusterTimeSelected);
_afterClusterTime = boost::none;
_atClusterTime = LogicalTime(ts);
+ _atClusterTimeSelected = true;
+ }
+
+ /**
+ * Return whether an atClusterTime has been selected by the server for a snapshot read. This
+ * function returns false if the atClusterTime was specified by the client.
+ */
+ bool wasAtClusterTimeSelected() const {
+ return _atClusterTimeSelected;
}
private:
@@ -223,6 +233,8 @@ private:
bool _specified;
ReadWriteConcernProvenance _provenance;
+
+ bool _atClusterTimeSelected = false;
};
} // namespace repl
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index 55959347ec9..1e9f51a137a 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -615,13 +615,21 @@ void invokeWithSessionCheckedOut(OperationContext* opCtx,
if (!opCtx->getClient()->isInDirectClient()) {
const auto& readConcernArgs = repl::ReadConcernArgs::get(opCtx);
+ auto command = invocation->definition();
+ // Record readConcern usages for commands run inside transactions after unstashing the
+ // transaction resources.
+ if (command->shouldAffectReadConcernCounter() && opCtx->inMultiDocumentTransaction()) {
+ ServerReadConcernMetrics::get(opCtx)->recordReadConcern(readConcernArgs,
+ true /* isTransaction */);
+ }
+
// For replica sets, we do not receive the readConcernArgs of our parent transaction
// statements until we unstash the transaction resources. The below check is necessary to
// ensure commands, including those occurring after the first statement in their respective
// transactions, are checked for readConcern support. Presently, only `create` and
// `createIndexes` do not support readConcern inside transactions.
// TODO(SERVER-46971): Consider how to extend this check to other commands.
- auto cmdName = invocation->definition()->getName();
+ auto cmdName = command->getName();
auto readConcernSupport = invocation->supportsReadConcern(readConcernArgs.getLevel());
if (readConcernArgs.hasLevel() &&
(cmdName == "create"_sd || cmdName == "createIndexes"_sd)) {
@@ -714,6 +722,15 @@ bool runCommandImpl(OperationContext* opCtx,
const bool shouldWaitForWriteConcern =
invocation->supportsWriteConcern() || command->getLogicalOp() == LogicalOp::opGetMore;
+ // Record readConcern usages for commands run outside of transactions, excluding DBDirectClient.
+ // For commands inside a transaction, they inherit the readConcern from the transaction. So we
+ // will record their readConcern usages after we have unstashed the transaction resources.
+ if (!opCtx->getClient()->isInDirectClient() && command->shouldAffectReadConcernCounter() &&
+ !opCtx->inMultiDocumentTransaction()) {
+ ServerReadConcernMetrics::get(opCtx)->recordReadConcern(repl::ReadConcernArgs::get(opCtx),
+ false /* isTransaction */);
+ }
+
if (shouldWaitForWriteConcern) {
auto lastOpBeforeRun = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp();
@@ -1436,7 +1453,11 @@ DbResponse receivedQuery(OperationContext* opCtx,
const ServiceEntryPointCommon::Hooks& behaviors) {
invariant(!nss.isCommand());
globalOpCounters.gotQuery();
- ServerReadConcernMetrics::get(opCtx)->recordReadConcern(repl::ReadConcernArgs::get(opCtx));
+
+ if (!opCtx->getClient()->isInDirectClient()) {
+ ServerReadConcernMetrics::get(opCtx)->recordReadConcern(repl::ReadConcernArgs::get(opCtx),
+ false /* isTransaction */);
+ }
DbMessage d(m);
QueryMessage q(d);
diff --git a/src/mongo/db/stats/read_concern_stats.idl b/src/mongo/db/stats/read_concern_stats.idl
index 60636cf0c08..30ec3c47a05 100644
--- a/src/mongo/db/stats/read_concern_stats.idl
+++ b/src/mongo/db/stats/read_concern_stats.idl
@@ -38,26 +38,45 @@ imports:
structs:
- ReadConcernStats:
- description: "A struct representing the section of the server status
- command with information about readConcern levels used by operations"
+ SnapshotOps:
+ description: "A struct representing the number of operations used with snapshot readConcern"
strict: true
fields:
- available:
+ withClusterTime:
type: long
default: 0
- linearizable:
+ withoutClusterTime:
+ type: long
+ default: 0
+
+ ReadConcernOps:
+ description: "A struct representing readConcern level usages by read operations"
+ strict: true
+ fields:
+ none:
type: long
default: 0
local:
type: long
default: 0
+ available:
+ type: long
+ optional: true
majority:
type: long
default: 0
snapshot:
+ type: SnapshotOps
+ linearizable:
type: long
- default: 0
- none:
- type: long
- default: 0
+ optional: true
+
+ ReadConcernStats:
+ description: "A struct representing the section of the server status
+ command with information about readConcern levels used by operations"
+ strict: true
+ fields:
+ nonTransactionOps:
+ type: ReadConcernOps
+ transactionOps:
+ type: ReadConcernOps
diff --git a/src/mongo/db/stats/server_read_concern_metrics.cpp b/src/mongo/db/stats/server_read_concern_metrics.cpp
index 1dd4c9fff8e..a29b7ab781a 100644
--- a/src/mongo/db/stats/server_read_concern_metrics.cpp
+++ b/src/mongo/db/stats/server_read_concern_metrics.cpp
@@ -50,31 +50,41 @@ ServerReadConcernMetrics* ServerReadConcernMetrics::get(OperationContext* opCtx)
return get(opCtx->getServiceContext());
}
-void ServerReadConcernMetrics::recordReadConcern(const repl::ReadConcernArgs& readConcernArgs) {
+void ServerReadConcernMetrics::recordReadConcern(const repl::ReadConcernArgs& readConcernArgs,
+ bool isTransaction) {
+ auto& ops = isTransaction ? _transactionOps : _nonTransactionOps;
+
if (!readConcernArgs.hasLevel()) {
- _noLevelCount.fetchAndAdd(1);
+ ops.noLevelCount.fetchAndAdd(1);
return;
}
switch (readConcernArgs.getLevel()) {
case repl::ReadConcernLevel::kAvailableReadConcern:
- _levelAvailableCount.fetchAndAdd(1);
+ invariant(!isTransaction);
+ ops.levelAvailableCount.fetchAndAdd(1);
break;
case repl::ReadConcernLevel::kLinearizableReadConcern:
- _levelLinearizableCount.fetchAndAdd(1);
+ invariant(!isTransaction);
+ ops.levelLinearizableCount.fetchAndAdd(1);
break;
case repl::ReadConcernLevel::kLocalReadConcern:
- _levelLocalCount.fetchAndAdd(1);
+ ops.levelLocalCount.fetchAndAdd(1);
break;
case repl::ReadConcernLevel::kMajorityReadConcern:
- _levelMajorityCount.fetchAndAdd(1);
+ ops.levelMajorityCount.fetchAndAdd(1);
break;
case repl::ReadConcernLevel::kSnapshotReadConcern:
- _levelSnapshotCount.fetchAndAdd(1);
+ if (readConcernArgs.getArgsAtClusterTime() &&
+ !readConcernArgs.wasAtClusterTimeSelected()) {
+ ops.atClusterTimeCount.fetchAndAdd(1);
+ } else {
+ ops.levelSnapshotCount.fetchAndAdd(1);
+ }
break;
default:
@@ -83,20 +93,35 @@ void ServerReadConcernMetrics::recordReadConcern(const repl::ReadConcernArgs& re
}
void ServerReadConcernMetrics::updateStats(ReadConcernStats* stats, OperationContext* opCtx) {
- stats->setAvailable(_levelAvailableCount.load());
- stats->setLinearizable(_levelLinearizableCount.load());
- stats->setLocal(_levelLocalCount.load());
- stats->setMajority(_levelMajorityCount.load());
- stats->setSnapshot(_levelSnapshotCount.load());
- stats->setNone(_noLevelCount.load());
+ ReadConcernOps nonTransactionOps;
+ SnapshotOps nonTransactionSnapshotOps;
+ nonTransactionSnapshotOps.setWithoutClusterTime(_nonTransactionOps.levelSnapshotCount.load());
+ nonTransactionSnapshotOps.setWithClusterTime(_nonTransactionOps.atClusterTimeCount.load());
+ nonTransactionOps.setNone(_nonTransactionOps.noLevelCount.load());
+ nonTransactionOps.setAvailable(_nonTransactionOps.levelAvailableCount.load());
+ nonTransactionOps.setLinearizable(_nonTransactionOps.levelLinearizableCount.load());
+ nonTransactionOps.setLocal(_nonTransactionOps.levelLocalCount.load());
+ nonTransactionOps.setMajority(_nonTransactionOps.levelMajorityCount.load());
+ nonTransactionOps.setSnapshot(nonTransactionSnapshotOps);
+ stats->setNonTransactionOps(nonTransactionOps);
+
+ ReadConcernOps transactionOps;
+ SnapshotOps transactionSnapshotOps;
+ transactionSnapshotOps.setWithoutClusterTime(_transactionOps.levelSnapshotCount.load());
+ transactionSnapshotOps.setWithClusterTime(_transactionOps.atClusterTimeCount.load());
+ transactionOps.setNone(_transactionOps.noLevelCount.load());
+ transactionOps.setLocal(_transactionOps.levelLocalCount.load());
+ transactionOps.setMajority(_transactionOps.levelMajorityCount.load());
+ transactionOps.setSnapshot(transactionSnapshotOps);
+ stats->setTransactionOps(transactionOps);
}
namespace {
-class OpReadConcernCountersSSS : public ServerStatusSection {
+class ReadConcernCountersSSS : public ServerStatusSection {
public:
- OpReadConcernCountersSSS() : ServerStatusSection("opReadConcernCounters") {}
+ ReadConcernCountersSSS() : ServerStatusSection("readConcernCounters") {}
- ~OpReadConcernCountersSSS() override = default;
+ ~ReadConcernCountersSSS() override = default;
bool includeByDefault() const override {
return true;
@@ -109,7 +134,7 @@ public:
return stats.toBSON();
}
-} opReadConcernCountersSSS;
+} ReadConcernCountersSSS;
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/stats/server_read_concern_metrics.h b/src/mongo/db/stats/server_read_concern_metrics.h
index 1a247f79df6..7782b959bfd 100644
--- a/src/mongo/db/stats/server_read_concern_metrics.h
+++ b/src/mongo/db/stats/server_read_concern_metrics.h
@@ -52,7 +52,7 @@ public:
/**
* Updates counter for the level of 'readConcernArgs'.
*/
- void recordReadConcern(const repl::ReadConcernArgs& readConcernArgs);
+ void recordReadConcern(const repl::ReadConcernArgs& readConcernArgs, bool isTransaction);
/**
* Appends the accumulated stats to a readConcern stats object.
@@ -60,12 +60,17 @@ public:
void updateStats(ReadConcernStats* stats, OperationContext* opCtx);
private:
- AtomicWord<unsigned long long> _levelAvailableCount{0};
- AtomicWord<unsigned long long> _levelLinearizableCount{0};
- AtomicWord<unsigned long long> _levelLocalCount{0};
- AtomicWord<unsigned long long> _levelMajorityCount{0};
- AtomicWord<unsigned long long> _levelSnapshotCount{0};
- AtomicWord<unsigned long long> _noLevelCount{0};
+ struct readConcernCounters {
+ AtomicWord<unsigned long long> levelAvailableCount{0};
+ AtomicWord<unsigned long long> levelLinearizableCount{0};
+ AtomicWord<unsigned long long> levelLocalCount{0};
+ AtomicWord<unsigned long long> levelMajorityCount{0};
+ AtomicWord<unsigned long long> levelSnapshotCount{0};
+ AtomicWord<unsigned long long> atClusterTimeCount{0};
+ AtomicWord<unsigned long long> noLevelCount{0};
+ };
+ readConcernCounters _nonTransactionOps;
+ readConcernCounters _transactionOps;
};
} // namespace mongo