diff options
author | Randolph Tan <randolph@10gen.com> | 2021-10-04 19:31:33 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-10-04 20:09:48 +0000 |
commit | 42a05ea17f1860a8616efd47acfb0e1adb3c7917 (patch) | |
tree | 75cc75e03d534f42b4b68c3f378a5a9deb0d8765 | |
parent | ee1b091cf1b3ad4c07903c5f8cfe4cdabfebdd8f (diff) | |
download | mongo-42a05ea17f1860a8616efd47acfb0e1adb3c7917.tar.gz |
SERVER-59849 Add test to verify behavior of ReshardingOplogFetcher wh…
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(); |