summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias Stearn <mathias@10gen.com>2017-04-26 09:36:41 -0400
committerMathias Stearn <mathias@10gen.com>2017-05-12 12:08:30 -0400
commitf2902d59175c0724944ca98d13f784e2de944053 (patch)
tree44788ee704540d420f649279053019e3b6a332b2
parent1a955fc356627b8cc74eb15506608dd987184608 (diff)
downloadmongo-f2902d59175c0724944ca98d13f784e2de944053.tar.gz
SERVER-28814 Replace ServerSelectionMetadata with just ReadPreferenceSetting
-rw-r--r--src/mongo/client/SConscript1
-rw-r--r--src/mongo/client/dbclient_rs.cpp64
-rw-r--r--src/mongo/client/dbclient_rs_test.cpp14
-rw-r--r--src/mongo/client/query.cpp3
-rw-r--r--src/mongo/client/read_preference.cpp39
-rw-r--r--src/mongo/client/read_preference.h53
-rw-r--r--src/mongo/client/read_preference_test.cpp20
-rw-r--r--src/mongo/db/commands.h6
-rw-r--r--src/mongo/db/commands/dbcommands.cpp5
-rw-r--r--src/mongo/db/commands/explain_cmd.cpp3
-rw-r--r--src/mongo/rpc/SConscript3
-rw-r--r--src/mongo/rpc/command_request.cpp15
-rw-r--r--src/mongo/rpc/command_request_builder.cpp11
-rw-r--r--src/mongo/rpc/metadata.cpp75
-rw-r--r--src/mongo/rpc/metadata/server_selection_metadata.cpp344
-rw-r--r--src/mongo/rpc/metadata/server_selection_metadata.h126
-rw-r--r--src/mongo/rpc/metadata/server_selection_metadata_test.cpp206
-rw-r--r--src/mongo/rpc/metadata_test.cpp143
-rw-r--r--src/mongo/s/async_requests_sender.cpp7
-rw-r--r--src/mongo/s/commands/cluster_commands_common.cpp12
-rw-r--r--src/mongo/s/commands/cluster_commands_common.h13
-rw-r--r--src/mongo/s/commands/cluster_find_cmd.cpp9
-rw-r--r--src/mongo/s/commands/cluster_pipeline_cmd.cpp11
-rw-r--r--src/mongo/s/commands/strategy.cpp47
-rw-r--r--src/mongo/s/commands/strategy.h6
-rw-r--r--src/mongo/s/query/async_results_merger.cpp7
-rw-r--r--src/mongo/s/query/async_results_merger_test.cpp5
-rw-r--r--src/mongo/s/query/cluster_find.cpp10
-rw-r--r--src/mongo/s/query/cluster_find.h4
29 files changed, 381 insertions, 881 deletions
diff --git a/src/mongo/client/SConscript b/src/mongo/client/SConscript
index 89fb9b75516..fa9f3dcff45 100644
--- a/src/mongo/client/SConscript
+++ b/src/mongo/client/SConscript
@@ -37,6 +37,7 @@ env.Library(
],
LIBDEPS=[
'$BUILD_DIR/mongo/bson/util/bson_extract',
+ '$BUILD_DIR/mongo/db/service_context'
],
)
diff --git a/src/mongo/client/dbclient_rs.cpp b/src/mongo/client/dbclient_rs.cpp
index 219069bb9dc..cd80a61f79d 100644
--- a/src/mongo/client/dbclient_rs.cpp
+++ b/src/mongo/client/dbclient_rs.cpp
@@ -43,7 +43,7 @@
#include "mongo/client/sasl_client_authenticate.h"
#include "mongo/db/dbmessage.h"
#include "mongo/db/jsobj.h"
-#include "mongo/rpc/metadata/server_selection_metadata.h"
+#include "mongo/stdx/memory.h"
#include "mongo/util/log.h"
namespace mongo {
@@ -104,30 +104,19 @@ const size_t MAX_RETRY = 3;
*
* @throws AssertionException if the read preference object is malformed
*/
-ReadPreferenceSetting* _extractReadPref(const BSONObj& query, int queryOptions) {
- if (Query::hasReadPreference(query)) {
- BSONElement readPrefElement;
-
- if (query.hasField(Query::ReadPrefField.name())) {
- readPrefElement = query[Query::ReadPrefField.name()];
- } else {
- readPrefElement = query["$queryOptions"][Query::ReadPrefField.name()];
- }
-
- uassert(16381, "$readPreference should be an object", readPrefElement.isABSONObj());
-
- const BSONObj& prefDoc = readPrefElement.Obj();
-
- auto readPrefSetting = uassertStatusOK(ReadPreferenceSetting::fromBSON(prefDoc));
-
- return new ReadPreferenceSetting(std::move(readPrefSetting));
- }
-
+std::unique_ptr<ReadPreferenceSetting> _extractReadPref(const BSONObj& query, int queryOptions) {
// Default read pref is primary only or secondary preferred with slaveOK
- ReadPreference pref = queryOptions & QueryOption_SlaveOk
- ? mongo::ReadPreference::SecondaryPreferred
- : mongo::ReadPreference::PrimaryOnly;
- return new ReadPreferenceSetting(pref, TagSet());
+ const auto defaultReadPref = queryOptions & QueryOption_SlaveOk
+ ? ReadPreference::SecondaryPreferred
+ : ReadPreference::PrimaryOnly;
+
+ auto readPrefContainingObj = query;
+ if (auto elem = query["$queryOptions"]) {
+ // The readPreference is embedded in the $queryOptions field.
+ readPrefContainingObj = elem.Obj();
+ }
+ return stdx::make_unique<ReadPreferenceSetting>(uassertStatusOK(
+ ReadPreferenceSetting::fromContainingBSON(readPrefContainingObj, defaultReadPref)));
}
} // namespace
@@ -273,7 +262,9 @@ bool _isSecondaryQuery(const string& ns,
// Only certain commands are supported for secondary operation.
BSONObj actualQueryObj;
- if (strcmp(queryObj.firstElement().fieldName(), "query") == 0) {
+ if (strcmp(queryObj.firstElement().fieldName(), "$query") == 0) {
+ actualQueryObj = queryObj["$query"].embeddedObject();
+ } else if (strcmp(queryObj.firstElement().fieldName(), "query") == 0) {
actualQueryObj = queryObj["query"].embeddedObject();
} else {
actualQueryObj = queryObj;
@@ -532,7 +523,7 @@ unique_ptr<DBClientCursor> DBClientReplicaSet::query(const string& ns,
shared_ptr<ReadPreferenceSetting> readPref(_extractReadPref(query.obj, queryOptions));
if (_isSecondaryQuery(ns, query.obj, *readPref)) {
LOG(3) << "dbclient_rs query using secondary or tagged node selection in "
- << _getMonitor()->getName() << ", read pref is " << readPref->toBSON()
+ << _getMonitor()->getName() << ", read pref is " << readPref->toString()
<< " (primary : "
<< (_master.get() != NULL ? _master->getServerAddress() : "[not cached]")
<< ", lastTagged : " << (_lastSlaveOkConn.get() != NULL
@@ -584,7 +575,7 @@ BSONObj DBClientReplicaSet::findOne(const string& ns,
shared_ptr<ReadPreferenceSetting> readPref(_extractReadPref(query.obj, queryOptions));
if (_isSecondaryQuery(ns, query.obj, *readPref)) {
LOG(3) << "dbclient_rs findOne using secondary or tagged node selection in "
- << _getMonitor()->getName() << ", read pref is " << readPref->toBSON()
+ << _getMonitor()->getName() << ", read pref is " << readPref->toString()
<< " (primary : "
<< (_master.get() != NULL ? _master->getServerAddress() : "[not cached]")
<< ", lastTagged : " << (_lastSlaveOkConn.get() != NULL
@@ -761,7 +752,7 @@ void DBClientReplicaSet::say(Message& toSend, bool isRetry, string* actualServer
shared_ptr<ReadPreferenceSetting> readPref(_extractReadPref(qm.query, qm.queryOptions));
if (_isSecondaryQuery(qm.ns, qm.query, *readPref)) {
LOG(3) << "dbclient_rs say using secondary or tagged node selection in "
- << _getMonitor()->getName() << ", read pref is " << readPref->toBSON()
+ << _getMonitor()->getName() << ", read pref is " << readPref->toString()
<< " (primary : "
<< (_master.get() != NULL ? _master->getServerAddress() : "[not cached]")
<< ", lastTagged : " << (_lastSlaveOkConn.get() != NULL
@@ -932,16 +923,7 @@ DBClientReplicaSet::runCommandWithMetadataAndTarget(StringData database,
// so we don't have to re-parse it, however, that will come with its own set of
// complications (e.g. some kind of base class or concept for MetadataSerializable
// objects). For now we do it the stupid way.
- auto ssm = uassertStatusOK(rpc::ServerSelectionMetadata::readFromMetadata(
- metadata.getField(rpc::ServerSelectionMetadata::fieldName())));
-
- // If we didn't get a readPref with this query, we assume SecondaryPreferred if secondaryOk
- // is true, and PrimaryOnly otherwise. This logic is replicated from _extractReadPref.
- auto defaultReadPref = ssm.isSecondaryOk()
- ? ReadPreferenceSetting(ReadPreference::SecondaryPreferred, TagSet())
- : ReadPreferenceSetting(ReadPreference::PrimaryOnly, TagSet::primaryOnly());
-
- auto readPref = ssm.getReadPreference().get_value_or(defaultReadPref);
+ auto readPref = uassertStatusOK(ReadPreferenceSetting::fromContainingBSON(metadata));
if (readPref.pref == ReadPreference::PrimaryOnly ||
// If the command is not runnable on a secondary, we run it on the primary
@@ -970,8 +952,8 @@ DBClientReplicaSet::runCommandWithMetadataAndTarget(StringData database,
}
uasserted(ErrorCodes::NodeNotFound,
- str::stream() << "Could not satisfy $readPreference of '" << readPref.toBSON() << "' "
- << "while attempting to run command "
+ str::stream() << "Could not satisfy $readPreference of '" << readPref.toString()
+ << "' while attempting to run command "
<< command);
}
@@ -990,7 +972,7 @@ bool DBClientReplicaSet::call(Message& toSend,
shared_ptr<ReadPreferenceSetting> readPref(_extractReadPref(qm.query, qm.queryOptions));
if (_isSecondaryQuery(ns, qm.query, *readPref)) {
LOG(3) << "dbclient_rs call using secondary or tagged node selection in "
- << _getMonitor()->getName() << ", read pref is " << readPref->toBSON()
+ << _getMonitor()->getName() << ", read pref is " << readPref->toString()
<< " (primary : "
<< (_master.get() != NULL ? _master->getServerAddress() : "[not cached]")
<< ", lastTagged : " << (_lastSlaveOkConn.get() != NULL
diff --git a/src/mongo/client/dbclient_rs_test.cpp b/src/mongo/client/dbclient_rs_test.cpp
index 15967c07677..15bc9f24a58 100644
--- a/src/mongo/client/dbclient_rs_test.cpp
+++ b/src/mongo/client/dbclient_rs_test.cpp
@@ -44,7 +44,6 @@
#include "mongo/db/jsobj.h"
#include "mongo/dbtests/mock/mock_conn_registry.h"
#include "mongo/dbtests/mock/mock_replica_set.h"
-#include "mongo/rpc/metadata/server_selection_metadata.h"
#include "mongo/stdx/unordered_set.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/assert_util.h"
@@ -62,11 +61,8 @@ using std::vector;
/**
* Constructs a metadata object containing the passed server selection metadata.
*/
-BSONObj makeMetadata(ReadPreference rp, TagSet tagSet, bool secondaryOk) {
- BSONObjBuilder metadataBob;
- rpc::ServerSelectionMetadata ssm(secondaryOk, ReadPreferenceSetting(rp, tagSet));
- uassertStatusOK(ssm.writeToMetadata(&metadataBob));
- return metadataBob.obj();
+BSONObj makeMetadata(ReadPreference rp, TagSet tagSet) {
+ return ReadPreferenceSetting(rp, std::move(tagSet)).toContainingBSON();
}
/**
@@ -105,7 +101,7 @@ void assertOneOfNodesSelected(MockReplicaSet* replSet,
auto tagSet = secondaryOk ? TagSet() : TagSet::primaryOnly();
// We need the command to be a "SecOk command"
auto res = replConn.runCommandWithMetadata(
- "foo", "dbStats", makeMetadata(rp, tagSet, secondaryOk), BSON("dbStats" << 1));
+ "foo", "dbStats", makeMetadata(rp, tagSet), BSON("dbStats" << 1));
stdx::unordered_set<HostAndPort> hostSet;
for (const auto& hostName : hostNames) {
hostSet.emplace(hostName);
@@ -228,13 +224,11 @@ private:
void assertRunCommandWithReadPrefThrows(MockReplicaSet* replSet, ReadPreference rp) {
bool isPrimaryOnly = (rp == ReadPreference::PrimaryOnly);
-
- bool secondaryOk = !isPrimaryOnly;
TagSet ts = isPrimaryOnly ? TagSet::primaryOnly() : TagSet();
DBClientReplicaSet replConn(replSet->getSetName(), replSet->getHosts(), StringData());
ASSERT_THROWS(replConn.runCommandWithMetadata(
- "foo", "whoami", makeMetadata(rp, ts, secondaryOk), BSON("dbStats" << 1)),
+ "foo", "whoami", makeMetadata(rp, ts), BSON("dbStats" << 1)),
AssertionException);
}
diff --git a/src/mongo/client/query.cpp b/src/mongo/client/query.cpp
index 419f5b485e0..349ed5ff96d 100644
--- a/src/mongo/client/query.cpp
+++ b/src/mongo/client/query.cpp
@@ -115,7 +115,8 @@ bool Query::isComplex(const BSONObj& obj, bool* hasDollar) {
}
Query& Query::readPref(ReadPreference pref, const BSONArray& tags) {
- appendComplex(ReadPrefField.name().c_str(), ReadPreferenceSetting(pref, TagSet(tags)).toBSON());
+ appendComplex(ReadPrefField.name().c_str(),
+ ReadPreferenceSetting(pref, TagSet(tags)).toInnerBSON());
return *this;
}
diff --git a/src/mongo/client/read_preference.cpp b/src/mongo/client/read_preference.cpp
index acdd83c1771..a8790ae9f3c 100644
--- a/src/mongo/client/read_preference.cpp
+++ b/src/mongo/client/read_preference.cpp
@@ -119,10 +119,14 @@ TagSet defaultTagSetForMode(ReadPreference mode) {
*/
const Seconds ReadPreferenceSetting::kMinimalMaxStalenessValue(90);
+const OperationContext::Decoration<ReadPreferenceSetting> ReadPreferenceSetting::get =
+ OperationContext::declareDecoration<ReadPreferenceSetting>();
+
const BSONObj& ReadPreferenceSetting::secondaryPreferredMetadata() {
// This is a static method rather than a static member only because it is used by another TU
// during dynamic init.
- static const auto bson = BSON("$ssm" << BSON("$secondaryOk" << true));
+ static const auto bson =
+ ReadPreferenceSetting(ReadPreference::SecondaryPreferred).toContainingBSON();
return bson;
}
@@ -148,7 +152,7 @@ ReadPreferenceSetting::ReadPreferenceSetting(ReadPreference pref, TagSet tags)
ReadPreferenceSetting::ReadPreferenceSetting(ReadPreference pref)
: ReadPreferenceSetting(pref, defaultTagSetForMode(pref)) {}
-StatusWith<ReadPreferenceSetting> ReadPreferenceSetting::fromBSON(const BSONObj& readPrefObj) {
+StatusWith<ReadPreferenceSetting> ReadPreferenceSetting::fromInnerBSON(const BSONObj& readPrefObj) {
std::string modeStr;
auto modeExtractStatus = bsonExtractStringField(readPrefObj, kModeFieldName, &modeStr);
if (!modeExtractStatus.isOK()) {
@@ -227,20 +231,37 @@ StatusWith<ReadPreferenceSetting> ReadPreferenceSetting::fromBSON(const BSONObj&
return ReadPreferenceSetting(mode, tags, Seconds(maxStalenessSecondsValue));
}
-BSONObj ReadPreferenceSetting::toBSON() const {
- BSONObjBuilder bob;
- bob.append(kModeFieldName, readPreferenceName(pref));
+StatusWith<ReadPreferenceSetting> ReadPreferenceSetting::fromInnerBSON(const BSONElement& elem) {
+ if (elem.type() != mongo::Object) {
+ return Status(ErrorCodes::TypeMismatch,
+ str::stream() << "$readPreference has incorrect type: expected "
+ << mongo::Object
+ << " but got "
+ << elem.type());
+ }
+ return fromInnerBSON(elem.Obj());
+}
+
+StatusWith<ReadPreferenceSetting> ReadPreferenceSetting::fromContainingBSON(
+ const BSONObj& obj, ReadPreference defaultReadPref) {
+ if (auto elem = obj["$readPreference"]) {
+ return fromInnerBSON(elem);
+ }
+ return ReadPreferenceSetting(defaultReadPref);
+}
+
+void ReadPreferenceSetting::toInnerBSON(BSONObjBuilder* bob) const {
+ bob->append(kModeFieldName, readPreferenceName(pref));
if (tags != defaultTagSetForMode(pref)) {
- bob.append(kTagsFieldName, tags.getTagBSON());
+ bob->append(kTagsFieldName, tags.getTagBSON());
}
if (maxStalenessSeconds.count() > 0) {
- bob.append(kMaxStalenessSecondsFieldName, maxStalenessSeconds.count());
+ bob->append(kMaxStalenessSecondsFieldName, maxStalenessSeconds.count());
}
- return bob.obj();
}
std::string ReadPreferenceSetting::toString() const {
- return toBSON().toString();
+ return toInnerBSON().toString();
}
} // namespace mongo
diff --git a/src/mongo/client/read_preference.h b/src/mongo/client/read_preference.h
index 35ef1c9067b..c1f09765be5 100644
--- a/src/mongo/client/read_preference.h
+++ b/src/mongo/client/read_preference.h
@@ -30,6 +30,7 @@
#include "mongo/bson/simple_bsonobj_comparator.h"
#include "mongo/db/jsobj.h"
+#include "mongo/db/operation_context.h"
#include "mongo/db/repl/optime.h"
#include "mongo/util/duration.h"
@@ -115,6 +116,8 @@ private:
};
struct ReadPreferenceSetting {
+ static const OperationContext::Decoration<ReadPreferenceSetting> get;
+
/**
* The minimal value maxStalenessSeconds can have.
*/
@@ -122,7 +125,7 @@ struct ReadPreferenceSetting {
/**
* An object representing the metadata generated for a SecondaryPreferred read preference:
- * {$ssm: {$secondaryOk: true}}
+ * {$readPreference: {mode: "secondaryPreferred"}}
*/
static const BSONObj& secondaryPreferredMetadata();
@@ -137,6 +140,7 @@ struct ReadPreferenceSetting {
ReadPreferenceSetting(ReadPreference pref, Seconds maxStalenessSeconds);
ReadPreferenceSetting(ReadPreference pref, TagSet tags);
explicit ReadPreferenceSetting(ReadPreference pref);
+ ReadPreferenceSetting() : ReadPreferenceSetting(ReadPreference::PrimaryOnly) {}
inline bool equals(const ReadPreferenceSetting& other) const {
return (pref == other.pref) && (tags == other.tags) &&
@@ -144,14 +148,33 @@ struct ReadPreferenceSetting {
}
/**
- * Serializes this ReadPreferenceSetting as a BSON document.
+ * Serializes this ReadPreferenceSetting as an inner BSON document. (The document inside a
+ * $readPreference element)
*/
- BSONObj toBSON() const;
+ void toInnerBSON(BSONObjBuilder* builder) const;
+ BSONObj toInnerBSON() const {
+ BSONObjBuilder bob;
+ toInnerBSON(&bob);
+ return bob.obj();
+ }
/**
- * Describes this ReadPreferenceSetting as a string.
+ * Serializes this ReadPreferenceSetting as a containing BSON document. (The document containing
+ * a $readPreference element)
+ *
+ * Will not add the $readPreference element if the read preference is PrimaryOnly.
*/
- std::string toString() const;
+ void toContainingBSON(BSONObjBuilder* builder) const {
+ if (!canRunOnSecondary())
+ return; // Write nothing since default is fine.
+ BSONObjBuilder inner(builder->subobjStart("$readPreference"));
+ toInnerBSON(&inner);
+ }
+ BSONObj toContainingBSON() const {
+ BSONObjBuilder bob;
+ toContainingBSON(&bob);
+ return bob.obj();
+ }
/**
* Parses a ReadPreferenceSetting from a BSON document of the form:
@@ -161,7 +184,25 @@ struct ReadPreferenceSetting {
* further validation is performed on it other than checking that it is an array, and that it is
* empty if 'mode' is 'primary'.
*/
- static StatusWith<ReadPreferenceSetting> fromBSON(const BSONObj& readPrefSettingObj);
+ static StatusWith<ReadPreferenceSetting> fromInnerBSON(const BSONObj& readPrefSettingObj);
+ static StatusWith<ReadPreferenceSetting> fromInnerBSON(const BSONElement& readPrefSettingObj);
+
+ /**
+ * Parses a ReadPreference setting from an object that may contain a $readPreference object
+ * field with the contents described in fromInnerObject(). If the field is missing, returns the
+ * default read preference.
+ */
+ static StatusWith<ReadPreferenceSetting> fromContainingBSON(
+ const BSONObj& obj, ReadPreference defaultReadPref = ReadPreference::PrimaryOnly);
+
+ /**
+ * Describes this ReadPreferenceSetting as a string.
+ */
+ std::string toString() const;
+
+ bool canRunOnSecondary() const {
+ return pref != ReadPreference::PrimaryOnly;
+ }
ReadPreference pref;
TagSet tags;
diff --git a/src/mongo/client/read_preference_test.cpp b/src/mongo/client/read_preference_test.cpp
index dcf20107433..fda208ef45f 100644
--- a/src/mongo/client/read_preference_test.cpp
+++ b/src/mongo/client/read_preference_test.cpp
@@ -39,7 +39,7 @@ using namespace mongo;
static const Seconds kMinMaxStaleness = ReadPreferenceSetting::kMinimalMaxStalenessValue;
void checkParse(const BSONObj& rpsObj, const ReadPreferenceSetting& expected) {
- const auto swRps = ReadPreferenceSetting::fromBSON(rpsObj);
+ const auto swRps = ReadPreferenceSetting::fromInnerBSON(rpsObj);
ASSERT_OK(swRps.getStatus());
const auto rps = swRps.getValue();
ASSERT_TRUE(rps.equals(expected));
@@ -92,12 +92,18 @@ TEST(ReadPreferenceSetting, ParseValid) {
}
void checkParseFails(const BSONObj& rpsObj) {
- auto swRps = ReadPreferenceSetting::fromBSON(rpsObj);
+ auto swRps = ReadPreferenceSetting::fromInnerBSON(rpsObj);
ASSERT_NOT_OK(swRps.getStatus());
}
void checkParseFailsWithError(const BSONObj& rpsObj, ErrorCodes::Error error) {
- auto swRps = ReadPreferenceSetting::fromBSON(rpsObj);
+ auto swRps = ReadPreferenceSetting::fromInnerBSON(rpsObj);
+ ASSERT_NOT_OK(swRps.getStatus());
+ ASSERT_EQUALS(swRps.getStatus().code(), error);
+}
+
+void checkParseContainerFailsWithError(const BSONObj& rpsObj, ErrorCodes::Error error) {
+ auto swRps = ReadPreferenceSetting::fromContainingBSON(rpsObj);
ASSERT_NOT_OK(swRps.getStatus());
ASSERT_EQUALS(swRps.getStatus().code(), error);
}
@@ -169,10 +175,16 @@ TEST(ReadPreferenceSetting, ParseInvalid) {
<< "secondary"
<< "maxStalenessSeconds"
<< Seconds::max().count()));
+
+ checkParseContainerFailsWithError(BSON("$query" << BSON("pang"
+ << "pong")
+ << "$readPreference"
+ << 2),
+ ErrorCodes::TypeMismatch);
}
void checkRoundtrip(const ReadPreferenceSetting& rps) {
- auto parsed = ReadPreferenceSetting::fromBSON(rps.toBSON());
+ auto parsed = ReadPreferenceSetting::fromInnerBSON(rps.toInnerBSON());
ASSERT_OK(parsed.getStatus());
ASSERT_TRUE(parsed.getValue().equals(rps));
}
diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h
index a6a53116bac..780af5827cc 100644
--- a/src/mongo/db/commands.h
+++ b/src/mongo/db/commands.h
@@ -63,10 +63,6 @@ namespace mutablebson {
class Document;
} // namespace mutablebson
-namespace rpc {
-class ServerSelectionMetadata;
-} // namespace rpc
-
/**
* Serves as a base for server commands. See the constructor for more details.
*/
@@ -483,8 +479,8 @@ public:
arg == "$gleStats" || //
arg == "$oplogQueryData" || //
arg == "$queryOptions" || //
+ arg == "$readPreference" || //
arg == "$replData" || //
- arg == "$ssm" || //
arg == "logicalTime" || //
arg == "maxTimeMS" || //
arg == "readConcern" || //
diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp
index 05600734aa7..8751e871a82 100644
--- a/src/mongo/db/commands/dbcommands.cpp
+++ b/src/mongo/db/commands/dbcommands.cpp
@@ -102,7 +102,6 @@
#include "mongo/rpc/metadata/logical_time_metadata.h"
#include "mongo/rpc/metadata/oplog_query_metadata.h"
#include "mongo/rpc/metadata/repl_set_metadata.h"
-#include "mongo/rpc/metadata/server_selection_metadata.h"
#include "mongo/rpc/metadata/sharding_metadata.h"
#include "mongo/rpc/metadata/tracking_metadata.h"
#include "mongo/rpc/reply_builder_interface.h"
@@ -1602,8 +1601,8 @@ void mongo::execCommandDatabase(OperationContext* opCtx,
{
bool commandCanRunOnSecondary = command->slaveOk();
- bool commandIsOverriddenToRunOnSecondary = command->slaveOverrideOk() &&
- rpc::ServerSelectionMetadata::get(opCtx).canRunOnSecondary();
+ bool commandIsOverriddenToRunOnSecondary =
+ command->slaveOverrideOk() && ReadPreferenceSetting::get(opCtx).canRunOnSecondary();
bool iAmStandalone = !opCtx->writesAreReplicated();
bool canRunHere = iAmPrimary || commandCanRunOnSecondary ||
diff --git a/src/mongo/db/commands/explain_cmd.cpp b/src/mongo/db/commands/explain_cmd.cpp
index 59004e87e12..b62fe416ac7 100644
--- a/src/mongo/db/commands/explain_cmd.cpp
+++ b/src/mongo/db/commands/explain_cmd.cpp
@@ -31,7 +31,6 @@
#include "mongo/db/commands.h"
#include "mongo/db/query/explain.h"
#include "mongo/db/repl/replication_coordinator_global.h"
-#include "mongo/rpc/metadata/server_selection_metadata.h"
#include "mongo/util/mongoutils/str.h"
namespace mongo {
@@ -150,7 +149,7 @@ public:
bool commandCanRunOnSecondary = commToExplain->slaveOk();
bool commandIsOverriddenToRunOnSecondary = commToExplain->slaveOverrideOk() &&
- rpc::ServerSelectionMetadata::get(opCtx).canRunOnSecondary();
+ ReadPreferenceSetting::get(opCtx).canRunOnSecondary();
bool iAmStandalone = !opCtx->writesAreReplicated();
const bool canRunHere = iAmPrimary || commandCanRunOnSecondary ||
diff --git a/src/mongo/rpc/SConscript b/src/mongo/rpc/SConscript
index 3d7982e6a34..47e3af75eee 100644
--- a/src/mongo/rpc/SConscript
+++ b/src/mongo/rpc/SConscript
@@ -134,7 +134,6 @@ env.Clone().InjectModule("enterprise").Library(
'metadata/config_server_metadata.cpp',
'metadata/egress_metadata_hook_list.cpp',
'metadata/logical_time_metadata.cpp',
- 'metadata/server_selection_metadata.cpp',
'metadata/sharding_metadata.cpp',
'metadata/repl_set_metadata.cpp',
'metadata/oplog_query_metadata.cpp',
@@ -158,9 +157,9 @@ env.CppUnitTest(
'rpc_metadata_test',
],
source=[
+ 'metadata_test.cpp',
'metadata/egress_metadata_hook_list_test.cpp',
'metadata/logical_time_metadata_test.cpp',
- 'metadata/server_selection_metadata_test.cpp',
'metadata/sharding_metadata_test.cpp',
'metadata/tracking_metadata_test.cpp',
],
diff --git a/src/mongo/rpc/command_request.cpp b/src/mongo/rpc/command_request.cpp
index 08bb54e91fe..54cb58a4313 100644
--- a/src/mongo/rpc/command_request.cpp
+++ b/src/mongo/rpc/command_request.cpp
@@ -93,13 +93,24 @@ CommandRequest::CommandRequest(const Message* message) : _message(message) {
_commandArgs.firstElementFieldName() == _commandName);
// OP_COMMAND is only used when communicating with 3.4 nodes and they serialize their metadata
- // fields differently. We do all up- and down-conversion here so that the rest of the code only
- // has to deal with the current format.
+ // fields differently. We do all up-conversion here so that the rest of the code only has to
+ // deal with the current format.
uassertStatusOK(cur.readAndAdvance<>(&obj));
BSONObjBuilder metadataBuilder;
for (auto elem : obj.val) {
if (elem.fieldNameStringData() == "configsvr") {
metadataBuilder.appendAs(elem, "$configServerState");
+ } else if (elem.fieldNameStringData() == "$ssm") {
+ auto ssmObj = elem.Obj();
+ if (auto readPrefElem = ssmObj["$readPreference"]) {
+ // Promote the read preference to the top level.
+ metadataBuilder.append(readPrefElem);
+ } else if (ssmObj["$secondaryOk"].trueValue()) {
+ // Convert secondaryOk to equivalent read preference if none was explicitly
+ // provided.
+ ReadPreferenceSetting(ReadPreference::SecondaryPreferred)
+ .toContainingBSON(&metadataBuilder);
+ }
} else {
metadataBuilder.append(elem);
}
diff --git a/src/mongo/rpc/command_request_builder.cpp b/src/mongo/rpc/command_request_builder.cpp
index 34f6daa0888..9b784db1841 100644
--- a/src/mongo/rpc/command_request_builder.cpp
+++ b/src/mongo/rpc/command_request_builder.cpp
@@ -32,6 +32,7 @@
#include <utility>
+#include "mongo/client/read_preference.h"
#include "mongo/stdx/memory.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/net/message.h"
@@ -71,12 +72,18 @@ CommandRequestBuilder& CommandRequestBuilder::setCommandArgs(BSONObj commandArgs
CommandRequestBuilder& CommandRequestBuilder::setMetadata(BSONObj metadata) {
invariant(_state == State::kMetadata);
// OP_COMMAND is only used when communicating with 3.4 nodes and they serialize their metadata
- // fields differently. We do all up- and down-conversion here so that the rest of the code only
- // has to deal with the current format.
+ // fields differently. We do all down-conversion here so that the rest of the code only has to
+ // deal with the current format.
BSONObjBuilder bob(_builder);
for (auto elem : metadata) {
if (elem.fieldNameStringData() == "$configServerState") {
bob.appendAs(elem, "configsvr");
+ } else if (elem.fieldNameStringData() == "$readPreference") {
+ BSONObjBuilder ssmBuilder(bob.subobjStart("$ssm"));
+ ssmBuilder.append(elem);
+ ssmBuilder.append(
+ "$secondaryOk",
+ uassertStatusOK(ReadPreferenceSetting::fromInnerBSON(elem)).canRunOnSecondary());
} else {
bob.append(elem);
}
diff --git a/src/mongo/rpc/metadata.cpp b/src/mongo/rpc/metadata.cpp
index 5dc5f65d169..77545303ff7 100644
--- a/src/mongo/rpc/metadata.cpp
+++ b/src/mongo/rpc/metadata.cpp
@@ -43,7 +43,6 @@
#include "mongo/rpc/metadata/client_metadata_ismaster.h"
#include "mongo/rpc/metadata/config_server_metadata.h"
#include "mongo/rpc/metadata/logical_time_metadata.h"
-#include "mongo/rpc/metadata/server_selection_metadata.h"
#include "mongo/rpc/metadata/sharding_metadata.h"
#include "mongo/rpc/metadata/tracking_metadata.h"
@@ -76,7 +75,7 @@ BSONObj makeEmptyMetadata() {
}
void readRequestMetadata(OperationContext* opCtx, const BSONObj& metadataObj) {
- BSONElement ssmElem;
+ BSONElement readPreferenceElem;
BSONElement auditElem;
BSONElement configSvrElem;
BSONElement trackingElem;
@@ -85,8 +84,8 @@ void readRequestMetadata(OperationContext* opCtx, const BSONObj& metadataObj) {
for (const auto& metadataElem : metadataObj) {
auto fieldName = metadataElem.fieldNameStringData();
- if (fieldName == ServerSelectionMetadata::fieldName()) {
- ssmElem = metadataElem;
+ if (fieldName == "$readPreference") {
+ readPreferenceElem = metadataElem;
} else if (fieldName == AuditMetadata::fieldName()) {
auditElem = metadataElem;
} else if (fieldName == ConfigServerMetadata::fieldName()) {
@@ -100,8 +99,10 @@ void readRequestMetadata(OperationContext* opCtx, const BSONObj& metadataObj) {
}
}
- ServerSelectionMetadata::get(opCtx) =
- uassertStatusOK(ServerSelectionMetadata::readFromMetadata(ssmElem));
+ if (readPreferenceElem) {
+ ReadPreferenceSetting::get(opCtx) =
+ uassertStatusOK(ReadPreferenceSetting::fromInnerBSON(readPreferenceElem));
+ }
AuditMetadata::get(opCtx) = uassertStatusOK(AuditMetadata::readFromMetadata(auditElem));
@@ -143,20 +144,44 @@ void readRequestMetadata(OperationContext* opCtx, const BSONObj& metadataObj) {
CommandAndMetadata upconvertRequestMetadata(BSONObj legacyCmdObj, int queryFlags) {
// We can reuse the same metadata BOB for every upconvert call, but we need to keep
// making new command BOBs as each metadata bob will need to remove fields. We can not use
- // mutablebson here because the ServerSelectionMetadata upconvert routine performs
+ // mutablebson here because the ReadPreference upconvert routine performs
// manipulations (replacing a root with its child) that mutablebson doesn't
// support.
- BSONObjBuilder metadataBob;
- // Ordering is important here - ServerSelectionMetadata must be upconverted
- // first, then AuditMetadata.
- BSONObjBuilder ssmCommandBob;
- uassertStatusOK(
- ServerSelectionMetadata::upconvert(legacyCmdObj, queryFlags, &ssmCommandBob, &metadataBob));
+ auto readPrefContainer = BSONObj();
+ const StringData firstFieldName = legacyCmdObj.firstElementFieldName();
+ if (firstFieldName == "$query" || firstFieldName == "query") {
+ // Commands sent over OP_QUERY specify read preference by putting it at the top level and
+ // putting the command in a nested field called either query or $query.
+
+ // Check if legacyCommand has an invalid $maxTimeMS option.
+ uassert(ErrorCodes::InvalidOptions,
+ "cannot use $maxTimeMS query option with commands; use maxTimeMS command option "
+ "instead",
+ !legacyCmdObj.hasField("$maxTimeMS"));
+ readPrefContainer = legacyCmdObj;
+ legacyCmdObj = legacyCmdObj.firstElement().Obj().getOwned();
+ } else if (auto queryOptions = legacyCmdObj["$queryOptions"]) {
+ // Mongos rewrites commands with $readPreference to put it in a field nested inside of
+ // $queryOptions. Its command implementations often forward commands in that format to
+ // shards. This function is responsible for rewriting it to a format that the shards
+ // understand.
+ readPrefContainer = queryOptions.Obj().getOwned();
+ legacyCmdObj = legacyCmdObj.removeField("$queryOptions");
+ }
+ BSONObjBuilder metadataBob;
+ if (auto readPref = readPrefContainer["$readPreference"]) {
+ metadataBob.append(readPref);
+ } else if (queryFlags & QueryOption_SlaveOk) {
+ ReadPreferenceSetting(ReadPreference::SecondaryPreferred).toContainingBSON(&metadataBob);
+ }
+
+ // Ordering is important here - AuditMetadata::upconvert() expects the above up-conversion to
+ // already be done.
BSONObjBuilder auditCommandBob;
uassertStatusOK(
- AuditMetadata::upconvert(ssmCommandBob.done(), queryFlags, &auditCommandBob, &metadataBob));
+ AuditMetadata::upconvert(legacyCmdObj, queryFlags, &auditCommandBob, &metadataBob));
return std::make_tuple(auditCommandBob.obj(), metadataBob.obj());
}
@@ -165,16 +190,28 @@ LegacyCommandAndFlags downconvertRequestMetadata(BSONObj cmdObj, BSONObj metadat
int legacyQueryFlags = 0;
BSONObjBuilder auditCommandBob;
// Ordering is important here - AuditingMetadata must be downconverted first,
- // then ServerSelectionMetadata.
+ // then ReadPreference.
uassertStatusOK(
AuditMetadata::downconvert(cmdObj, metadata, &auditCommandBob, &legacyQueryFlags));
- BSONObjBuilder ssmCommandBob;
- uassertStatusOK(ServerSelectionMetadata::downconvert(
- auditCommandBob.done(), metadata, &ssmCommandBob, &legacyQueryFlags));
+ auto readPref = metadata["$readPreference"];
+ if (!readPref)
+ readPref = cmdObj["$readPreference"];
+
+ if (readPref) {
+ BSONObjBuilder bob;
+ bob.append("$query", cmdObj);
+ bob.append(readPref);
+ cmdObj = bob.obj();
+
+ auto parsed = ReadPreferenceSetting::fromInnerBSON(readPref);
+ if (parsed.isOK() && parsed.getValue().canRunOnSecondary()) {
+ legacyQueryFlags |= QueryOption_SlaveOk;
+ }
+ }
- return std::make_tuple(ssmCommandBob.obj(), std::move(legacyQueryFlags));
+ return std::make_tuple(cmdObj, std::move(legacyQueryFlags));
}
CommandReplyWithMetadata upconvertReplyMetadata(const BSONObj& legacyReply) {
diff --git a/src/mongo/rpc/metadata/server_selection_metadata.cpp b/src/mongo/rpc/metadata/server_selection_metadata.cpp
deleted file mode 100644
index 533ebd650e1..00000000000
--- a/src/mongo/rpc/metadata/server_selection_metadata.cpp
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * Copyright (C) 2015 MongoDB Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the GNU Affero General Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/rpc/metadata/server_selection_metadata.h"
-
-#include <tuple>
-#include <utility>
-
-#include "mongo/base/status_with.h"
-#include "mongo/bson/util/bson_extract.h"
-#include "mongo/client/dbclientinterface.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/db/operation_context.h"
-#include "mongo/util/assert_util.h"
-
-namespace mongo {
-namespace rpc {
-
-
-namespace {
-
-// Symbolic constant for the "$readPreference" metadata field. The field should be of Object type
-// when present.
-const char kReadPreferenceFieldName[] = "$readPreference";
-
-const char kQueryOptionsFieldName[] = "$queryOptions";
-
-const char kDollarQueryWrapper[] = "$query";
-const char kQueryWrapper[] = "query";
-
-/**
- * Utility to unwrap a '$query' or 'query' wrapped command object. The first element of the
- * returned tuple indicates whether the command was unwrapped, and the second element is either
- * the unwrapped command (if it was wrapped), or the original command if it was not.
- */
-StatusWith<std::tuple<bool, BSONObj>> unwrapCommand(const BSONObj& maybeWrapped) {
- const auto firstElFieldName = maybeWrapped.firstElementFieldName();
-
- if ((firstElFieldName != StringData(kDollarQueryWrapper)) &&
- (firstElFieldName != StringData(kQueryWrapper))) {
- return std::make_tuple(false, maybeWrapped);
- }
-
- BSONElement inner;
- auto extractStatus =
- bsonExtractTypedField(maybeWrapped, firstElFieldName, mongo::Object, &inner);
-
- if (!extractStatus.isOK()) {
- return extractStatus;
- }
-
- return std::make_tuple(true, inner.Obj());
-}
-
-/**
- * Reads a top-level $readPreference field from a wrapped command.
- */
-Status extractWrappedReadPreference(const BSONObj& wrappedCommand, BSONObjBuilder* metadataBob) {
- BSONElement readPrefEl;
- auto rpExtractStatus =
- bsonExtractTypedField(wrappedCommand, kReadPreferenceFieldName, mongo::Object, &readPrefEl);
- if (rpExtractStatus.isOK()) {
- metadataBob->append(readPrefEl);
- } else if (rpExtractStatus != ErrorCodes::NoSuchKey) {
- return rpExtractStatus;
- }
-
- return Status::OK();
-}
-
-/**
- * Reads a $readPreference from a $queryOptions subobject, if it exists, and writes it to
- * metadataBob. Writes out the original command excluding the $queryOptions subobject.
- */
-Status extractUnwrappedReadPreference(const BSONObj& unwrappedCommand,
- BSONObjBuilder* commandBob,
- BSONObjBuilder* metadataBob) {
- BSONElement queryOptionsEl;
- BSONElement readPrefEl;
-
- auto queryOptionsExtractStatus = bsonExtractTypedField(
- unwrappedCommand, kQueryOptionsFieldName, mongo::Object, &queryOptionsEl);
-
- // If there is no queryOptions subobject, we write out the command and return.
- if (queryOptionsExtractStatus == ErrorCodes::NoSuchKey) {
- commandBob->appendElements(unwrappedCommand);
- return Status::OK();
- } else if (!queryOptionsExtractStatus.isOK()) {
- return queryOptionsExtractStatus;
- }
-
- // Write out the command excluding the $queryOptions field.
- for (const auto& elem : unwrappedCommand) {
- if (elem.fieldNameStringData() != kQueryOptionsFieldName) {
- commandBob->append(elem);
- }
- }
-
- auto rpExtractStatus = bsonExtractTypedField(
- queryOptionsEl.embeddedObject(), kReadPreferenceFieldName, mongo::Object, &readPrefEl);
-
- // If there is a $queryOptions field, we expect there to be a $readPreference.
- if (!rpExtractStatus.isOK()) {
- return rpExtractStatus;
- }
-
- metadataBob->append(readPrefEl);
- return Status::OK();
-}
-
-} // namespace
-
-// Symbolic constant for the "$secondaryOk" metadata field. This field should be of boolean or
-// numeric type, and is treated as a boolean.
-const char ServerSelectionMetadata::kSecondaryOkFieldName[] = "$secondaryOk";
-
-const OperationContext::Decoration<ServerSelectionMetadata> ServerSelectionMetadata::get =
- OperationContext::declareDecoration<ServerSelectionMetadata>();
-
-ServerSelectionMetadata::ServerSelectionMetadata(
- bool secondaryOk, boost::optional<ReadPreferenceSetting> readPreference)
- : _secondaryOk(secondaryOk), _readPreference(std::move(readPreference)) {}
-
-StatusWith<ServerSelectionMetadata> ServerSelectionMetadata::readFromMetadata(
- const BSONObj& metadataObj) {
- return readFromMetadata(metadataObj.getField(fieldName()));
-}
-
-StatusWith<ServerSelectionMetadata> ServerSelectionMetadata::readFromMetadata(
- const BSONElement& metadataElem) {
- if (metadataElem.eoo()) {
- return ServerSelectionMetadata{};
- } else if (metadataElem.type() != mongo::Object) {
- return {ErrorCodes::TypeMismatch,
- str::stream() << "ServerSelectionMetadata element has incorrect type: expected"
- << mongo::Object
- << " but got "
- << metadataElem.type()};
- }
-
- bool secondaryOk = false;
- boost::optional<ReadPreferenceSetting> readPreference;
- BSONElement rpElem;
- for (const auto& ssmElem : metadataElem.Obj()) {
- auto ssmElemFieldName = ssmElem.fieldNameStringData();
- if (ssmElemFieldName == kSecondaryOkFieldName) {
- secondaryOk = ssmElem.trueValue();
- } else if (ssmElemFieldName == kReadPreferenceFieldName) {
- if (ssmElem.type() != mongo::Object) {
- return Status(ErrorCodes::TypeMismatch,
- str::stream() << "ReadPreference has incorrect type: expected"
- << mongo::Object
- << "but got"
- << metadataElem.type());
- }
- auto parsedRps = ReadPreferenceSetting::fromBSON(ssmElem.Obj());
- if (!parsedRps.isOK()) {
- return parsedRps.getStatus();
- }
- readPreference.emplace(std::move(parsedRps.getValue()));
- }
- }
-
- return ServerSelectionMetadata(secondaryOk, std::move(readPreference));
-}
-
-Status ServerSelectionMetadata::writeToMetadata(BSONObjBuilder* metadataBob) const {
- BSONObjBuilder ssmBob;
- if (isSecondaryOk()) {
- ssmBob.append(kSecondaryOkFieldName, 1);
- }
-
- if (getReadPreference()) {
- ssmBob.append(kReadPreferenceFieldName, getReadPreference()->toBSON());
- }
-
- auto ssm = ssmBob.done();
- if (!ssm.isEmpty()) {
- metadataBob->append(fieldName(), ssm);
- }
-
- return Status::OK();
-}
-
-BSONObj ServerSelectionMetadata::toBSON() const {
- BSONObjBuilder bob;
- writeToMetadata(&bob);
- return bob.obj();
-}
-
-Status ServerSelectionMetadata::downconvert(const BSONObj& command,
- const BSONObj& metadata,
- BSONObjBuilder* legacyCommand,
- int* legacyQueryFlags) {
- auto ssmElem = metadata.getField(fieldName());
- if (ssmElem.eoo()) {
- // slaveOk is false by default.
- *legacyQueryFlags &= ~mongo::QueryOption_SlaveOk;
- legacyCommand->appendElements(command);
- return Status::OK();
- } else if (ssmElem.type() != mongo::Object) {
- return {
- ErrorCodes::TypeMismatch,
- str::stream() << "ServerSelectionMetadata metadata element must be an object, but got "
- << typeName(ssmElem.type())};
- }
-
- auto ssmObj = ssmElem.Obj();
- BSONElement secondaryOkElem;
- BSONElement readPreferenceElem;
-
- for (auto&& el : ssmObj) {
- auto fname = el.fieldNameStringData();
- if (fname == kSecondaryOkFieldName) {
- secondaryOkElem = std::move(el);
- } else if (fname == kReadPreferenceFieldName) {
- readPreferenceElem = std::move(el);
- }
- }
-
- if (!secondaryOkElem.eoo() && secondaryOkElem.trueValue()) {
- *legacyQueryFlags |= mongo::QueryOption_SlaveOk;
- } else {
- *legacyQueryFlags &= ~mongo::QueryOption_SlaveOk;
- }
-
- if (!readPreferenceElem.eoo()) {
- // Use 'query' to wrap query, then append read preference.
-
- // NOTE(amidvidy): Oddly, the _isSecondaryQuery implementation in dbclient_rs does
- // not unwrap the query properly - it only checks for 'query', and not
- // '$query'. We should probably standardize on one - drivers use '$query',
- // and the shell uses 'query'. See SERVER-18705 for details.
-
- // TODO: this may need to use the $queryOptions hack on mongos.
- legacyCommand->append(kQueryWrapper, command);
- legacyCommand->append(readPreferenceElem);
- } else {
- legacyCommand->appendElements(command);
- }
-
- return Status::OK();
-}
-
-Status ServerSelectionMetadata::upconvert(const BSONObj& legacyCommand,
- const int legacyQueryFlags,
- BSONObjBuilder* commandBob,
- BSONObjBuilder* metadataBob) {
- // The secondaryOK option is equivalent to the slaveOk bit being set on legacy commands.
- BSONObjBuilder ssmBob;
- if (legacyQueryFlags & QueryOption_SlaveOk) {
- ssmBob.append(kSecondaryOkFieldName, 1);
- }
-
- // First we need to check if we have a wrapped command. That is, a command of the form
- // {'$query': { 'commandName': 1, ...}, '$someOption': 5, ....}. Curiously, the field name
- // of the wrapped query can be either '$query', or 'query'.
- auto swUnwrapped = unwrapCommand(legacyCommand);
- if (!swUnwrapped.isOK()) {
- return swUnwrapped.getStatus();
- }
-
- BSONObj maybeUnwrapped;
- bool wasWrapped;
- std::tie(wasWrapped, maybeUnwrapped) = swUnwrapped.getValue();
-
- if (wasWrapped) {
- // Check if legacyCommand has an invalid $maxTimeMS option.
- // TODO: Move this check elsewhere when we handle upconverting/downconverting maxTimeMS.
- if (legacyCommand.hasField("$maxTimeMS")) {
- return Status(ErrorCodes::InvalidOptions,
- "cannot use $maxTimeMS query option with "
- "commands; use maxTimeMS command option "
- "instead");
- }
-
- // If the command was wrapped, we can write out the upconverted command now, as there
- // is nothing else we need to remove from it.
- commandBob->appendElements(maybeUnwrapped);
-
- auto status = extractWrappedReadPreference(legacyCommand, &ssmBob);
- if (!status.isOK()) {
- return status;
- }
- } else {
- // If the command was not wrapped, we need to check for a readPreference sent by mongos
- // on the $queryOptions field of the command. If it is set, we remove it from the
- // upconverted command, so we need to pass the command builder along.
-
- auto status = extractUnwrappedReadPreference(maybeUnwrapped, commandBob, &ssmBob);
- if (!status.isOK()) {
- return status;
- }
- }
-
- auto ssm = ssmBob.done();
- if (!ssm.isEmpty()) {
- metadataBob->append(fieldName(), ssm);
- }
- return Status::OK();
-}
-
-bool ServerSelectionMetadata::isSecondaryOk() const {
- return _secondaryOk;
-}
-
-const boost::optional<ReadPreferenceSetting>& ServerSelectionMetadata::getReadPreference() const {
- return _readPreference;
-}
-
-bool ServerSelectionMetadata::canRunOnSecondary() const {
- return _secondaryOk ||
- (_readPreference && (_readPreference->pref != ReadPreference::PrimaryOnly));
-}
-
-} // rpc
-} // mongo
diff --git a/src/mongo/rpc/metadata/server_selection_metadata.h b/src/mongo/rpc/metadata/server_selection_metadata.h
deleted file mode 100644
index 26983bf7533..00000000000
--- a/src/mongo/rpc/metadata/server_selection_metadata.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2015 MongoDB Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the GNU Affero General Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include <boost/optional.hpp>
-
-#include "mongo/base/disallow_copying.h"
-#include "mongo/client/read_preference.h"
-#include "mongo/db/operation_context.h"
-
-namespace mongo {
-class BSONObj;
-class BSONObjBuilder;
-class Status;
-template <typename T>
-class StatusWith;
-
-namespace rpc {
-
-/**
- * This class comprises the request metadata fields that concern server selection, that is,
- * the conditions on which servers can execute this operation.
- */
-class ServerSelectionMetadata {
- MONGO_DISALLOW_COPYING(ServerSelectionMetadata);
-
-public:
- static const char kSecondaryOkFieldName[];
- static const OperationContext::Decoration<ServerSelectionMetadata> get;
-
- ServerSelectionMetadata() = default;
-
- ServerSelectionMetadata(ServerSelectionMetadata&&) = default;
-
- ServerSelectionMetadata& operator=(ServerSelectionMetadata&&) = default;
-
- /**
- * Loads ServerSelectionMetadata from a metadata object.
- */
- static StatusWith<ServerSelectionMetadata> readFromMetadata(const BSONObj& metadataObj);
-
- static StatusWith<ServerSelectionMetadata> readFromMetadata(const BSONElement& metadataElem);
-
- /**
- * Writes this operation's ServerSelectionMetadata to a metadata object.
- */
- Status writeToMetadata(BSONObjBuilder* metadataBob) const;
-
- BSONObj toBSON() const;
-
- /**
- * Rewrites the ServerSelectionMetadata from the metadata object format to the legacy OP_QUERY
- * format. In particular, if secondaryOk is set, this will set QueryOption_SlaveOk
- * on the legacyQueryFlags. If a readPreference is set, the legacy command will be wrapped
- * in a 'query' element and a top-level $readPreference field will be set on the command.
- */
- static Status downconvert(const BSONObj& command,
- const BSONObj& metadata,
- BSONObjBuilder* legacyCommand,
- int* legacyQueryFlags);
-
- /**
- * Rewrites the ServerSelectionMetadata from the legacy OP_QUERY format to the metadata
- * object format.
- */
- static Status upconvert(const BSONObj& legacyCommand,
- const int legacyQueryFlags,
- BSONObjBuilder* commandBob,
- BSONObjBuilder* metadataBob);
- /**
- * Returns true if this operation has been explicitly overridden to run on a secondary.
- * This replaces previous usage of QueryOption_SlaveOk.
- */
- bool isSecondaryOk() const;
-
- /**
- * Returns the ReadPreference associated with this operation. See
- * mongo/client/read_preference.h for further details.
- */
- const boost::optional<ReadPreferenceSetting>& getReadPreference() const;
-
- /**
- * Returns true if this operation can run on secondary.
- */
- bool canRunOnSecondary() const;
-
- ServerSelectionMetadata(bool secondaryOk,
- boost::optional<ReadPreferenceSetting> readPreference);
-
- static StringData fieldName() {
- return "$ssm";
- }
-
-private:
- bool _secondaryOk{false};
- boost::optional<ReadPreferenceSetting> _readPreference{};
-};
-
-} // namespace rpc
-} // namespace mongo
diff --git a/src/mongo/rpc/metadata/server_selection_metadata_test.cpp b/src/mongo/rpc/metadata/server_selection_metadata_test.cpp
deleted file mode 100644
index 3775f489a06..00000000000
--- a/src/mongo/rpc/metadata/server_selection_metadata_test.cpp
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2015 MongoDB Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the GNU Affero General Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/platform/basic.h"
-
-#include <utility>
-
-#include "mongo/base/status.h"
-#include "mongo/client/dbclientinterface.h"
-#include "mongo/client/read_preference.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/rpc/metadata/server_selection_metadata.h"
-#include "mongo/unittest/unittest.h"
-
-namespace {
-using namespace mongo;
-using namespace mongo::rpc;
-using mongo::unittest::assertGet;
-
-ServerSelectionMetadata checkParse(const BSONObj& metadata) {
- return assertGet(ServerSelectionMetadata::readFromMetadata(metadata));
-}
-
-TEST(ServerSelectionMetadata, ReadFromMetadata) {
- {
- // Empty object - should work just fine.
- auto ss = checkParse(BSONObj());
- ASSERT_FALSE(ss.isSecondaryOk());
- ASSERT_FALSE(ss.getReadPreference().is_initialized());
- }
- {
- // Set secondaryOk but not readPreference.
- auto ss = checkParse(BSON("$ssm" << BSON("$secondaryOk" << 1)));
- ASSERT_TRUE(ss.isSecondaryOk());
- ASSERT_FALSE(ss.getReadPreference().is_initialized());
- }
- {
- // Set readPreference but not secondaryOk.
- auto ss = checkParse(BSON("$ssm" << BSON("$readPreference" << BSON("mode"
- << "primary"))));
- ASSERT_FALSE(ss.isSecondaryOk());
- ASSERT_TRUE(ss.getReadPreference().is_initialized());
- ASSERT_TRUE(ss.getReadPreference()->pref == ReadPreference::PrimaryOnly);
- }
- {
- // Set both.
- auto ss = checkParse(BSON("$ssm" << BSON("$secondaryOk" << 1 << "$readPreference"
- << BSON("mode"
- << "secondaryPreferred"))));
- ASSERT_TRUE(ss.isSecondaryOk());
- ASSERT_TRUE(ss.getReadPreference()->pref == ReadPreference::SecondaryPreferred);
- }
-}
-
-void checkUpconvert(const BSONObj& legacyCommand,
- const int legacyQueryFlags,
- const BSONObj& upconvertedCommand,
- const BSONObj& upconvertedMetadata) {
- BSONObjBuilder upconvertedCommandBob;
- BSONObjBuilder upconvertedMetadataBob;
- auto convertStatus = ServerSelectionMetadata::upconvert(
- legacyCommand, legacyQueryFlags, &upconvertedCommandBob, &upconvertedMetadataBob);
- ASSERT_OK(convertStatus);
- // We don't care about the order of the fields in the metadata object
- const auto sorted = [](const BSONObj& obj) {
- BSONObjIteratorSorted iter(obj);
- BSONObjBuilder bob;
- while (iter.more()) {
- bob.append(iter.next());
- }
- return bob.obj();
- };
-
- ASSERT_BSONOBJ_EQ(upconvertedCommand, upconvertedCommandBob.done());
- ASSERT_BSONOBJ_EQ(sorted(upconvertedMetadata), sorted(upconvertedMetadataBob.done()));
-}
-
-TEST(ServerSelectionMetadata, UpconvertValidMetadata) {
- // Wrapped in $query, with readPref and slaveOk bit set.
- checkUpconvert(
- BSON("$query" << BSON("ping" << 1) << "$readPreference" << BSON("mode"
- << "secondary")),
- mongo::QueryOption_SlaveOk,
- BSON("ping" << 1),
- BSON("$ssm" << BSON("$secondaryOk" << 1 << "$readPreference" << BSON("mode"
- << "secondary"))));
-
- // Wrapped in 'query', with readPref.
- checkUpconvert(BSON("query" << BSON("pong" << 1 << "foo"
- << "bar")
- << "$readPreference"
- << BSON("mode"
- << "primary"
- << "tags"
- << BSON("dc"
- << "ny"))),
- 0,
- BSON("pong" << 1 << "foo"
- << "bar"),
- BSON("$ssm" << BSON("$readPreference" << BSON("mode"
- << "primary"
- << "tags"
- << BSON("dc"
- << "ny")))));
- // Unwrapped, no readPref, no slaveOk
- checkUpconvert(BSON("ping" << 1), 0, BSON("ping" << 1), BSONObj());
-
- // Readpref wrapped in $queryOptions
- checkUpconvert(BSON("pang"
- << "pong"
- << "$queryOptions"
- << BSON("$readPreference" << BSON("mode"
- << "nearest"
- << "tags"
- << BSON("rack"
- << "city")))),
- 0,
- BSON("pang"
- << "pong"),
- BSON("$ssm" << BSON("$readPreference" << BSON("mode"
- << "nearest"
- << "tags"
- << BSON("rack"
- << "city")))));
-}
-
-void checkUpconvertFails(const BSONObj& legacyCommand, ErrorCodes::Error error) {
- BSONObjBuilder upconvertedCommandBob;
- BSONObjBuilder upconvertedMetadataBob;
- auto upconvertStatus = ServerSelectionMetadata::upconvert(
- legacyCommand, 0, &upconvertedCommandBob, &upconvertedMetadataBob);
- ASSERT_NOT_OK(upconvertStatus);
- ASSERT_EQ(upconvertStatus.code(), error);
-}
-
-TEST(ServerSelectionMetadata, UpconvertInvalidMetadata) {
- // $readPreference not an object.
- checkUpconvertFails(BSON("$query" << BSON("pang"
- << "pong")
- << "$readPreference"
- << 2),
- ErrorCodes::TypeMismatch);
-
- // has $maxTimeMS option
- checkUpconvertFails(BSON("query" << BSON("foo"
- << "bar")
- << "$maxTimeMS"
- << 200),
- ErrorCodes::InvalidOptions);
- checkUpconvertFails(BSON("$query" << BSON("foo"
- << "bar")
- << "$maxTimeMS"
- << 200),
- ErrorCodes::InvalidOptions);
-
- // has $queryOptions field, but invalid $readPreference
- checkUpconvertFails(BSON("ping"
- << "pong"
- << "$queryOptions"
- << BSON("$readPreference" << 1.2)),
- ErrorCodes::TypeMismatch);
-
- // has $queryOptions field, but no $readPreference
- checkUpconvertFails(BSON("ping"
- << "pong"
- << "$queryOptions"
- << BSONObj()),
- ErrorCodes::NoSuchKey);
-
- // invalid wrapped query
- checkUpconvertFails(BSON("$query" << 1), ErrorCodes::TypeMismatch);
- checkUpconvertFails(BSON("$query"
- << ""),
- ErrorCodes::TypeMismatch);
- checkUpconvertFails(BSON("query" << 1), ErrorCodes::TypeMismatch);
- checkUpconvertFails(BSON("query"
- << ""),
- ErrorCodes::TypeMismatch);
-}
-
-} // namespace
diff --git a/src/mongo/rpc/metadata_test.cpp b/src/mongo/rpc/metadata_test.cpp
new file mode 100644
index 00000000000..1b538d4717d
--- /dev/null
+++ b/src/mongo/rpc/metadata_test.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include <utility>
+
+#include "mongo/client/dbclientinterface.h"
+#include "mongo/db/jsobj.h"
+#include "mongo/rpc/metadata.h"
+#include "mongo/unittest/unittest.h"
+
+namespace {
+using namespace mongo;
+using namespace mongo::rpc;
+using mongo::unittest::assertGet;
+
+void checkUpconvert(const BSONObj& legacyCommand,
+ const int legacyQueryFlags,
+ const BSONObj& upconvertedCommand,
+ const BSONObj& upconvertedMetadata) {
+
+ auto converted = upconvertRequestMetadata(legacyCommand, legacyQueryFlags);
+ // We don't care about the order of the fields in the metadata object
+ const auto sorted = [](const BSONObj& obj) {
+ BSONObjIteratorSorted iter(obj);
+ BSONObjBuilder bob;
+ while (iter.more()) {
+ bob.append(iter.next());
+ }
+ return bob.obj();
+ };
+
+ ASSERT_BSONOBJ_EQ(upconvertedCommand, std::get<0>(converted));
+ ASSERT_BSONOBJ_EQ(sorted(upconvertedMetadata), sorted(std::get<1>(converted)));
+}
+
+TEST(Metadata, UpconvertValidMetadata) {
+ // Wrapped in $query, with readPref and slaveOk bit set.
+ checkUpconvert(BSON("$query" << BSON("ping" << 1) << //
+ "$readPreference"
+ << BSON("mode"
+ << "secondary")),
+ mongo::QueryOption_SlaveOk,
+ BSON("ping" << 1),
+ BSON("$readPreference" << BSON("mode"
+ << "secondary")));
+
+ // Wrapped in 'query', with readPref.
+ checkUpconvert(BSON("query" << BSON("pong" << 1 << "foo"
+ << "bar")
+ << "$readPreference"
+ << BSON("mode"
+ << "primary"
+ << "tags"
+ << BSON("dc"
+ << "ny"))),
+ 0,
+ BSON("pong" << 1 << "foo"
+ << "bar"),
+ BSON("$readPreference" << BSON("mode"
+ << "primary"
+ << "tags"
+ << BSON("dc"
+ << "ny"))));
+ // Unwrapped, no readPref, no slaveOk
+ checkUpconvert(BSON("ping" << 1), 0, BSON("ping" << 1), BSONObj());
+
+ // Readpref wrapped in $queryOptions
+ checkUpconvert(BSON("pang"
+ << "pong"
+ << "$queryOptions"
+ << BSON("$readPreference" << BSON("mode"
+ << "nearest"
+ << "tags"
+ << BSON("rack"
+ << "city")))),
+ 0,
+ BSON("pang"
+ << "pong"),
+ BSON("$readPreference" << BSON("mode"
+ << "nearest"
+ << "tags"
+ << BSON("rack"
+ << "city"))));
+}
+
+TEST(Metadata, UpconvertInvalidMetadata) {
+ // has $maxTimeMS option
+ ASSERT_THROWS_CODE(upconvertRequestMetadata(BSON("query" << BSON("foo"
+ << "bar")
+ << "$maxTimeMS"
+ << 200),
+ 0),
+ UserException,
+ ErrorCodes::InvalidOptions);
+ ASSERT_THROWS_CODE(upconvertRequestMetadata(BSON("$query" << BSON("foo"
+ << "bar")
+ << "$maxTimeMS"
+ << 200),
+ 0),
+ UserException,
+ ErrorCodes::InvalidOptions);
+
+ // invalid wrapped query
+ ASSERT_THROWS(upconvertRequestMetadata(BSON("$query" << 1), 0), UserException);
+ ASSERT_THROWS(upconvertRequestMetadata(BSON("$query"
+ << ""),
+ 0),
+ UserException);
+ ASSERT_THROWS(upconvertRequestMetadata(BSON("query" << 1), 0), UserException);
+ ASSERT_THROWS(upconvertRequestMetadata(BSON("query"
+ << ""),
+ 0),
+ UserException);
+}
+
+} // namespace
diff --git a/src/mongo/s/async_requests_sender.cpp b/src/mongo/s/async_requests_sender.cpp
index eb512b1a281..55f63010a13 100644
--- a/src/mongo/s/async_requests_sender.cpp
+++ b/src/mongo/s/async_requests_sender.cpp
@@ -35,7 +35,6 @@
#include "mongo/client/remote_command_targeter.h"
#include "mongo/executor/remote_command_request.h"
#include "mongo/rpc/get_status_from_command_result.h"
-#include "mongo/rpc/metadata/server_selection_metadata.h"
#include "mongo/s/client/shard_registry.h"
#include "mongo/s/grid.h"
#include "mongo/stdx/memory.h"
@@ -62,11 +61,7 @@ AsyncRequestsSender::AsyncRequestsSender(OperationContext* opCtx,
}
// Initialize command metadata to handle the read preference.
- BSONObjBuilder metadataBuilder;
- rpc::ServerSelectionMetadata metadata(_readPreference.pref != ReadPreference::PrimaryOnly,
- boost::none);
- uassertStatusOK(metadata.writeToMetadata(&metadataBuilder));
- _metadataObj = metadataBuilder.obj();
+ _metadataObj = readPreference.toContainingBSON();
// Schedule the requests immediately.
diff --git a/src/mongo/s/commands/cluster_commands_common.cpp b/src/mongo/s/commands/cluster_commands_common.cpp
index 24cfe617e98..a5e961eda2b 100644
--- a/src/mongo/s/commands/cluster_commands_common.cpp
+++ b/src/mongo/s/commands/cluster_commands_common.cpp
@@ -37,7 +37,6 @@
#include "mongo/db/query/cursor_response.h"
#include "mongo/executor/task_executor_pool.h"
#include "mongo/rpc/get_status_from_command_result.h"
-#include "mongo/rpc/metadata/server_selection_metadata.h"
#include "mongo/s/catalog/type_collection.h"
#include "mongo/s/catalog_cache.h"
#include "mongo/s/client/parallel.h"
@@ -208,16 +207,7 @@ BSONObj appendShardVersion(const BSONObj& cmdObj, ChunkVersion version) {
ReadPreferenceSetting getReadPref(const BSONObj& cmdObj) {
const auto queryOptionsObj = cmdObj.getObjectField(QueryRequest::kUnwrappedReadPrefField);
- const auto readPrefObj = queryOptionsObj.getObjectField(QueryRequest::kWrappedReadPrefField);
- return readPrefObj.isEmpty() ? ReadPreferenceSetting(ReadPreference::PrimaryOnly, TagSet())
- : uassertStatusOK(ReadPreferenceSetting::fromBSON(readPrefObj));
-}
-
-ReadPreferenceSetting getReadPref(const rpc::ServerSelectionMetadata& ssm) {
- return ssm.getReadPreference()
- ? *ssm.getReadPreference()
- : (ssm.isSecondaryOk() ? ReadPreferenceSetting(ReadPreference::SecondaryPreferred, TagSet())
- : ReadPreferenceSetting(ReadPreference::PrimaryOnly, TagSet()));
+ return uassertStatusOK(ReadPreferenceSetting::fromContainingBSON(queryOptionsObj));
}
StatusWith<std::vector<AsyncRequestsSender::Response>> scatterGather(
diff --git a/src/mongo/s/commands/cluster_commands_common.h b/src/mongo/s/commands/cluster_commands_common.h
index 5b999272807..56bac9b8e6a 100644
--- a/src/mongo/s/commands/cluster_commands_common.h
+++ b/src/mongo/s/commands/cluster_commands_common.h
@@ -57,19 +57,6 @@ BSONObj appendShardVersion(const BSONObj& cmdObj, ChunkVersion version);
ReadPreferenceSetting getReadPref(const BSONObj& cmdObj);
/**
- * Returns the read preference from the ServerSelectionMetadata if set or defaults to PrimaryOnly
- * and an empty TagSet.
- *
- * This is used by explain commands, where the read preference is extracted and placed into
- * ServerSelectionMetadata before the explain command is run.
- *
- * This is because only the value of the 'explain' field in the cmdObj is passed to the Command's
- * explain() method, and the readPreference is at the same level as the 'explain' field:
- * { explain: { find: "foo" }, $queryOptions: { $readPreference: { ... } } }
- */
-ReadPreferenceSetting getReadPref(const rpc::ServerSelectionMetadata& ssm);
-
-/**
* Broadcasts 'cmdObj' to all shards and returns the responses as a vector.
*
* Returns a non-OK status if a failure occurs on *this* node during execution.
diff --git a/src/mongo/s/commands/cluster_find_cmd.cpp b/src/mongo/s/commands/cluster_find_cmd.cpp
index 83598d92401..80e9508ae55 100644
--- a/src/mongo/s/commands/cluster_find_cmd.cpp
+++ b/src/mongo/s/commands/cluster_find_cmd.cpp
@@ -38,7 +38,6 @@
#include "mongo/db/stats/counters.h"
#include "mongo/db/views/resolved_view.h"
#include "mongo/rpc/get_status_from_command_result.h"
-#include "mongo/rpc/metadata/server_selection_metadata.h"
#include "mongo/s/commands/cluster_aggregate.h"
#include "mongo/s/commands/strategy.h"
#include "mongo/s/query/cluster_find.h"
@@ -115,12 +114,8 @@ public:
return qr.getStatus();
}
- auto result = Strategy::explainFind(opCtx,
- cmdObj,
- *qr.getValue(),
- verbosity,
- rpc::ServerSelectionMetadata::get(opCtx),
- out);
+ auto result = Strategy::explainFind(
+ opCtx, cmdObj, *qr.getValue(), verbosity, ReadPreferenceSetting::get(opCtx), out);
if (result == ErrorCodes::CommandOnShardedViewNotSupportedOnMongod) {
auto resolvedView = ResolvedView::fromBSON(out->asTempObj());
diff --git a/src/mongo/s/commands/cluster_pipeline_cmd.cpp b/src/mongo/s/commands/cluster_pipeline_cmd.cpp
index 9841f87d6a4..010bed8f14b 100644
--- a/src/mongo/s/commands/cluster_pipeline_cmd.cpp
+++ b/src/mongo/s/commands/cluster_pipeline_cmd.cpp
@@ -33,7 +33,6 @@
#include "mongo/base/status.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/commands.h"
-#include "mongo/rpc/metadata/server_selection_metadata.h"
#include "mongo/s/commands/cluster_aggregate.h"
namespace mongo {
@@ -81,13 +80,15 @@ public:
const BSONObj& cmdObj,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
- // Add the server selection metadata to the aggregate command in the "unwrapped" format that
+ // Add the read preference to the aggregate command in the "unwrapped" format that
// runAggregate() expects: {aggregate: ..., $queryOptions: {$readPreference: ...}}.
+ const auto& readPref = ReadPreferenceSetting::get(opCtx);
BSONObjBuilder aggCmdBuilder;
aggCmdBuilder.appendElements(cmdObj);
- if (auto readPref = rpc::ServerSelectionMetadata::get(opCtx).getReadPreference()) {
- aggCmdBuilder.append(QueryRequest::kUnwrappedReadPrefField,
- BSON("$readPreference" << readPref->toBSON()));
+ if (readPref.canRunOnSecondary()) {
+ auto queryOptionsBuilder =
+ BSONObjBuilder(aggCmdBuilder.subobjStart(QueryRequest::kUnwrappedReadPrefField));
+ readPref.toContainingBSON(&queryOptionsBuilder);
}
BSONObj aggCmd = aggCmdBuilder.obj();
diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp
index 7500509f366..11cd2ef1381 100644
--- a/src/mongo/s/commands/strategy.cpp
+++ b/src/mongo/s/commands/strategy.cpp
@@ -55,7 +55,6 @@
#include "mongo/db/views/resolved_view.h"
#include "mongo/rpc/get_status_from_command_result.h"
#include "mongo/rpc/metadata/logical_time_metadata.h"
-#include "mongo/rpc/metadata/server_selection_metadata.h"
#include "mongo/rpc/metadata/tracking_metadata.h"
#include "mongo/s/catalog_cache.h"
#include "mongo/s/client/parallel.h"
@@ -224,20 +223,11 @@ DbResponse Strategy::queryOp(OperationContext* opCtx, const NamespaceString& nss
}
// Determine the default read preference mode based on the value of the slaveOk flag.
- const ReadPreferenceSetting readPreference = [&]() {
- BSONElement rpElem;
- auto readPrefExtractStatus = bsonExtractTypedField(
- q.query, QueryRequest::kWrappedReadPrefField, mongo::Object, &rpElem);
- if (readPrefExtractStatus == ErrorCodes::NoSuchKey) {
- return ReadPreferenceSetting(q.queryOptions & QueryOption_SlaveOk
- ? ReadPreference::SecondaryPreferred
- : ReadPreference::PrimaryOnly);
- }
-
- uassertStatusOK(readPrefExtractStatus);
-
- return uassertStatusOK(ReadPreferenceSetting::fromBSON(rpElem.Obj()));
- }();
+ const auto defaultReadPref = q.queryOptions & QueryOption_SlaveOk
+ ? ReadPreference::SecondaryPreferred
+ : ReadPreference::PrimaryOnly;
+ const auto readPreference =
+ uassertStatusOK(ReadPreferenceSetting::fromContainingBSON(q.query, defaultReadPref));
auto canonicalQuery =
uassertStatusOK(CanonicalQuery::canonicalize(opCtx, q, ExtensionsCallbackNoop()));
@@ -251,12 +241,9 @@ DbResponse Strategy::queryOp(OperationContext* opCtx, const NamespaceString& nss
// We default to allPlansExecution verbosity.
const auto verbosity = ExplainOptions::Verbosity::kExecAllPlans;
- const bool secondaryOk = (readPreference.pref != ReadPreference::PrimaryOnly);
- const rpc::ServerSelectionMetadata metadata(secondaryOk, readPreference);
-
BSONObjBuilder explainBuilder;
uassertStatusOK(Strategy::explainFind(
- opCtx, findCommand, queryRequest, verbosity, metadata, &explainBuilder));
+ opCtx, findCommand, queryRequest, verbosity, readPreference, &explainBuilder));
BSONObj explainObj = explainBuilder.done();
return replyToQuery(explainObj);
@@ -357,10 +344,9 @@ DbResponse Strategy::clientCommandOp(OperationContext* opCtx,
// The command has a read preference setting. We don't want to lose this information
// so we put it on the OperationContext and copy it to a new field called
// $queryOptions.$readPreference
- auto readPref =
- uassertStatusOK(ReadPreferenceSetting::fromBSON(readPrefElem.Obj()));
- rpc::ServerSelectionMetadata::get(opCtx) = rpc::ServerSelectionMetadata(
- readPref.pref != ReadPreference::PrimaryOnly, readPref);
+ ReadPreferenceSetting::get(opCtx) =
+ uassertStatusOK(ReadPreferenceSetting::fromInnerBSON(readPrefElem));
+
haveReadPref = true;
BSONObjBuilder finalCmdObjBuilder;
finalCmdObjBuilder.appendElements(e.embeddedObject());
@@ -378,16 +364,15 @@ DbResponse Strategy::clientCommandOp(OperationContext* opCtx,
if (!haveReadPref && q.queryOptions & QueryOption_SlaveOk) {
// If the slaveOK bit is set, behave as-if read preference secondary-preferred was
// specified.
- rpc::ServerSelectionMetadata::get(opCtx) = rpc::ServerSelectionMetadata(
- true, ReadPreferenceSetting(ReadPreference::SecondaryPreferred));
+ const auto readPref = ReadPreferenceSetting(ReadPreference::SecondaryPreferred);
+ ReadPreferenceSetting::get(opCtx) = readPref;
+
BSONObjBuilder finalCmdObjBuilder;
finalCmdObjBuilder.appendElements(cmdObj);
BSONObjBuilder queryOptionsBuilder(finalCmdObjBuilder.subobjStart("$queryOptions"));
- queryOptionsBuilder.append(
- Query::ReadPrefField.name(),
- ReadPreferenceSetting(ReadPreference::SecondaryPreferred).toBSON());
- queryOptionsBuilder.done();
+ readPref.toContainingBSON(&queryOptionsBuilder);
+ queryOptionsBuilder.doneFast();
cmdObj = finalCmdObjBuilder.obj();
}
@@ -624,7 +609,7 @@ Status Strategy::explainFind(OperationContext* opCtx,
const BSONObj& findCommand,
const QueryRequest& qr,
ExplainOptions::Verbosity verbosity,
- const rpc::ServerSelectionMetadata& ssm,
+ const ReadPreferenceSetting& readPref,
BSONObjBuilder* out) {
const auto explainCmd = ClusterExplain::wrapAsExplain(findCommand, verbosity);
@@ -635,7 +620,7 @@ Status Strategy::explainFind(OperationContext* opCtx,
auto swShardResponses = scatterGatherForNamespace(opCtx,
qr.nss(),
explainCmd,
- getReadPref(ssm),
+ readPref,
qr.getFilter(),
qr.getCollation(),
true, // do shard versioning
diff --git a/src/mongo/s/commands/strategy.h b/src/mongo/s/commands/strategy.h
index f91e3d61793..9da42bae611 100644
--- a/src/mongo/s/commands/strategy.h
+++ b/src/mongo/s/commands/strategy.h
@@ -42,10 +42,6 @@ class NamespaceString;
class OperationContext;
class QueryRequest;
-namespace rpc {
-class ServerSelectionMetadata;
-} // namespace rpc
-
/**
* Legacy interface for processing client read/write/cmd requests.
*/
@@ -101,7 +97,7 @@ public:
const BSONObj& findCommand,
const QueryRequest& qr,
ExplainOptions::Verbosity verbosity,
- const rpc::ServerSelectionMetadata& serverSelectionMetadata,
+ const ReadPreferenceSetting& readPref,
BSONObjBuilder* out);
struct CommandResult {
diff --git a/src/mongo/s/query/async_results_merger.cpp b/src/mongo/s/query/async_results_merger.cpp
index bd3c67bf44a..ced16197e98 100644
--- a/src/mongo/s/query/async_results_merger.cpp
+++ b/src/mongo/s/query/async_results_merger.cpp
@@ -38,7 +38,6 @@
#include "mongo/db/query/killcursors_request.h"
#include "mongo/executor/remote_command_request.h"
#include "mongo/executor/remote_command_response.h"
-#include "mongo/rpc/metadata/server_selection_metadata.h"
#include "mongo/s/client/shard_registry.h"
#include "mongo/s/grid.h"
#include "mongo/util/assert_util.h"
@@ -72,11 +71,7 @@ AsyncResultsMerger::AsyncResultsMerger(executor::TaskExecutor* executor,
// is primaryOnly, in which case if the remote host for one of the cursors changes roles, the
// remote will return an error.
if (_params->readPreference) {
- BSONObjBuilder metadataBuilder;
- rpc::ServerSelectionMetadata metadata(
- _params->readPreference->pref != ReadPreference::PrimaryOnly, boost::none);
- uassertStatusOK(metadata.writeToMetadata(&metadataBuilder));
- _metadataObj = metadataBuilder.obj();
+ _metadataObj = _params->readPreference->toContainingBSON();
}
}
diff --git a/src/mongo/s/query/async_results_merger_test.cpp b/src/mongo/s/query/async_results_merger_test.cpp
index 3396649256c..bca910ce347 100644
--- a/src/mongo/s/query/async_results_merger_test.cpp
+++ b/src/mongo/s/query/async_results_merger_test.cpp
@@ -39,7 +39,6 @@
#include "mongo/executor/network_interface_mock.h"
#include "mongo/executor/task_executor.h"
#include "mongo/executor/thread_pool_task_executor_test_fixture.h"
-#include "mongo/rpc/metadata/server_selection_metadata.h"
#include "mongo/s/catalog/type_shard.h"
#include "mongo/s/client/shard_registry.h"
#include "mongo/s/sharding_test_fixture.h"
@@ -1226,7 +1225,9 @@ TEST_F(AsyncResultsMergerTest, SendsSecondaryOkAsMetadata) {
ASSERT_FALSE(arm->ready());
BSONObj cmdRequestMetadata = getFirstPendingRequest().metadata;
- ASSERT_BSONOBJ_EQ(cmdRequestMetadata, rpc::ServerSelectionMetadata(true, boost::none).toBSON());
+ ASSERT(uassertStatusOK(ReadPreferenceSetting::fromContainingBSON(cmdRequestMetadata))
+ .canRunOnSecondary())
+ << "full metadata: " << cmdRequestMetadata;
std::vector<CursorResponse> responses;
std::vector<BSONObj> batch1 = {fromjson("{_id: 1}")};
diff --git a/src/mongo/s/query/cluster_find.cpp b/src/mongo/s/query/cluster_find.cpp
index 0dd13e834d5..c282fee7800 100644
--- a/src/mongo/s/query/cluster_find.cpp
+++ b/src/mongo/s/query/cluster_find.cpp
@@ -470,15 +470,7 @@ StatusWith<ReadPreferenceSetting> ClusterFind::extractUnwrappedReadPref(const BS
if (status.isOK()) {
// There must be a nested object containing the read preference if there is a queryOptions
// field.
- BSONObj queryOptionsObj = queryOptionsElt.Obj();
- invariant(queryOptionsObj[QueryRequest::kWrappedReadPrefField].type() == BSONType::Object);
- BSONObj readPrefObj = queryOptionsObj[QueryRequest::kWrappedReadPrefField].Obj();
-
- auto readPref = ReadPreferenceSetting::fromBSON(readPrefObj);
- if (!readPref.isOK()) {
- return readPref.getStatus();
- }
- return readPref.getValue();
+ return ReadPreferenceSetting::fromContainingBSON(queryOptionsElt.Obj());
} else if (status != ErrorCodes::NoSuchKey) {
return status;
}
diff --git a/src/mongo/s/query/cluster_find.h b/src/mongo/s/query/cluster_find.h
index 8cf65aac017..492c0dc81e2 100644
--- a/src/mongo/s/query/cluster_find.h
+++ b/src/mongo/s/query/cluster_find.h
@@ -44,10 +44,6 @@ class OperationContext;
struct GetMoreRequest;
struct ReadPreferenceSetting;
-namespace rpc {
-class ServerSelectionMetadata;
-} // namespace rpc
-
/**
* Methods for running find and getMore operations across a sharded cluster.
*/