summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Pulo <kevin.pulo@mongodb.com>2019-10-21 11:59:09 +0000
committerevergreen <evergreen@mongodb.com>2019-10-21 11:59:09 +0000
commitc7e3ba1d4f415fb94eace24e1f94e5d4eb60456f (patch)
tree96638b25c046aaafe094bcfc8d666d21ae2a3332
parent42c40db7ef341b3dbf2b975e61d87ce8000042a9 (diff)
downloadmongo-c7e3ba1d4f415fb94eace24e1f94e5d4eb60456f.tar.gz
SERVER-43123 SERVER-43124 Use ReadWriteConcernDefaults for incoming mongod requests
-rw-r--r--src/mongo/db/commands.cpp3
-rw-r--r--src/mongo/db/commands.h58
-rw-r--r--src/mongo/db/commands/count_cmd.cpp9
-rw-r--r--src/mongo/db/commands/distinct.cpp9
-rw-r--r--src/mongo/db/commands/find_cmd.cpp5
-rw-r--r--src/mongo/db/commands/haystack.cpp9
-rw-r--r--src/mongo/db/commands/pipeline_command.cpp10
-rw-r--r--src/mongo/db/read_write_concern_defaults.cpp24
-rw-r--r--src/mongo/db/read_write_concern_defaults.h9
-rw-r--r--src/mongo/db/repl/read_concern_args.cpp116
-rw-r--r--src/mongo/db/repl/read_concern_args.h19
-rw-r--r--src/mongo/db/repl/read_concern_level.h21
-rw-r--r--src/mongo/db/service_entry_point_common.cpp58
-rw-r--r--src/mongo/db/write_concern.cpp22
-rw-r--r--src/mongo/s/commands/cluster_distinct_cmd.cpp9
-rw-r--r--src/mongo/s/commands/cluster_find_and_modify_cmd.cpp9
-rw-r--r--src/mongo/s/commands/cluster_find_cmd.cpp5
-rw-r--r--src/mongo/s/commands/cluster_killcursors_cmd.cpp9
-rw-r--r--src/mongo/s/commands/cluster_pipeline_cmd.cpp5
-rw-r--r--src/mongo/s/commands/cluster_write_cmd.cpp5
20 files changed, 298 insertions, 116 deletions
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index e78d3c19773..fd6c28d145c 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -52,6 +52,7 @@
#include "mongo/db/curop.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
+#include "mongo/db/read_write_concern_defaults.h"
#include "mongo/rpc/factory.h"
#include "mongo/rpc/metadata/client_metadata_ismaster.h"
#include "mongo/rpc/op_msg_rpc_impls.h"
@@ -628,7 +629,7 @@ private:
return _command->supportsWriteConcern(cmdObj());
}
- bool supportsReadConcern(repl::ReadConcernLevel level) const override {
+ ReadConcernSupportResult supportsReadConcern(repl::ReadConcernLevel level) const override {
return _command->supportsReadConcern(_dbName, cmdObj(), level);
}
diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h
index 6002af5e03e..6f174a40dfa 100644
--- a/src/mongo/db/commands.h
+++ b/src/mongo/db/commands.h
@@ -432,6 +432,46 @@ private:
};
/**
+ * The result of checking an invocation's support for readConcern. There are two parts:
+ * - Whether or not the invocation supports the given readConcern.
+ * - Whether or not the invocation permits having the default readConcern applied to it.
+ */
+struct ReadConcernSupportResult {
+ /**
+ * Whether this command invocation supports the requested readConcern level. This only
+ * applies when running outside transactions because all commands that are allowed to run
+ * in a transaction must support all the read concerns that can be used in a transaction.
+ */
+ enum class ReadConcern { kSupported, kNotSupported } readConcern;
+
+ /**
+ * Whether this command invocation supports applying the default readConcern to it.
+ */
+ enum class DefaultReadConcern { kPermitted, kNotPermitted } defaultReadConcern;
+
+ /**
+ * Construct with either the enum value or a bool, where true indicates
+ * ReadConcern::kSupported or DefaultReadConcern::kPermitted (as appropriate).
+ */
+ ReadConcernSupportResult(ReadConcern supported, DefaultReadConcern defaultPermitted)
+ : readConcern(supported), defaultReadConcern(defaultPermitted) {}
+
+ ReadConcernSupportResult(bool supported, DefaultReadConcern defaultPermitted)
+ : readConcern(supported ? ReadConcern::kSupported : ReadConcern::kNotSupported),
+ defaultReadConcern(defaultPermitted) {}
+
+ ReadConcernSupportResult(ReadConcern supported, bool defaultPermitted)
+ : readConcern(supported),
+ defaultReadConcern(defaultPermitted ? DefaultReadConcern::kPermitted
+ : DefaultReadConcern::kNotPermitted) {}
+
+ ReadConcernSupportResult(bool supported, bool defaultPermitted)
+ : readConcern(supported ? ReadConcern::kSupported : ReadConcern::kNotSupported),
+ defaultReadConcern(defaultPermitted ? DefaultReadConcern::kPermitted
+ : DefaultReadConcern::kNotPermitted) {}
+};
+
+/**
* Represents a single invocation of a given command.
*/
class CommandInvocation {
@@ -472,12 +512,11 @@ public:
virtual bool supportsWriteConcern() const = 0;
/**
- * Returns true if this command invocation supports the given readConcern level. This only
- * applies when running outside transactions because all commands that are allowed to run in a
- * transaction must support all the read concerns that can be used in a transaction.
+ * Returns this invocation's support for readConcern.
*/
- virtual bool supportsReadConcern(repl::ReadConcernLevel level) const {
- return level == repl::ReadConcernLevel::kLocalReadConcern;
+ virtual ReadConcernSupportResult supportsReadConcern(repl::ReadConcernLevel level) const {
+ return {level == repl::ReadConcernLevel::kLocalReadConcern,
+ ReadConcernSupportResult::DefaultReadConcern::kNotPermitted};
}
/**
@@ -624,10 +663,11 @@ public:
* the option to the shards as needed. We rely on the shards to fail the commands in the
* cases where it isn't supported.
*/
- virtual bool supportsReadConcern(const std::string& dbName,
- const BSONObj& cmdObj,
- repl::ReadConcernLevel level) const {
- return level == repl::ReadConcernLevel::kLocalReadConcern;
+ virtual ReadConcernSupportResult supportsReadConcern(const std::string& dbName,
+ const BSONObj& cmdObj,
+ repl::ReadConcernLevel level) const {
+ return {level == repl::ReadConcernLevel::kLocalReadConcern,
+ ReadConcernSupportResult::DefaultReadConcern::kNotPermitted};
}
virtual bool allowsAfterClusterTime(const BSONObj& cmdObj) const {
diff --git a/src/mongo/db/commands/count_cmd.cpp b/src/mongo/db/commands/count_cmd.cpp
index da7b522855b..da2033e7645 100644
--- a/src/mongo/db/commands/count_cmd.cpp
+++ b/src/mongo/db/commands/count_cmd.cpp
@@ -90,10 +90,11 @@ public:
return false;
}
- bool supportsReadConcern(const std::string& dbName,
- const BSONObj& cmdObj,
- repl::ReadConcernLevel level) const override {
- return true;
+ ReadConcernSupportResult supportsReadConcern(const std::string& dbName,
+ const BSONObj& cmdObj,
+ repl::ReadConcernLevel level) const override {
+ return {ReadConcernSupportResult::ReadConcern::kSupported,
+ ReadConcernSupportResult::DefaultReadConcern::kPermitted};
}
ReadWriteType getReadWriteType() const override {
diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp
index 645839bb0f0..ff93a8fcfae 100644
--- a/src/mongo/db/commands/distinct.cpp
+++ b/src/mongo/db/commands/distinct.cpp
@@ -84,10 +84,11 @@ public:
return true;
}
- bool supportsReadConcern(const std::string& dbName,
- const BSONObj& cmdObj,
- repl::ReadConcernLevel level) const override {
- return true;
+ ReadConcernSupportResult supportsReadConcern(const std::string& dbName,
+ const BSONObj& cmdObj,
+ repl::ReadConcernLevel level) const override {
+ return {ReadConcernSupportResult::ReadConcern::kSupported,
+ ReadConcernSupportResult::DefaultReadConcern::kPermitted};
}
ReadWriteType getReadWriteType() const override {
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index 9da9269284f..5b91aa22420 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -176,8 +176,9 @@ public:
return false;
}
- bool supportsReadConcern(repl::ReadConcernLevel level) const final {
- return true;
+ ReadConcernSupportResult supportsReadConcern(repl::ReadConcernLevel level) const final {
+ return {ReadConcernSupportResult::ReadConcern::kSupported,
+ ReadConcernSupportResult::DefaultReadConcern::kPermitted};
}
bool canIgnorePrepareConflicts() const override {
diff --git a/src/mongo/db/commands/haystack.cpp b/src/mongo/db/commands/haystack.cpp
index 79e6907c28c..a14a4fd3a29 100644
--- a/src/mongo/db/commands/haystack.cpp
+++ b/src/mongo/db/commands/haystack.cpp
@@ -73,10 +73,11 @@ public:
return AllowedOnSecondary::kAlways;
}
- bool supportsReadConcern(const std::string& dbName,
- const BSONObj& cmdObj,
- repl::ReadConcernLevel level) const final {
- return true;
+ ReadConcernSupportResult supportsReadConcern(const std::string& dbName,
+ const BSONObj& cmdObj,
+ repl::ReadConcernLevel level) const final {
+ return {ReadConcernSupportResult::ReadConcern::kSupported,
+ ReadConcernSupportResult::DefaultReadConcern::kPermitted};
}
ReadWriteType getReadWriteType() const {
diff --git a/src/mongo/db/commands/pipeline_command.cpp b/src/mongo/db/commands/pipeline_command.cpp
index 4550b138626..85c220d091a 100644
--- a/src/mongo/db/commands/pipeline_command.cpp
+++ b/src/mongo/db/commands/pipeline_command.cpp
@@ -82,15 +82,17 @@ public:
return true;
}
- bool supportsReadConcern(repl::ReadConcernLevel level) const override {
+ ReadConcernSupportResult supportsReadConcern(repl::ReadConcernLevel level) const override {
// Aggregations that are run directly against a collection allow any read concern.
// Otherwise, if the aggregate is collectionless then the read concern must be 'local'
// (e.g. $currentOp). The exception to this is a $changeStream on a whole database,
// which is considered collectionless but must be read concern 'majority'. Further read
// concern validation is done one the pipeline is parsed.
- return level == repl::ReadConcernLevel::kLocalReadConcern ||
- level == repl::ReadConcernLevel::kMajorityReadConcern ||
- !AggregationRequest::parseNs(_dbName, _request.body).isCollectionlessAggregateNS();
+ return {level == repl::ReadConcernLevel::kLocalReadConcern ||
+ level == repl::ReadConcernLevel::kMajorityReadConcern ||
+ !AggregationRequest::parseNs(_dbName, _request.body)
+ .isCollectionlessAggregateNS(),
+ ReadConcernSupportResult::DefaultReadConcern::kPermitted};
}
bool allowsSpeculativeMajorityReads() const override {
diff --git a/src/mongo/db/read_write_concern_defaults.cpp b/src/mongo/db/read_write_concern_defaults.cpp
index 828160e2e41..267ee413045 100644
--- a/src/mongo/db/read_write_concern_defaults.cpp
+++ b/src/mongo/db/read_write_concern_defaults.cpp
@@ -35,15 +35,27 @@
namespace mongo {
+namespace {
+
+static constexpr auto kReadConcernLevelsDisallowedAsDefault = {
+ repl::ReadConcernLevel::kSnapshotReadConcern, repl::ReadConcernLevel::kLinearizableReadConcern};
+
+}
+
+bool ReadWriteConcernDefaults::isSuitableReadConcernLevel(repl::ReadConcernLevel level) {
+ for (auto bannedLevel : kReadConcernLevelsDisallowedAsDefault) {
+ if (level == bannedLevel) {
+ return false;
+ }
+ }
+ return true;
+}
+
void ReadWriteConcernDefaults::checkSuitabilityAsDefault(const ReadConcern& rc) {
uassert(ErrorCodes::BadValue,
- str::stream() << "level: '" << ReadConcern::kSnapshotReadConcernStr
- << "' is not suitable for the default read concern",
- rc.getLevel() != repl::ReadConcernLevel::kSnapshotReadConcern);
- uassert(ErrorCodes::BadValue,
- str::stream() << "level: '" << ReadConcern::kLinearizableReadConcernStr
+ str::stream() << "level: '" << repl::readConcernLevels::toString(rc.getLevel())
<< "' is not suitable for the default read concern",
- rc.getLevel() != repl::ReadConcernLevel::kLinearizableReadConcern);
+ isSuitableReadConcernLevel(rc.getLevel()));
uassert(ErrorCodes::BadValue,
str::stream() << "'" << ReadConcern::kAfterOpTimeFieldName
<< "' is not suitable for the default read concern",
diff --git a/src/mongo/db/read_write_concern_defaults.h b/src/mongo/db/read_write_concern_defaults.h
index f7ce2f9bfee..cb8b6386ae8 100644
--- a/src/mongo/db/read_write_concern_defaults.h
+++ b/src/mongo/db/read_write_concern_defaults.h
@@ -61,6 +61,15 @@ public:
ReadWriteConcernDefaults() = default;
~ReadWriteConcernDefaults() = default;
+ /**
+ * Returns true if the RC level is permissible to use as a default, and false if it cannot be a
+ * RC default.
+ */
+ static bool isSuitableReadConcernLevel(repl::ReadConcernLevel level);
+
+ /**
+ * Checks if the given RWC is suitable to use as a default, and uasserts if not.
+ */
static void checkSuitabilityAsDefault(const ReadConcern& rc);
static void checkSuitabilityAsDefault(const WriteConcern& wc);
diff --git a/src/mongo/db/repl/read_concern_args.cpp b/src/mongo/db/repl/read_concern_args.cpp
index a5de08a0fd7..d7ef1ce6ff1 100644
--- a/src/mongo/db/repl/read_concern_args.cpp
+++ b/src/mongo/db/repl/read_concern_args.cpp
@@ -57,18 +57,20 @@ const ReadConcernArgs& ReadConcernArgs::get(const OperationContext* opCtx) {
}
-ReadConcernArgs::ReadConcernArgs() = default;
+ReadConcernArgs::ReadConcernArgs() : _specified(false) {}
ReadConcernArgs::ReadConcernArgs(boost::optional<ReadConcernLevel> level)
- : _level(std::move(level)) {}
+ : _level(std::move(level)), _specified(_level) {}
ReadConcernArgs::ReadConcernArgs(boost::optional<OpTime> opTime,
boost::optional<ReadConcernLevel> level)
- : _opTime(std::move(opTime)), _level(std::move(level)) {}
+ : _opTime(std::move(opTime)), _level(std::move(level)), _specified(_opTime || _level) {}
ReadConcernArgs::ReadConcernArgs(boost::optional<LogicalTime> clusterTime,
boost::optional<ReadConcernLevel> level)
- : _afterClusterTime(std::move(clusterTime)), _level(std::move(level)) {}
+ : _afterClusterTime(std::move(clusterTime)),
+ _level(std::move(level)),
+ _specified(_afterClusterTime || _level) {}
std::string ReadConcernArgs::toString() const {
return toBSON().toString();
@@ -84,6 +86,10 @@ bool ReadConcernArgs::isEmpty() const {
return !_afterClusterTime && !_opTime && !_atClusterTime && !_level;
}
+bool ReadConcernArgs::isSpecified() const {
+ return _specified;
+}
+
ReadConcernLevel ReadConcernArgs::getLevel() const {
return _level.value_or(ReadConcernLevel::kLocalReadConcern);
}
@@ -105,6 +111,8 @@ boost::optional<LogicalTime> ReadConcernArgs::getArgsAtClusterTime() const {
}
Status ReadConcernArgs::initialize(const BSONElement& readConcernElem) {
+ invariant(isEmpty()); // only legal to call on uninitialized object.
+ _specified = false;
if (readConcernElem.eoo()) {
return Status::OK();
}
@@ -158,21 +166,15 @@ Status ReadConcernArgs::parse(const BSONObj& readConcernObj) {
return readCommittedStatus;
}
- if (levelString == kLocalReadConcernStr) {
- _level = ReadConcernLevel::kLocalReadConcern;
- } else if (levelString == kMajorityReadConcernStr) {
- _level = ReadConcernLevel::kMajorityReadConcern;
- } else if (levelString == kLinearizableReadConcernStr) {
- _level = ReadConcernLevel::kLinearizableReadConcern;
- } else if (levelString == kAvailableReadConcernStr) {
- _level = ReadConcernLevel::kAvailableReadConcern;
- } else if (levelString == kSnapshotReadConcernStr) {
- _level = ReadConcernLevel::kSnapshotReadConcern;
- } else {
+ _level = readConcernLevels::fromString(levelString);
+ if (!_level) {
return Status(ErrorCodes::FailedToParse,
str::stream() << kReadConcernFieldName << '.' << kLevelFieldName
- << " must be either 'local', 'majority', "
- "'linearizable', 'available', or 'snapshot'");
+ << " must be either '" << readConcernLevels::kLocalName
+ << "', '" << readConcernLevels::kMajorityName << "', '"
+ << readConcernLevels::kLinearizableName << "', '"
+ << readConcernLevels::kAvailableName << "', or '"
+ << readConcernLevels::kSnapshotName << "'");
}
} else {
return Status(ErrorCodes::InvalidOptions,
@@ -202,22 +204,23 @@ Status ReadConcernArgs::parse(const BSONObj& readConcernObj) {
return Status(ErrorCodes::InvalidOptions,
str::stream()
<< kAfterClusterTimeFieldName << " field can be set only if "
- << kLevelFieldName << " is equal to " << kMajorityReadConcernStr << ", "
- << kLocalReadConcernStr << ", or " << kSnapshotReadConcernStr);
+ << kLevelFieldName << " is equal to " << readConcernLevels::kMajorityName
+ << ", " << readConcernLevels::kLocalName << ", or "
+ << readConcernLevels::kSnapshotName);
}
if (_opTime && getLevel() == ReadConcernLevel::kSnapshotReadConcern) {
return Status(ErrorCodes::InvalidOptions,
str::stream()
<< kAfterOpTimeFieldName << " field cannot be set if " << kLevelFieldName
- << " is equal to " << kSnapshotReadConcernStr);
+ << " is equal to " << readConcernLevels::kSnapshotName);
}
if (_atClusterTime && getLevel() != ReadConcernLevel::kSnapshotReadConcern) {
return Status(ErrorCodes::InvalidOptions,
- str::stream()
- << kAtClusterTimeFieldName << " field can be set only if "
- << kLevelFieldName << " is equal to " << kSnapshotReadConcernStr);
+ str::stream() << kAtClusterTimeFieldName << " field can be set only if "
+ << kLevelFieldName << " is equal to "
+ << readConcernLevels::kSnapshotName);
}
// Make sure that atClusterTime wasn't specified with zero seconds.
@@ -233,6 +236,7 @@ Status ReadConcernArgs::parse(const BSONObj& readConcernObj) {
str::stream() << kAfterClusterTimeFieldName << " cannot be a null timestamp");
}
+ _specified = true;
return Status::OK();
}
@@ -261,33 +265,7 @@ void ReadConcernArgs::appendInfo(BSONObjBuilder* builder) const {
BSONObjBuilder rcBuilder(builder->subobjStart(kReadConcernFieldName));
if (_level) {
- StringData levelName;
- switch (_level.get()) {
- case ReadConcernLevel::kLocalReadConcern:
- levelName = kLocalReadConcernStr;
- break;
-
- case ReadConcernLevel::kMajorityReadConcern:
- levelName = kMajorityReadConcernStr;
- break;
-
- case ReadConcernLevel::kLinearizableReadConcern:
- levelName = kLinearizableReadConcernStr;
- break;
-
- case ReadConcernLevel::kAvailableReadConcern:
- levelName = kAvailableReadConcernStr;
- break;
-
- case ReadConcernLevel::kSnapshotReadConcern:
- levelName = kSnapshotReadConcernStr;
- break;
-
- default:
- MONGO_UNREACHABLE;
- }
-
- rcBuilder.append(kLevelFieldName, levelName);
+ rcBuilder.append(kLevelFieldName, readConcernLevels::toString(_level.get()));
}
if (_opTime) {
@@ -305,5 +283,43 @@ void ReadConcernArgs::appendInfo(BSONObjBuilder* builder) const {
rcBuilder.done();
}
+boost::optional<ReadConcernLevel> readConcernLevels::fromString(StringData levelString) {
+ if (levelString == readConcernLevels::kLocalName) {
+ return ReadConcernLevel::kLocalReadConcern;
+ } else if (levelString == readConcernLevels::kMajorityName) {
+ return ReadConcernLevel::kMajorityReadConcern;
+ } else if (levelString == readConcernLevels::kLinearizableName) {
+ return ReadConcernLevel::kLinearizableReadConcern;
+ } else if (levelString == readConcernLevels::kAvailableName) {
+ return ReadConcernLevel::kAvailableReadConcern;
+ } else if (levelString == readConcernLevels::kSnapshotName) {
+ return ReadConcernLevel::kSnapshotReadConcern;
+ } else {
+ return boost::none;
+ }
+}
+
+StringData readConcernLevels::toString(ReadConcernLevel level) {
+ switch (level) {
+ case ReadConcernLevel::kLocalReadConcern:
+ return kLocalName;
+
+ case ReadConcernLevel::kMajorityReadConcern:
+ return kMajorityName;
+
+ case ReadConcernLevel::kLinearizableReadConcern:
+ return kLinearizableName;
+
+ case ReadConcernLevel::kAvailableReadConcern:
+ return kAvailableName;
+
+ case ReadConcernLevel::kSnapshotReadConcern:
+ return kSnapshotName;
+
+ default:
+ MONGO_UNREACHABLE;
+ }
+}
+
} // namespace repl
} // namespace mongo
diff --git a/src/mongo/db/repl/read_concern_args.h b/src/mongo/db/repl/read_concern_args.h
index 310cfc59432..110624649bd 100644
--- a/src/mongo/db/repl/read_concern_args.h
+++ b/src/mongo/db/repl/read_concern_args.h
@@ -54,12 +54,6 @@ public:
static constexpr StringData kAtClusterTimeFieldName = "atClusterTime"_sd;
static constexpr StringData kLevelFieldName = "level"_sd;
- static constexpr StringData kLocalReadConcernStr = "local"_sd;
- static constexpr StringData kMajorityReadConcernStr = "majority"_sd;
- static constexpr StringData kLinearizableReadConcernStr = "linearizable"_sd;
- static constexpr StringData kAvailableReadConcernStr = "available"_sd;
- static constexpr StringData kSnapshotReadConcernStr = "snapshot"_sd;
-
/**
* Represents the internal mechanism an operation uses to satisfy 'majority' read concern.
*/
@@ -147,6 +141,13 @@ public:
bool isEmpty() const;
/**
+ * Returns true if this ReadConcernArgs represents a read concern that was actually specified.
+ * If the RC was specified as an empty BSON object this will still be true (unlike isEmpty()).
+ * False represents an absent or missing read concern, ie. one which wasn't present at all.
+ */
+ bool isSpecified() const;
+
+ /**
* Returns default kLocalReadConcern if _level is not set.
*/
ReadConcernLevel getLevel() const;
@@ -187,6 +188,12 @@ private:
* level is 'majority'.
*/
MajorityReadMechanism _majorityReadMechanism{MajorityReadMechanism::kMajoritySnapshot};
+
+ /**
+ * True indicates that a read concern has been specified (even if it might be empty), as
+ * opposed to being absent or missing.
+ */
+ bool _specified;
};
} // namespace repl
diff --git a/src/mongo/db/repl/read_concern_level.h b/src/mongo/db/repl/read_concern_level.h
index 9b8c1951de9..e0a8f5c3304 100644
--- a/src/mongo/db/repl/read_concern_level.h
+++ b/src/mongo/db/repl/read_concern_level.h
@@ -29,6 +29,8 @@
#pragma once
+#include <boost/optional.hpp>
+
namespace mongo {
namespace repl {
@@ -40,5 +42,24 @@ enum class ReadConcernLevel {
kSnapshotReadConcern
};
+namespace readConcernLevels {
+
+constexpr std::initializer_list<ReadConcernLevel> all = {ReadConcernLevel::kLocalReadConcern,
+ ReadConcernLevel::kMajorityReadConcern,
+ ReadConcernLevel::kLinearizableReadConcern,
+ ReadConcernLevel::kAvailableReadConcern,
+ ReadConcernLevel::kSnapshotReadConcern};
+
+constexpr StringData kLocalName = "local"_sd;
+constexpr StringData kMajorityName = "majority"_sd;
+constexpr StringData kLinearizableName = "linearizable"_sd;
+constexpr StringData kAvailableName = "available"_sd;
+constexpr StringData kSnapshotName = "snapshot"_sd;
+
+boost::optional<ReadConcernLevel> fromString(StringData levelString);
+StringData toString(ReadConcernLevel level);
+
+} // namespace readConcernLevels
+
} // namespace repl
} // namespace mongo
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index 182509fa32a..6fb5557d3d4 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -62,6 +62,7 @@
#include "mongo/db/ops/write_ops_exec.h"
#include "mongo/db/query/find.h"
#include "mongo/db/read_concern.h"
+#include "mongo/db/read_write_concern_defaults.h"
#include "mongo/db/repl/optime.h"
#include "mongo/db/repl/read_concern_args.h"
#include "mongo/db/repl/repl_client_info.h"
@@ -226,7 +227,8 @@ private:
* Given the specified command, returns an effective read concern which should be used or an error
* if the read concern is not valid for the command.
*/
-StatusWith<repl::ReadConcernArgs> _extractReadConcern(const CommandInvocation* invocation,
+StatusWith<repl::ReadConcernArgs> _extractReadConcern(OperationContext* opCtx,
+ const CommandInvocation* invocation,
const BSONObj& cmdObj,
bool startTransaction) {
repl::ReadConcernArgs readConcernArgs;
@@ -236,7 +238,41 @@ StatusWith<repl::ReadConcernArgs> _extractReadConcern(const CommandInvocation* i
return readConcernParseStatus;
}
+ auto readConcernSupport = invocation->supportsReadConcern(readConcernArgs.getLevel());
+ if (readConcernSupport.defaultReadConcern ==
+ ReadConcernSupportResult::DefaultReadConcern::kPermitted &&
+ !opCtx->getClient()->isInDirectClient()) {
+ if (serverGlobalParams.clusterRole == ClusterRole::ShardServer ||
+ serverGlobalParams.clusterRole == ClusterRole::ConfigServer) {
+ // ReadConcern should always be explicitly specified by operations received on shard and
+ // config servers, even if it is empty (ie. readConcern: {}). In this context
+ // (shard/config servers) an empty RC indicates the operation should use the implicit
+ // server defaults. So, warn if the operation has not specified readConcern and is on a
+ // shard/config server.
+ if (!readConcernArgs.isSpecified()) {
+ // TODO: Disabled until after SERVER-43712, to avoid log spam.
+ // log() << "Missing readConcern on " << invocation->definition()->getName();
+ }
+ } else {
+ // A member in a regular replica set. Since these servers receive client queries, in
+ // this context empty RC (ie. readConcern: {}) means the same as if absent/unspecified,
+ // which is to apply the CWRWC defaults if present. This means we just test isEmpty(),
+ // since this covers both isSpecified() && !isSpecified()
+ if (readConcernArgs.isEmpty()) {
+ const auto rcDefault = ReadWriteConcernDefaults::get(opCtx->getServiceContext())
+ .getDefaultReadConcern();
+ if (rcDefault) {
+ readConcernArgs = *rcDefault;
+ LOG(2) << "Applying default readConcern on "
+ << invocation->definition()->getName() << " of " << *rcDefault;
+ }
+ }
+ }
+ }
+
auto readConcernLevel = readConcernArgs.getLevel();
+ // Update the readConcernSupport, in case the default RC was applied.
+ readConcernSupport = invocation->supportsReadConcern(readConcernLevel);
if (startTransaction && readConcernLevel != repl::ReadConcernLevel::kSnapshotReadConcern &&
readConcernLevel != repl::ReadConcernLevel::kMajorityReadConcern &&
readConcernLevel != repl::ReadConcernLevel::kLocalReadConcern) {
@@ -256,7 +292,8 @@ StatusWith<repl::ReadConcernArgs> _extractReadConcern(const CommandInvocation* i
// There is no need to check if the command supports the read concern while in a transaction
// because all commands that are allowed to run in a transaction must support all the read
// concerns that can be used with a transaction.
- if (!startTransaction && !invocation->supportsReadConcern(readConcernLevel)) {
+ if (!startTransaction &&
+ readConcernSupport.readConcern == ReadConcernSupportResult::ReadConcern::kNotSupported) {
return {ErrorCodes::InvalidOptions,
str::stream() << "Command does not support read concern "
<< readConcernArgs.toString()};
@@ -564,6 +601,18 @@ bool runCommandImpl(OperationContext* opCtx,
// the command body.
behaviors.uassertCommandDoesNotSpecifyWriteConcern(request.body);
} else {
+ // WriteConcern should always be explicitly specified by operations received on shard
+ // and config servers, even if it is empty (ie. writeConcern: {}). In this context
+ // (shard/config servers) an empty WC indicates the operation should use the implicit
+ // server defaults. So, warn if the operation has not specified writeConcern and is on
+ // a shard/config server.
+ if (!opCtx->getClient()->isInDirectClient() &&
+ (serverGlobalParams.clusterRole == ClusterRole::ShardServer ||
+ serverGlobalParams.clusterRole == ClusterRole::ConfigServer) &&
+ !request.body.hasField(WriteConcernOptions::kWriteConcernField)) {
+ // TODO: Disabled until after SERVER-43712, to avoid log spam.
+ // log() << "Missing writeConcern on " << command->getName();
+ }
extractedWriteConcern.emplace(
uassertStatusOK(extractWriteConcern(opCtx, request.body)));
if (sessionOptions.getAutocommit()) {
@@ -862,7 +911,7 @@ void execCommandDatabase(OperationContext* opCtx,
bool startTransaction = static_cast<bool>(sessionOptions.getStartTransaction());
if (!skipReadConcern) {
auto newReadConcernArgs = uassertStatusOK(
- _extractReadConcern(invocation.get(), request.body, startTransaction));
+ _extractReadConcern(opCtx, invocation.get(), request.body, startTransaction));
{
// We must obtain the client lock to set the ReadConcernArgs on the operation
// context as it may be concurrently read by CurrentOp.
@@ -976,7 +1025,8 @@ void execCommandDatabase(OperationContext* opCtx,
// parse it here, so if it is valid it can be used to compute the proper operationTime.
auto& readConcernArgs = repl::ReadConcernArgs::get(opCtx);
if (readConcernArgs.isEmpty()) {
- auto readConcernArgsStatus = _extractReadConcern(invocation.get(), request.body, false);
+ auto readConcernArgsStatus =
+ _extractReadConcern(opCtx, invocation.get(), request.body, false);
if (readConcernArgsStatus.isOK()) {
// We must obtain the client lock to set the ReadConcernArgs on the operation
// context as it may be concurrently read by CurrentOp.
diff --git a/src/mongo/db/write_concern.cpp b/src/mongo/db/write_concern.cpp
index 30c01e5a831..88577ae6269 100644
--- a/src/mongo/db/write_concern.cpp
+++ b/src/mongo/db/write_concern.cpp
@@ -38,6 +38,7 @@
#include "mongo/db/client.h"
#include "mongo/db/commands/server_status_metric.h"
#include "mongo/db/operation_context.h"
+#include "mongo/db/read_write_concern_defaults.h"
#include "mongo/db/repl/optime.h"
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/server_options.h"
@@ -79,11 +80,24 @@ StatusWith<WriteConcernOptions> extractWriteConcern(OperationContext* opCtx,
WriteConcernOptions writeConcern = wcResult.getValue();
- // Get the default write concern specified in ReplSetConfig only if no write concern is
- // specified in the command (when usedDefault is true) to avoid locking the
- // ReplicationCoordinator mutex unconditionally.
+ // If no write concern is specified in the command (so usedDefault is true), then use the
+ // cluster-wide default WC (if there is one), or else the default WC from the ReplSetConfig
+ // (which takes the ReplicationCoordinator mutex).
if (writeConcern.usedDefault) {
- writeConcern = repl::ReplicationCoordinator::get(opCtx)->getGetLastErrorDefault();
+ writeConcern = ([&]() {
+ // WriteConcern defaults can only be applied on regular replica set members. Operations
+ // received by shard and config servers should always have WC explicitly specified.
+ if (serverGlobalParams.clusterRole != ClusterRole::ShardServer &&
+ serverGlobalParams.clusterRole != ClusterRole::ConfigServer &&
+ !opCtx->getClient()->isInDirectClient()) {
+ auto wcDefault = ReadWriteConcernDefaults::get(opCtx->getServiceContext())
+ .getDefaultWriteConcern();
+ if (wcDefault) {
+ return *wcDefault;
+ }
+ }
+ return repl::ReplicationCoordinator::get(opCtx)->getGetLastErrorDefault();
+ })();
if (writeConcern.wNumNodes == 0 && writeConcern.wMode.empty()) {
writeConcern.wNumNodes = 1;
}
diff --git a/src/mongo/s/commands/cluster_distinct_cmd.cpp b/src/mongo/s/commands/cluster_distinct_cmd.cpp
index c584258344c..3c9c667678d 100644
--- a/src/mongo/s/commands/cluster_distinct_cmd.cpp
+++ b/src/mongo/s/commands/cluster_distinct_cmd.cpp
@@ -72,10 +72,11 @@ public:
return false;
}
- bool supportsReadConcern(const std::string& dbName,
- const BSONObj& cmdObj,
- repl::ReadConcernLevel level) const final {
- return true;
+ ReadConcernSupportResult supportsReadConcern(const std::string& dbName,
+ const BSONObj& cmdObj,
+ repl::ReadConcernLevel level) const final {
+ return {ReadConcernSupportResult::ReadConcern::kSupported,
+ ReadConcernSupportResult::DefaultReadConcern::kPermitted};
}
void addRequiredPrivileges(const std::string& dbname,
diff --git a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp
index ee72e7e43b1..90442a2b4c2 100644
--- a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp
+++ b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp
@@ -153,10 +153,11 @@ public:
return true;
}
- bool supportsReadConcern(const std::string& dbName,
- const BSONObj& cmdObj,
- repl::ReadConcernLevel level) const final {
- return true;
+ ReadConcernSupportResult supportsReadConcern(const std::string& dbName,
+ const BSONObj& cmdObj,
+ repl::ReadConcernLevel level) const final {
+ return {ReadConcernSupportResult::ReadConcern::kSupported,
+ ReadConcernSupportResult::DefaultReadConcern::kPermitted};
}
void addRequiredPrivileges(const std::string& dbname,
diff --git a/src/mongo/s/commands/cluster_find_cmd.cpp b/src/mongo/s/commands/cluster_find_cmd.cpp
index e133875439b..091cac8879b 100644
--- a/src/mongo/s/commands/cluster_find_cmd.cpp
+++ b/src/mongo/s/commands/cluster_find_cmd.cpp
@@ -112,8 +112,9 @@ public:
return false;
}
- bool supportsReadConcern(repl::ReadConcernLevel level) const final {
- return true;
+ ReadConcernSupportResult supportsReadConcern(repl::ReadConcernLevel level) const final {
+ return {ReadConcernSupportResult::ReadConcern::kSupported,
+ ReadConcernSupportResult::DefaultReadConcern::kPermitted};
}
NamespaceString ns() const override {
diff --git a/src/mongo/s/commands/cluster_killcursors_cmd.cpp b/src/mongo/s/commands/cluster_killcursors_cmd.cpp
index 6d8671c4ab4..a90a24d3f53 100644
--- a/src/mongo/s/commands/cluster_killcursors_cmd.cpp
+++ b/src/mongo/s/commands/cluster_killcursors_cmd.cpp
@@ -42,11 +42,12 @@ class ClusterKillCursorsCmd final : public KillCursorsCmdBase {
public:
ClusterKillCursorsCmd() = default;
- bool supportsReadConcern(const std::string& dbName,
- const BSONObj& cmdObj,
- repl::ReadConcernLevel level) const final {
+ ReadConcernSupportResult supportsReadConcern(const std::string& dbName,
+ const BSONObj& cmdObj,
+ repl::ReadConcernLevel level) const final {
// killCursors must support read concerns in order to be run in transactions.
- return true;
+ return {ReadConcernSupportResult::ReadConcern::kSupported,
+ ReadConcernSupportResult::DefaultReadConcern::kPermitted};
}
bool run(OperationContext* opCtx,
diff --git a/src/mongo/s/commands/cluster_pipeline_cmd.cpp b/src/mongo/s/commands/cluster_pipeline_cmd.cpp
index 25dbc52d9fc..02a70417d72 100644
--- a/src/mongo/s/commands/cluster_pipeline_cmd.cpp
+++ b/src/mongo/s/commands/cluster_pipeline_cmd.cpp
@@ -70,8 +70,9 @@ public:
return true;
}
- bool supportsReadConcern(repl::ReadConcernLevel level) const override {
- return true;
+ ReadConcernSupportResult supportsReadConcern(repl::ReadConcernLevel level) const override {
+ return {ReadConcernSupportResult::ReadConcern::kSupported,
+ ReadConcernSupportResult::DefaultReadConcern::kPermitted};
}
void _runAggCommand(OperationContext* opCtx,
diff --git a/src/mongo/s/commands/cluster_write_cmd.cpp b/src/mongo/s/commands/cluster_write_cmd.cpp
index 479ed6a81ba..e1b8a7a41e2 100644
--- a/src/mongo/s/commands/cluster_write_cmd.cpp
+++ b/src/mongo/s/commands/cluster_write_cmd.cpp
@@ -543,8 +543,9 @@ private:
return true;
}
- bool supportsReadConcern(repl::ReadConcernLevel level) const final {
- return true;
+ ReadConcernSupportResult supportsReadConcern(repl::ReadConcernLevel level) const final {
+ return {ReadConcernSupportResult::ReadConcern::kSupported,
+ ReadConcernSupportResult::DefaultReadConcern::kPermitted};
}
void doCheckAuthorization(OperationContext* opCtx) const final {