diff options
author | Randolph Tan <randolph@10gen.com> | 2019-02-11 13:23:54 -0500 |
---|---|---|
committer | Randolph Tan <randolph@10gen.com> | 2019-06-04 09:58:03 -0400 |
commit | 0c2e3a6b1ea226c9dfb9822b57f02399950493dc (patch) | |
tree | ad93c59dfff2297cf7f6ed6c59b2dfc3f83b28df | |
parent | 795dc5cb3977fc67e0cd640aa98b82138ccc6380 (diff) | |
download | mongo-0c2e3a6b1ea226c9dfb9822b57f02399950493dc.tar.gz |
SERVER-36004 SessionUpdateTracker should ignore no-op entries for pre/post image oplogs
(cherry picked from commit 9638317daa5300efdd43acfa4688e5c739beff4d)
(cherry picked from commit 39f932b0a4f580f7c9dcefb8b3a47335fdb4539f)
-rw-r--r-- | src/mongo/db/repl/session_update_tracker.cpp | 13 | ||||
-rw-r--r-- | src/mongo/db/repl/sync_tail_test.cpp | 125 |
2 files changed, 138 insertions, 0 deletions
diff --git a/src/mongo/db/repl/session_update_tracker.cpp b/src/mongo/db/repl/session_update_tracker.cpp index 5aa2152f5a0..703d204836c 100644 --- a/src/mongo/db/repl/session_update_tracker.cpp +++ b/src/mongo/db/repl/session_update_tracker.cpp @@ -65,6 +65,19 @@ void SessionUpdateTracker::_updateSessionInfo(const OplogEntry& entry) { const auto& lsid = sessionInfo.getSessionId(); invariant(lsid); + // Ignore any no-op oplog entries, except for the ones generated from session migration + // of CRUD ops. These entries will have an o2 field that contains the original CRUD + // oplog entry. + if (entry.getOpType() == OpTypeEnum::kNoop) { + if (!entry.getFromMigrate() || !*entry.getFromMigrate()) { + return; + } + + if (!entry.getObject2()) { + return; + } + } + auto iter = _sessionsToUpdate.find(*lsid); if (iter == _sessionsToUpdate.end()) { _sessionsToUpdate.emplace(*lsid, entry); diff --git a/src/mongo/db/repl/sync_tail_test.cpp b/src/mongo/db/repl/sync_tail_test.cpp index 975249cda01..3112894c2df 100644 --- a/src/mongo/db/repl/sync_tail_test.cpp +++ b/src/mongo/db/repl/sync_tail_test.cpp @@ -1817,6 +1817,34 @@ public: boost::none); // post-image optime } + /** + * Creates an OplogEntry with given parameters and preset defaults for this test suite. + */ + repl::OplogEntry makeOplogEntryForMigrate(const NamespaceString& ns, + repl::OpTime opTime, + repl::OpTypeEnum opType, + BSONObj object, + boost::optional<BSONObj> object2, + const OperationSessionInfo& sessionInfo, + Date_t wallClockTime) { + return repl::OplogEntry(opTime, // optime + 1LL, // hash + opType, // opType + ns, // namespace + boost::none, // uuid + true, // fromMigrate + 0, // version + object, // o + object2, // o2 + sessionInfo, // sessionInfo + boost::none, // false + wallClockTime, // wall clock time + boost::none, // statement id + boost::none, // optime of previous write within same transaction + boost::none, // pre-image optime + boost::none); // post-image optime + } + void checkTxnTable(const OperationSessionInfo& sessionInfo, const repl::OpTime& expectedOpTime, Date_t expectedWallClock) { @@ -2090,6 +2118,103 @@ TEST_F(SyncTailTxnTableTest, MultiApplyUpdatesTheTransactionTable) { ASSERT_TRUE(resultNoTxn.isEmpty()); } +TEST_F(SyncTailTxnTableTest, SessionMigrationNoOpEntriesShouldUpdateTxnTable) { + const auto insertLsid = makeLogicalSessionIdForTest(); + OperationSessionInfo insertSessionInfo; + insertSessionInfo.setSessionId(insertLsid); + insertSessionInfo.setTxnNumber(3); + auto date = Date_t::now(); + + auto innerOplog = makeOplogEntry(nss(), + {Timestamp(10, 10), 1}, + repl::OpTypeEnum::kInsert, + BSON("_id" << 1), + boost::none, + insertSessionInfo, + date); + + auto outerInsertDate = Date_t::now(); + auto insertOplog = makeOplogEntryForMigrate(nss(), + {Timestamp(40, 0), 1}, + repl::OpTypeEnum::kNoop, + BSON("$sessionMigrateInfo" << 1), + innerOplog.toBSON(), + insertSessionInfo, + outerInsertDate); + + auto writerPool = SyncTail::makeWriterPool(); + SyncTail syncTail(nullptr, multiSyncApply); + auto applyOperation = [&](MultiApplier::OperationPtrs* ops) -> Status { + multiSyncApply(ops, &syncTail); + return Status::OK(); + }; + + ASSERT_OK(multiApply(_opCtx.get(), writerPool.get(), {insertOplog}, applyOperation)); + + checkTxnTable(insertSessionInfo, {Timestamp(40, 0), 1}, outerInsertDate); +} + +TEST_F(SyncTailTxnTableTest, PreImageNoOpEntriesShouldNotUpdateTxnTable) { + const auto preImageLsid = makeLogicalSessionIdForTest(); + OperationSessionInfo preImageSessionInfo; + preImageSessionInfo.setSessionId(preImageLsid); + preImageSessionInfo.setTxnNumber(3); + auto preImageDate = Date_t::now(); + + auto preImageOplog = makeOplogEntryForMigrate(nss(), + {Timestamp(30, 0), 1}, + repl::OpTypeEnum::kNoop, + BSON("_id" << 1), + boost::none, + preImageSessionInfo, + preImageDate); + + auto writerPool = SyncTail::makeWriterPool(); + SyncTail syncTail(nullptr, multiSyncApply); + auto applyOperation = [&](MultiApplier::OperationPtrs* ops) -> Status { + multiSyncApply(ops, &syncTail); + return Status::OK(); + }; + + ASSERT_OK(multiApply(_opCtx.get(), writerPool.get(), {preImageOplog}, applyOperation)); + + DBDirectClient client(_opCtx.get()); + auto result = client.findOne(NamespaceString::kSessionTransactionsTableNamespace.ns(), + {BSON(SessionTxnRecord::kSessionIdFieldName + << preImageSessionInfo.getSessionId()->toBSON())}); + ASSERT_TRUE(result.isEmpty()); +} + +TEST_F(SyncTailTxnTableTest, NonMigrateNoOpEntriesShouldNotUpdateTxnTable) { + const auto lsid = makeLogicalSessionIdForTest(); + OperationSessionInfo sessionInfo; + sessionInfo.setSessionId(lsid); + sessionInfo.setTxnNumber(3); + + auto oplog = makeOplogEntry(nss(), + {Timestamp(30, 0), 1}, + repl::OpTypeEnum::kNoop, + BSON("_id" << 1), + boost::none, + sessionInfo, + Date_t::now()); + + auto writerPool = SyncTail::makeWriterPool(); + SyncTail syncTail(nullptr, multiSyncApply); + auto applyOperation = [&](MultiApplier::OperationPtrs* ops) -> Status { + multiSyncApply(ops, &syncTail); + return Status::OK(); + }; + + ASSERT_OK(multiApply(_opCtx.get(), writerPool.get(), {oplog}, applyOperation)); + + DBDirectClient client(_opCtx.get()); + auto result = client.findOne( + NamespaceString::kSessionTransactionsTableNamespace.ns(), + {BSON(SessionTxnRecord::kSessionIdFieldName << sessionInfo.getSessionId()->toBSON())}); + ASSERT_TRUE(result.isEmpty()); +} + TEST_F(IdempotencyTest, EmptyCappedNamespaceNotFound) { // Create a BSON "emptycapped" command. auto emptyCappedCmd = BSON("emptycapped" << nss.coll()); |