summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuhong Zhang <danielzhangyh@gmail.com>2021-05-10 01:13:11 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-05-11 14:11:28 +0000
commita14ed641bb351eae6030b2d1346b5ad457ee3c18 (patch)
tree44dbbb1dfad6237ff95662c50d070fcc71c8595a
parentbebf55ebc71bb6d9070691b4b31662082465992c (diff)
downloadmongo-a14ed641bb351eae6030b2d1346b5ad457ee3c18.tar.gz
SERVER-55260 Test multiple statement ids per oplog entry with applyOps
-rw-r--r--jstests/core/apply_ops1.js35
-rw-r--r--src/mongo/db/repl/apply_ops_test.cpp62
2 files changed, 95 insertions, 2 deletions
diff --git a/jstests/core/apply_ops1.js b/jstests/core/apply_ops1.js
index 7c6982e047a..92242bd971a 100644
--- a/jstests/core/apply_ops1.js
+++ b/jstests/core/apply_ops1.js
@@ -401,6 +401,41 @@ assert.eq(true, res.results[0], "Valid insert with transaction number failed");
assert.eq(true, res.results[1], "Valid update with transaction number failed");
assert.eq(true, res.results[2], "Valid delete with transaction number failed");
+// Ops with multiple statement IDs are valid.
+res = db.runCommand({
+ applyOps: [
+ {
+ op: "i",
+ ns: t.getFullName(),
+ o: {_id: 7, x: 24},
+ lsid: lsid,
+ txnNumber: NumberLong(3),
+ stmtId: [NumberInt(0), NumberInt(1)]
+ },
+ {
+ op: "u",
+ ns: t.getFullName(),
+ o2: {_id: 8},
+ o: {$set: {x: 25}},
+ lsid: lsid,
+ txnNumber: NumberLong(3),
+ stmtId: [NumberInt(2), NumberInt(3)]
+ },
+ {
+ op: "d",
+ ns: t.getFullName(),
+ o: {_id: 7},
+ lsid: lsid,
+ txnNumber: NumberLong(4),
+ stmtId: [NumberInt(0), NumberInt(1)]
+ },
+ ]
+});
+
+assert.eq(true, res.results[0], "Valid insert with multiple statement IDs failed");
+assert.eq(true, res.results[1], "Valid update with multiple statement IDs failed");
+assert.eq(true, res.results[2], "Valid delete with multiple statement IDs failed");
+
// When applying a "u" (update) op, we default to 'UpdateNode' update semantics, and $set
// operations add new fields in lexicographic order.
res = assert.commandWorked(db.adminCommand({
diff --git a/src/mongo/db/repl/apply_ops_test.cpp b/src/mongo/db/repl/apply_ops_test.cpp
index 6999de3ca98..2f1480c48cc 100644
--- a/src/mongo/db/repl/apply_ops_test.cpp
+++ b/src/mongo/db/repl/apply_ops_test.cpp
@@ -336,7 +336,9 @@ TEST_F(ApplyOpsTest, ApplyOpsPropagatesOplogApplicationMode) {
/**
* Generates oplog entries with the given number used for the timestamp.
*/
-OplogEntry makeOplogEntry(OpTypeEnum opType, const BSONObj& oField) {
+OplogEntry makeOplogEntry(OpTypeEnum opType,
+ const BSONObj& oField,
+ const std::vector<StmtId>& stmtIds = {}) {
return {DurableOplogEntry(OpTime(Timestamp(1, 1), 1), // optime
boost::none, // hash
opType, // op type
@@ -349,7 +351,7 @@ OplogEntry makeOplogEntry(OpTypeEnum opType, const BSONObj& oField) {
{}, // sessionInfo
boost::none, // upsert
Date_t(), // wall clock time
- {}, // statement ids
+ stmtIds, // statement ids
boost::none, // optime of previous write within same transaction
boost::none, // pre-image optime
boost::none, // post-image optime
@@ -453,6 +455,62 @@ TEST_F(ApplyOpsTest, ExtractOperationsReturnsOperationsWithSameOpTimeAsApplyOps)
ASSERT(operations.cend() == it);
}
+TEST_F(ApplyOpsTest, ExtractOperationsFromApplyOpsMultiStmtIds) {
+ NamespaceString ns1("test.a");
+ auto ui1 = UUID::gen();
+ auto op1 = BSON("op"
+ << "i"
+ << "ns" << ns1.ns() << "ui" << ui1 << "o" << BSON("_id" << 1));
+
+ NamespaceString ns2("test.b");
+ auto ui2 = UUID::gen();
+ auto op2 = BSON("op"
+ << "u"
+ << "ns" << ns2.ns() << "ui" << ui2 << "b" << true << "o" << BSON("x" << 1)
+ << "o2" << BSON("_id" << 2));
+
+ auto oplogEntry =
+ makeOplogEntry(OpTypeEnum::kCommand, BSON("applyOps" << BSON_ARRAY(op1 << op2)), {0, 1});
+
+ auto operations = ApplyOps::extractOperations(oplogEntry);
+ ASSERT_EQUALS(2U, operations.size())
+ << "Unexpected number of operations extracted: " << oplogEntry.toBSONForLogging();
+
+ // Check extracted CRUD operations.
+ auto it = operations.cbegin();
+ {
+ ASSERT(operations.cend() != it);
+ const auto& operation1 = *(it++);
+ ASSERT(OpTypeEnum::kInsert == operation1.getOpType())
+ << "Unexpected op type: " << operation1.toBSONForLogging();
+ ASSERT_EQUALS(ui1, *operation1.getUuid());
+ ASSERT_EQUALS(ns1, operation1.getNss());
+ ASSERT_BSONOBJ_EQ(BSON("_id" << 1), operation1.getOperationToApply());
+
+ // OpTime of CRUD operation should match applyOps.
+ ASSERT_EQUALS(oplogEntry.getOpTime(), operation1.getOpTime());
+ }
+
+ {
+ ASSERT(operations.cend() != it);
+ const auto& operation2 = *(it++);
+ ASSERT(OpTypeEnum::kUpdate == operation2.getOpType())
+ << "Unexpected op type: " << operation2.toBSONForLogging();
+ ASSERT_EQUALS(ui2, *operation2.getUuid());
+ ASSERT_EQUALS(ns2, operation2.getNss());
+ ASSERT_BSONOBJ_EQ(BSON("x" << 1), operation2.getOperationToApply());
+
+ auto optionalUpsertBool = operation2.getUpsert();
+ ASSERT(optionalUpsertBool);
+ ASSERT(*optionalUpsertBool);
+
+ // OpTime of CRUD operation should match applyOps.
+ ASSERT_EQUALS(oplogEntry.getOpTime(), operation2.getOpTime());
+ }
+
+ ASSERT(operations.cend() == it);
+}
+
TEST_F(ApplyOpsTest, ApplyOpsFailsToDropAdmin) {
auto opCtx = cc().makeOperationContext();
auto mode = OplogApplication::Mode::kApplyOpsCmd;