summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandolph Tan <randolph@10gen.com>2021-10-04 19:31:33 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-10-04 20:09:48 +0000
commit42a05ea17f1860a8616efd47acfb0e1adb3c7917 (patch)
tree75cc75e03d534f42b4b68c3f378a5a9deb0d8765
parentee1b091cf1b3ad4c07903c5f8cfe4cdabfebdd8f (diff)
downloadmongo-42a05ea17f1860a8616efd47acfb0e1adb3c7917.tar.gz
SERVER-59849 Add test to verify behavior of ReshardingOplogFetcher wh…
-rw-r--r--src/mongo/db/ops/write_ops_retryability_test.cpp62
-rw-r--r--src/mongo/db/s/resharding/resharding_donor_oplog_iterator_test.cpp54
-rw-r--r--src/mongo/db/s/resharding/resharding_oplog_session_application_test.cpp58
3 files changed, 170 insertions, 4 deletions
diff --git a/src/mongo/db/ops/write_ops_retryability_test.cpp b/src/mongo/db/ops/write_ops_retryability_test.cpp
index c8d41d7625b..d14376532f5 100644
--- a/src/mongo/db/ops/write_ops_retryability_test.cpp
+++ b/src/mongo/db/ops/write_ops_retryability_test.cpp
@@ -564,6 +564,37 @@ TEST_F(FindAndModifyRetryability, NestedUpdateWithPreImage) {
result);
}
+TEST_F(FindAndModifyRetryability, UpdateRequestWithPreImageButNestedOpHasNoLinkShouldAssert) {
+ auto request = makeFindAndModifyRequest(
+ kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
+ request.setNew(false);
+
+ repl::OpTime imageOpTime(Timestamp(120, 3), 1);
+ auto noteOplog = makeOplogEntry(imageOpTime, // optime
+ repl::OpTypeEnum::kNoop, // op type
+ kNs, // namespace
+ BSON("_id" << 1 << "z" << 1)); // o
+
+ insertOplogEntry(noteOplog);
+
+ auto innerOplog = makeOplogEntry(repl::OpTime(), // optime
+ repl::OpTypeEnum::kUpdate, // op type
+ kNs, // namespace
+ BSON("_id" << 1 << "y" << 1), // o
+ BSON("_id" << 1)); // o2
+
+ auto updateOplog = makeOplogEntry(repl::OpTime(Timestamp(60, 10), 1), // optime
+ repl::OpTypeEnum::kNoop, // optype
+ kNs, // namespace
+ kNestedOplog, // o
+ innerOplog.getEntry().toBSON(), // o2
+ boost::none, // pre-image optime
+ boost::none); // post-image optime
+
+ ASSERT_THROWS(parseOplogEntryForFindAndModify(opCtx(), request, updateOplog),
+ AssertionException);
+}
+
TEST_F(FindAndModifyRetryability, UpdateWithPostImage) {
auto request = makeFindAndModifyRequest(
kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
@@ -624,6 +655,37 @@ TEST_F(FindAndModifyRetryability, NestedUpdateWithPostImage) {
result);
}
+TEST_F(FindAndModifyRetryability, UpdateRequestWithPostImageButNestedOpHasNoLinkShouldAssert) {
+ auto request = makeFindAndModifyRequest(
+ kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
+ request.setNew(true);
+
+ repl::OpTime imageOpTime(Timestamp(120, 3), 1);
+ auto noteOplog = makeOplogEntry(imageOpTime, // optime
+ repl::OpTypeEnum::kNoop, // op type
+ kNs, // namespace
+ BSON("a" << 1 << "b" << 1)); // o
+
+ insertOplogEntry(noteOplog);
+
+ auto innerOplog = makeOplogEntry(repl::OpTime(), // optime
+ repl::OpTypeEnum::kUpdate, // op type
+ kNs, // namespace
+ BSON("_id" << 1 << "y" << 1), // o
+ BSON("_id" << 1)); // o2
+
+ auto updateOplog = makeOplogEntry(repl::OpTime(Timestamp(60, 10), 1), // optime
+ repl::OpTypeEnum::kNoop, // op type
+ kNs, // namespace
+ kNestedOplog, // o
+ innerOplog.getEntry().toBSON(), // o2
+ boost::none, // pre-image optime
+ boost::none); // post-image optime
+
+ ASSERT_THROWS(parseOplogEntryForFindAndModify(opCtx(), request, updateOplog),
+ AssertionException);
+}
+
TEST_F(FindAndModifyRetryability, UpdateWithPostImageButOplogDoesNotExistShouldError) {
auto request = makeFindAndModifyRequest(
kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
diff --git a/src/mongo/db/s/resharding/resharding_donor_oplog_iterator_test.cpp b/src/mongo/db/s/resharding/resharding_donor_oplog_iterator_test.cpp
index f98dae2426f..e8bfafde493 100644
--- a/src/mongo/db/s/resharding/resharding_donor_oplog_iterator_test.cpp
+++ b/src/mongo/db/s/resharding/resharding_donor_oplog_iterator_test.cpp
@@ -386,6 +386,33 @@ TEST_F(ReshardingDonorOplogIterTest, FillsInPreImageOplogEntry) {
ASSERT_TRUE(next.empty());
}
+TEST_F(ReshardingDonorOplogIterTest, ShouldNotErrorIfPreImageOplogEntryCannotBeFound) {
+ const auto& preImageDoc = BSON("_id" << 0 << "x" << 1);
+ const auto& [preImageOp, deleteOp] =
+ makeDeleteWithPreImage(Timestamp(2, 4), preImageDoc, Timestamp(2, 5));
+ const auto& finalOplog = makeFinalOplog(Timestamp(43, 24));
+
+ DBDirectClient client(operationContext());
+ const auto& ns = oplogNss().ns();
+ client.insert(ns, deleteOp.toBSON());
+ client.insert(ns, finalOplog.toBSON());
+
+ ReshardingDonorOplogIterator iter(oplogNss(), kResumeFromBeginning, &onInsertAlwaysReady);
+ auto executor = makeTaskExecutorForIterator();
+ auto factory = makeCancelableOpCtx();
+ auto altClient = makeKillableClient();
+ AlternativeClientRegion acr(altClient);
+
+ auto next = getNextBatch(&iter, executor, factory);
+ ASSERT_EQ(next.size(), 1U);
+ ASSERT_BSONOBJ_EQ(getId(deleteOp), getId(next[0]));
+ ASSERT_FALSE(bool(next[0].getPreImageOp()));
+ ASSERT_FALSE(bool(next[0].getPostImageOp()));
+
+ next = getNextBatch(&iter, executor, factory);
+ ASSERT_TRUE(next.empty());
+}
+
TEST_F(ReshardingDonorOplogIterTest, FillsInPostImageOplogEntry) {
const auto& postImageDoc = BSON("_id" << 0 << "x" << 1);
const auto& [postImageOp, updateOp] = makeUpdateWithPostImage(
@@ -420,6 +447,33 @@ TEST_F(ReshardingDonorOplogIterTest, FillsInPostImageOplogEntry) {
ASSERT_TRUE(next.empty());
}
+TEST_F(ReshardingDonorOplogIterTest, ShouldNotErrorIfPostImageOplogEntryCannotBeFound) {
+ const auto& postImageDoc = BSON("_id" << 0 << "x" << 1);
+ const auto& [postImageOp, updateOp] = makeUpdateWithPostImage(
+ Timestamp(2, 4), postImageDoc, Timestamp(2, 5), BSON("$set" << BSON("x" << 1)));
+ const auto& finalOplog = makeFinalOplog(Timestamp(43, 24));
+
+ DBDirectClient client(operationContext());
+ const auto& ns = oplogNss().ns();
+ client.insert(ns, updateOp.toBSON());
+ client.insert(ns, finalOplog.toBSON());
+
+ ReshardingDonorOplogIterator iter(oplogNss(), kResumeFromBeginning, &onInsertAlwaysReady);
+ auto executor = makeTaskExecutorForIterator();
+ auto factory = makeCancelableOpCtx();
+ auto altClient = makeKillableClient();
+ AlternativeClientRegion acr(altClient);
+
+ auto next = getNextBatch(&iter, executor, factory);
+ ASSERT_EQ(next.size(), 1U);
+ ASSERT_BSONOBJ_EQ(getId(updateOp), getId(next[0]));
+ ASSERT_FALSE(bool(next[0].getPreImageOp()));
+ ASSERT_FALSE(bool(next[0].getPostImageOp()));
+
+ next = getNextBatch(&iter, executor, factory);
+ ASSERT_TRUE(next.empty());
+}
+
TEST_F(ReshardingDonorOplogIterTest, BatchIncludesProgressMarkEntries) {
const auto oplog1 = makeInsertOplog(Timestamp(2, 4), BSON("x" << 1));
const auto progressMarkOplog1 = makeProgressMarkOplogEntry(Timestamp(15, 3));
diff --git a/src/mongo/db/s/resharding/resharding_oplog_session_application_test.cpp b/src/mongo/db/s/resharding/resharding_oplog_session_application_test.cpp
index 29c6fd3c26b..78f817508a1 100644
--- a/src/mongo/db/s/resharding/resharding_oplog_session_application_test.cpp
+++ b/src/mongo/db/s/resharding/resharding_oplog_session_application_test.cpp
@@ -141,10 +141,12 @@ public:
txnParticipant.stashTransactionResources(opCtx);
}
- repl::OplogEntry makeUpdateOp(BSONObj document,
- LogicalSessionId lsid,
- TxnNumber txnNumber,
- const std::vector<StmtId>& stmtIds) {
+ repl::OplogEntry makeUpdateOp(
+ BSONObj document,
+ LogicalSessionId lsid,
+ TxnNumber txnNumber,
+ const std::vector<StmtId>& stmtIds,
+ boost::optional<repl::RetryImageEnum> needsRetryImage = boost::none) {
repl::MutableOplogEntry op;
op.setOpType(repl::OpTypeEnum::kUpdate);
op.setObject2(document["_id"].wrap().getOwned());
@@ -152,6 +154,7 @@ public:
op.setSessionId(std::move(lsid));
op.setTxnNumber(std::move(txnNumber));
op.setStatementIds(stmtIds);
+ op.setNeedsRetryImage(needsRetryImage);
// These are unused by ReshardingOplogSessionApplication but required by IDL parsing.
op.setNss({});
@@ -707,6 +710,53 @@ TEST_F(ReshardingOplogSessionApplicationTest, IncomingRetryableWriteHasPostImage
}
}
+// Resharding converts oplog with retry image to old style no-op oplog pairs in normal cases. But
+// if it was not able to extract the document from the image collection, it will return the oplog
+// entry as is. This is how resharding oplog application can encounter oplog with needsRetryImage.
+TEST_F(ReshardingOplogSessionApplicationTest, IncomingRetryableWriteHasNeedsRetryImage) {
+ auto lsid = makeLogicalSessionIdForTest();
+
+ TxnNumber incomingTxnNumber = 100;
+ StmtId incomingStmtId = 2;
+
+ auto opTime = [&] {
+ auto opCtx = makeOperationContext();
+ return insertSessionRecord(opCtx.get(), makeLogicalSessionIdForTest(), 100, {3});
+ }();
+
+ auto oplogEntry = makeUpdateOp(BSON("_id" << 1),
+ lsid,
+ incomingTxnNumber,
+ {incomingStmtId},
+ repl::RetryImageEnum::kPreImage);
+
+ {
+ auto opCtx = makeOperationContext();
+ ReshardingOplogSessionApplication applier;
+ auto hitPreparedTxn = applier.tryApplyOperation(opCtx.get(), oplogEntry);
+ ASSERT_FALSE(bool(hitPreparedTxn));
+ }
+
+ {
+ auto opCtx = makeOperationContext();
+ auto foundOps = findOplogEntriesNewerThan(opCtx.get(), opTime.getTimestamp());
+ ASSERT_EQ(foundOps.size(), 1U);
+ checkGeneratedNoop(foundOps[0], lsid, incomingTxnNumber, {incomingStmtId});
+ ASSERT_FALSE(foundOps[0].getPostImageOpTime());
+ ASSERT_FALSE(foundOps[0].getPreImageOpTime());
+ ASSERT_FALSE(foundOps[0].getNeedsRetryImage());
+
+ auto sessionTxnRecord = findSessionRecord(opCtx.get(), lsid);
+ ASSERT_TRUE(bool(sessionTxnRecord));
+ checkSessionTxnRecord(*sessionTxnRecord, foundOps[0]);
+ }
+
+ {
+ auto opCtx = makeOperationContext();
+ checkStatementExecuted(opCtx.get(), lsid, incomingTxnNumber, incomingStmtId);
+ }
+}
+
TEST_F(ReshardingOplogSessionApplicationTest, IncomingTxnForNewSession) {
auto lsid = makeLogicalSessionIdForTest();