summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmirsaman Memaripour <amirsaman.memaripour@mongodb.com>2020-03-02 15:28:39 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-10-20 15:02:57 +0000
commit3641c00be722f394a2b0987cfcac023930e5bbf1 (patch)
tree405dfe0daee00d5954d1be20677a618a5952e71f
parentddee85735f38dc77f233dc16840658c8764e65cb (diff)
downloadmongo-3641c00be722f394a2b0987cfcac023930e5bbf1.tar.gz
SERVER-46521 Extend mirrored reads generation tests
(cherry picked from commit f321509051ee1da3fd3646bec3ba24658062ab19)
-rw-r--r--src/mongo/db/commands/command_mirroring_test.cpp311
1 files changed, 288 insertions, 23 deletions
diff --git a/src/mongo/db/commands/command_mirroring_test.cpp b/src/mongo/db/commands/command_mirroring_test.cpp
index 3f7f2753a5b..68b42d6ea30 100644
--- a/src/mongo/db/commands/command_mirroring_test.cpp
+++ b/src/mongo/db/commands/command_mirroring_test.cpp
@@ -53,13 +53,35 @@ public:
auto client = Client::releaseCurrent();
client.reset(nullptr);
}
+ virtual std::string commandName() = 0;
- virtual OpMsgRequest makeCommand(std::string, std::vector<BSONObj>) = 0;
+ virtual OpMsgRequest makeCommand(std::string coll, std::vector<BSONObj> args) {
+ BSONObjBuilder bob;
+
+ bob << commandName() << coll;
+ bob << "lsid" << _lsid.toBSON();
+
+ for (auto arg : args) {
+ bob << arg.firstElement();
+ }
+
+ auto request = OpMsgRequest::fromDBAndBody(kDB, bob.obj());
+ return request;
+ }
+
+ BSONObj createCommandAndGetMirrored(std::string coll, std::vector<BSONObj> args) {
+ auto cmd = makeCommand(coll, args);
+ return getMirroredCommand(cmd);
+ }
- const LogicalSessionId& getLogicalSessionId() const {
- return _lsid;
+ // Checks if "a" and "b" (both BSON objects) are equal.
+ bool compareBSONObjs(BSONObj a, BSONObj b) {
+ return (a == b).type == BSONObj::DeferredComparison::Type::kEQ;
}
+ static constexpr auto kDB = "test"_sd;
+
+private:
BSONObj getMirroredCommand(OpMsgRequest& request) {
auto cmd = globalCommandRegistry()->findCommand(request.getCommandName());
ASSERT(cmd);
@@ -75,25 +97,22 @@ public:
return bob.obj();
}
- static constexpr auto kDB = "test"_sd;
-
-private:
const LogicalSessionId _lsid;
};
class UpdateCommandTest : public CommandMirroringTest {
public:
- OpMsgRequest makeCommand(std::string coll, std::vector<BSONObj> updates) override {
- BSONObjBuilder bob;
-
- bob << "update" << coll;
- bob << "lsid" << getLogicalSessionId().toBSON();
+ std::string commandName() override {
+ return "update";
+ }
- auto request = OpMsgRequest::fromDBAndBody(kDB, bob.obj());
+ OpMsgRequest makeCommand(std::string coll, std::vector<BSONObj> updates) override {
+ auto request = CommandMirroringTest::makeCommand(coll, {});
// Directly add `updates` to `OpMsg::sequences` to emulate `OpMsg::parse()` behavior.
OpMsg::DocumentSequence seq;
seq.name = "updates";
+
for (auto update : updates) {
seq.objs.emplace_back(std::move(update));
}
@@ -105,9 +124,7 @@ public:
TEST_F(UpdateCommandTest, NoQuery) {
auto update = BSON("q" << BSONObj() << "u" << BSON("$set" << BSON("_id" << 1)));
- auto cmd = makeCommand("my_collection", {update});
-
- auto mirroredObj = getMirroredCommand(cmd);
+ auto mirroredObj = createCommandAndGetMirrored("my_collection", {update});
ASSERT_EQ(mirroredObj["find"].String(), "my_collection");
ASSERT_EQ(mirroredObj["filter"].Obj().toString(), "{}");
@@ -119,9 +136,7 @@ TEST_F(UpdateCommandTest, NoQuery) {
TEST_F(UpdateCommandTest, SingleQuery) {
auto update =
BSON("q" << BSON("qty" << BSON("$lt" << 50.0)) << "u" << BSON("$inc" << BSON("qty" << 1)));
- auto cmd = makeCommand("products", {update});
-
- auto mirroredObj = getMirroredCommand(cmd);
+ auto mirroredObj = createCommandAndGetMirrored("products", {update});
ASSERT_EQ(mirroredObj["find"].String(), "products");
ASSERT_EQ(mirroredObj["filter"].Obj().toString(), "{ qty: { $lt: 50.0 } }");
@@ -136,9 +151,8 @@ TEST_F(UpdateCommandTest, SingleQueryWithHintAndCollation) {
<< BSON("locale"
<< "fr")
<< "u" << BSON("$inc" << BSON("price" << 10)));
- auto cmd = makeCommand("products", {update});
- auto mirroredObj = getMirroredCommand(cmd);
+ auto mirroredObj = createCommandAndGetMirrored("products", {update});
ASSERT_EQ(mirroredObj["find"].String(), "products");
ASSERT_EQ(mirroredObj["filter"].Obj().toString(), "{ price: { $gt: 100 } }");
@@ -155,9 +169,7 @@ TEST_F(UpdateCommandTest, MultipleQueries) {
updates.emplace_back(BSON("q" << BSON("_id" << BSON("$eq" << i)) << "u"
<< BSON("$inc" << BSON("qty" << 1))));
}
- auto cmd = makeCommand("products", updates);
-
- auto mirroredObj = getMirroredCommand(cmd);
+ auto mirroredObj = createCommandAndGetMirrored("products", updates);
ASSERT_EQ(mirroredObj["find"].String(), "products");
ASSERT_EQ(mirroredObj["filter"].Obj().toString(), "{ _id: { $eq: 0 } }");
@@ -166,5 +178,258 @@ TEST_F(UpdateCommandTest, MultipleQueries) {
ASSERT_EQ(mirroredObj["batchSize"].Int(), 1);
}
+class FindCommandTest : public CommandMirroringTest {
+public:
+ std::string commandName() override {
+ return "find";
+ }
+
+ virtual std::vector<std::string> getAllowedKeys() const {
+ return {"find",
+ "filter",
+ "skip",
+ "limit",
+ "sort",
+ "hint",
+ "collation",
+ "min",
+ "max",
+ "batchSize",
+ "singleBatch"};
+ }
+
+ void checkFieldNamesAreAllowed(BSONObj& mirroredObj) {
+ const auto possibleKeys = getAllowedKeys();
+ for (auto key : mirroredObj.getFieldNames<std::set<std::string>>()) {
+ ASSERT(std::find(possibleKeys.begin(), possibleKeys.end(), key) != possibleKeys.end());
+ }
+ }
+};
+
+TEST_F(FindCommandTest, MirrorableKeys) {
+ auto findArgs = {BSON("filter" << BSONObj()),
+ BSON("sort" << BSONObj()),
+ BSON("projection" << BSONObj()),
+ BSON("hint" << BSONObj()),
+ BSON("skip" << 1),
+ BSON("limit" << 1),
+ BSON("batchSize" << 1),
+ BSON("singleBatch" << true),
+ BSON("comment"
+ << "This is a comment."),
+ BSON("maxTimeMS" << 100),
+ BSON("readConcern"
+ << "primary"),
+ BSON("max" << BSONObj()),
+ BSON("min" << BSONObj()),
+ BSON("returnKey" << true),
+ BSON("showRecordId" << false),
+ BSON("tailable" << false),
+ BSON("oplogReplay" << true),
+ BSON("noCursorTimeout" << true),
+ BSON("awaitData" << true),
+ BSON("allowPartialResults" << true),
+ BSON("collation" << BSONObj())};
+
+ auto mirroredObj = createCommandAndGetMirrored("test", findArgs);
+ checkFieldNamesAreAllowed(mirroredObj);
+}
+
+TEST_F(FindCommandTest, BatchSizeReconfiguration) {
+ auto findArgs = {
+ BSON("filter" << BSONObj()), BSON("batchSize" << 100), BSON("singleBatch" << false)};
+
+ auto mirroredObj = createCommandAndGetMirrored("test", findArgs);
+ ASSERT_EQ(mirroredObj["singleBatch"].Bool(), true);
+ ASSERT_EQ(mirroredObj["batchSize"].Int(), 1);
+}
+
+TEST_F(FindCommandTest, ValidateMirroredQuery) {
+ constexpr auto collection = "restaurants";
+ const auto filter = BSON("rating" << BSON("$gte" << 9) << "cuisine"
+ << "Italian");
+ constexpr auto skip = 10;
+ constexpr auto limit = 50;
+ const auto sortObj = BSON("name" << 1);
+ const auto hint = BSONObj();
+ const auto collation = BSON("locale"
+ << "\"fr\""
+ << "strength" << 1);
+ const auto min = BSONObj();
+ const auto max = BSONObj();
+
+ auto findArgs = {BSON("filter" << filter),
+ BSON("skip" << skip),
+ BSON("limit" << limit),
+ BSON("sort" << sortObj),
+ BSON("hint" << hint),
+ BSON("collation" << collation),
+ BSON("min" << min),
+ BSON("max" << max)};
+
+ auto mirroredObj = createCommandAndGetMirrored(collection, findArgs);
+
+ ASSERT_EQ(mirroredObj["find"].String(), collection);
+ ASSERT(compareBSONObjs(mirroredObj["filter"].Obj(), filter));
+ ASSERT_EQ(mirroredObj["skip"].Int(), skip);
+ ASSERT_EQ(mirroredObj["limit"].Int(), limit);
+ ASSERT(compareBSONObjs(mirroredObj["sort"].Obj(), sortObj));
+ ASSERT(compareBSONObjs(mirroredObj["hint"].Obj(), hint));
+ ASSERT(compareBSONObjs(mirroredObj["collation"].Obj(), collation));
+ ASSERT(compareBSONObjs(mirroredObj["min"].Obj(), min));
+ ASSERT(compareBSONObjs(mirroredObj["max"].Obj(), max));
+}
+
+class FindAndModifyCommandTest : public FindCommandTest {
+public:
+ std::string commandName() override {
+ return "findAndModify";
+ }
+
+ std::vector<std::string> getAllowedKeys() const override {
+ return {"sort", "collation", "find", "filter", "batchSize", "singleBatch"};
+ }
+};
+
+TEST_F(FindAndModifyCommandTest, MirrorableKeys) {
+ auto findAndModifyArgs = {BSON("query" << BSONObj()),
+ BSON("sort" << BSONObj()),
+ BSON("remove" << false),
+ BSON("update" << BSONObj()),
+ BSON("new" << true),
+ BSON("fields" << BSONObj()),
+ BSON("upsert" << true),
+ BSON("bypassDocumentValidation" << false),
+ BSON("writeConcern" << BSONObj()),
+ BSON("maxTimeMS" << 100),
+ BSON("collation" << BSONObj()),
+ BSON("arrayFilters" << BSONArray())};
+
+ auto mirroredObj = createCommandAndGetMirrored("test", findAndModifyArgs);
+ checkFieldNamesAreAllowed(mirroredObj);
+}
+
+TEST_F(FindAndModifyCommandTest, BatchSizeReconfiguration) {
+ auto findAndModifyArgs = {BSON("query" << BSONObj()),
+ BSON("update" << BSONObj()),
+ BSON("batchSize" << 100),
+ BSON("singleBatch" << false)};
+
+ auto mirroredObj = createCommandAndGetMirrored("test", findAndModifyArgs);
+ ASSERT_EQ(mirroredObj["singleBatch"].Bool(), true);
+ ASSERT_EQ(mirroredObj["batchSize"].Int(), 1);
+}
+
+TEST_F(FindAndModifyCommandTest, ValidateMirroredQuery) {
+ constexpr auto collection = "people";
+ const auto query = BSON("name"
+ << "Andy");
+ const auto sortObj = BSON("rating" << 1);
+ const auto update = BSON("$inc" << BSON("score" << 1));
+ constexpr auto upsert = true;
+ const auto collation = BSON("locale"
+ << "\"fr\"");
+
+ auto findAndModifyArgs = {BSON("query" << query),
+ BSON("sort" << sortObj),
+ BSON("update" << update),
+ BSON("upsert" << upsert),
+ BSON("collation" << collation)};
+
+ auto mirroredObj = createCommandAndGetMirrored(collection, findAndModifyArgs);
+
+ ASSERT_EQ(mirroredObj["find"].String(), collection);
+ ASSERT(!mirroredObj.hasField("upsert"));
+ ASSERT(compareBSONObjs(mirroredObj["filter"].Obj(), query));
+ ASSERT(compareBSONObjs(mirroredObj["sort"].Obj(), sortObj));
+ ASSERT(compareBSONObjs(mirroredObj["collation"].Obj(), collation));
+}
+
+class DistinctCommandTest : public FindCommandTest {
+public:
+ std::string commandName() override {
+ return "distinct";
+ }
+
+ std::vector<std::string> getAllowedKeys() const override {
+ return {"distinct", "key", "query", "collation"};
+ }
+};
+
+TEST_F(DistinctCommandTest, MirrorableKeys) {
+ auto distinctArgs = {BSON("key"
+ << ""),
+ BSON("query" << BSONObj()),
+ BSON("readConcern" << BSONObj()),
+ BSON("collation" << BSONObj())};
+
+ auto mirroredObj = createCommandAndGetMirrored("test", distinctArgs);
+ checkFieldNamesAreAllowed(mirroredObj);
+}
+
+TEST_F(DistinctCommandTest, ValidateMirroredQuery) {
+ constexpr auto collection = "restaurants";
+ constexpr auto key = "rating";
+ const auto query = BSON("cuisine"
+ << "italian");
+ const auto readConcern = BSON("level"
+ << "majority");
+ const auto collation = BSON("strength" << 1);
+
+ auto distinctArgs = {BSON("key" << key),
+ BSON("query" << query),
+ BSON("readConcern" << readConcern),
+ BSON("collation" << collation)};
+
+ auto mirroredObj = createCommandAndGetMirrored(collection, distinctArgs);
+
+ ASSERT_EQ(mirroredObj["distinct"].String(), collection);
+ ASSERT(!mirroredObj.hasField("readConcern"));
+ ASSERT_EQ(mirroredObj["key"].String(), key);
+ ASSERT(compareBSONObjs(mirroredObj["query"].Obj(), query));
+ ASSERT(compareBSONObjs(mirroredObj["collation"].Obj(), collation));
+}
+
+class CountCommandTest : public FindCommandTest {
+public:
+ std::string commandName() override {
+ return "count";
+ }
+
+ std::vector<std::string> getAllowedKeys() const override {
+ return {"count", "query", "skip", "limit", "hint", "collation"};
+ }
+};
+
+TEST_F(CountCommandTest, MirrorableKeys) {
+ auto countArgs = {BSON("query" << BSONObj()),
+ BSON("limit" << 100),
+ BSON("skip" << 10),
+ BSON("hint" << BSONObj()),
+ BSON("readConcern" << BSONObj()),
+ BSON("collation" << BSONObj())};
+
+ auto mirroredObj = createCommandAndGetMirrored("test", countArgs);
+ checkFieldNamesAreAllowed(mirroredObj);
+}
+
+TEST_F(CountCommandTest, ValidateMirroredQuery) {
+ constexpr auto collection = "orders";
+ const auto query = BSON("status"
+ << "Delivered");
+ const auto hint = BSON("status" << 1);
+ constexpr auto limit = 1000;
+
+ auto countArgs = {BSON("query" << query), BSON("hint" << hint), BSON("limit" << limit)};
+ auto mirroredObj = createCommandAndGetMirrored(collection, countArgs);
+
+ ASSERT_EQ(mirroredObj["count"].String(), collection);
+ ASSERT(!mirroredObj.hasField("skip"));
+ ASSERT(!mirroredObj.hasField("collation"));
+ ASSERT(compareBSONObjs(mirroredObj["query"].Obj(), query));
+ ASSERT(compareBSONObjs(mirroredObj["hint"].Obj(), hint));
+ ASSERT_EQ(mirroredObj["limit"].Int(), limit);
+}
+
} // namespace
} // namespace mongo