summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandolph Tan <randolph@10gen.com>2019-02-11 13:23:54 -0500
committerRandolph Tan <randolph@10gen.com>2019-06-04 09:58:03 -0400
commit0c2e3a6b1ea226c9dfb9822b57f02399950493dc (patch)
treead93c59dfff2297cf7f6ed6c59b2dfc3f83b28df
parent795dc5cb3977fc67e0cd640aa98b82138ccc6380 (diff)
downloadmongo-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.cpp13
-rw-r--r--src/mongo/db/repl/sync_tail_test.cpp125
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());