summaryrefslogtreecommitdiff
path: root/src/mongo/db
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 /src/mongo/db
parentf950ce6ca8e360becb502a09b7f371b2f2c807da (diff)
downloadmongo-f674117136f9b2699ab42a32f6f08f6c0b5a84b3.tar.gz
SERVER-47577: readConcernCounters metric in serverStatus
Diffstat (limited to 'src/mongo/db')
-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
12 files changed, 149 insertions, 37 deletions
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