diff options
author | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2018-12-19 15:51:56 -0500 |
---|---|---|
committer | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2018-12-22 09:11:43 -0500 |
commit | 8a40fbeb320c2109d81c933c2f6f6b9fc65e017f (patch) | |
tree | a10a2631c4a9d9898c9efcb3bd02985fabbf383e /src/mongo/db | |
parent | 402ea2b4d8e616878c2e14a5fb6f2f86faaaada0 (diff) | |
download | mongo-8a40fbeb320c2109d81c933c2f6f6b9fc65e017f.tar.gz |
SERVER-38713 Get rid of transaction number equality checking from checkStatementExecuted
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/commands/find_and_modify.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/op_observer_impl_test.cpp | 54 | ||||
-rw-r--r-- | src/mongo/db/ops/write_ops_exec.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/s/session_catalog_migration_destination.cpp | 23 | ||||
-rw-r--r-- | src/mongo/db/s/session_catalog_migration_destination_test.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/session_catalog_mongod.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/transaction_participant.cpp | 60 | ||||
-rw-r--r-- | src/mongo/db/transaction_participant.h | 19 | ||||
-rw-r--r-- | src/mongo/db/transaction_participant_retryable_writes_test.cpp | 91 | ||||
-rw-r--r-- | src/mongo/db/transaction_participant_test.cpp | 2 |
10 files changed, 86 insertions, 190 deletions
diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp index dfb83807cc5..b3920af779d 100644 --- a/src/mongo/db/commands/find_and_modify.cpp +++ b/src/mongo/db/commands/find_and_modify.cpp @@ -325,8 +325,7 @@ public: const auto stmtId = 0; if (opCtx->getTxnNumber() && !inTransaction) { const auto txnParticipant = TransactionParticipant::get(opCtx); - if (auto entry = - txnParticipant->checkStatementExecuted(opCtx, *opCtx->getTxnNumber(), stmtId)) { + if (auto entry = txnParticipant->checkStatementExecuted(stmtId)) { RetryableWritesStats::get(opCtx)->incrementRetriedCommandsCount(); RetryableWritesStats::get(opCtx)->incrementRetriedStatementsCount(); parseOplogEntryForFindAndModify(opCtx, args, *entry, &result); diff --git a/src/mongo/db/op_observer_impl_test.cpp b/src/mongo/db/op_observer_impl_test.cpp index 1fb9711b7c2..b802c28a301 100644 --- a/src/mongo/db/op_observer_impl_test.cpp +++ b/src/mongo/db/op_observer_impl_test.cpp @@ -332,56 +332,6 @@ public: }; TEST_F(OpObserverSessionCatalogRollbackTest, - OnRollbackInvalidatesSessionCatalogIfSessionOpsRolledBack) { - const NamespaceString nss("testDB", "testColl"); - - // Create a session. - auto sessionCatalog = SessionCatalog::get(getServiceContext()); - auto sessionId = makeLogicalSessionIdForTest(); - - const TxnNumber txnNum = 0; - const StmtId stmtId = 1000; - - { - auto opCtx = cc().makeOperationContext(); - opCtx->setLogicalSessionId(sessionId); - - // Create a session and sync it from disk - auto session = sessionCatalog->checkOutSession(opCtx.get()); - const auto txnParticipant = TransactionParticipant::get(session.get()); - txnParticipant->refreshFromStorageIfNeeded(); - - // Simulate a write occurring on that session - simulateSessionWrite(opCtx.get(), txnParticipant, nss, txnNum, stmtId); - - // Check that the statement executed - ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(txnNum, stmtId)); - } - - // The OpObserver should invalidate in-memory session state, so the check after this should - // fail. - { - auto opCtx = cc().makeOperationContext(); - - OpObserverImpl opObserver; - OpObserver::RollbackObserverInfo rbInfo; - rbInfo.rollbackSessionIds = {UUID::gen()}; - opObserver.onReplicationRollback(opCtx.get(), rbInfo); - } - - { - auto opCtx = cc().makeOperationContext(); - opCtx->setLogicalSessionId(sessionId); - - auto session = sessionCatalog->checkOutSession(opCtx.get()); - const auto txnParticipant = TransactionParticipant::get(session.get()); - ASSERT_THROWS_CODE(txnParticipant->checkStatementExecutedNoOplogEntryFetch(txnNum, stmtId), - DBException, - ErrorCodes::ConflictingOperationInProgress); - } -} - -TEST_F(OpObserverSessionCatalogRollbackTest, OnRollbackDoesntInvalidateSessionCatalogIfNoSessionOpsRolledBack) { const NamespaceString nss("testDB", "testColl"); @@ -404,7 +354,7 @@ TEST_F(OpObserverSessionCatalogRollbackTest, simulateSessionWrite(opCtx.get(), txnParticipant, nss, txnNum, stmtId); // Check that the statement executed - ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(txnNum, stmtId)); + ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(stmtId)); } // Because there are no sessions to rollback, the OpObserver should not invalidate the in-memory @@ -423,7 +373,7 @@ TEST_F(OpObserverSessionCatalogRollbackTest, auto session = sessionCatalog->checkOutSession(opCtx.get()); const auto txnParticipant = TransactionParticipant::get(session.get()); - ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(txnNum, stmtId)); + ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(stmtId)); } } diff --git a/src/mongo/db/ops/write_ops_exec.cpp b/src/mongo/db/ops/write_ops_exec.cpp index 09b2d8f2fe8..a6301e041a4 100644 --- a/src/mongo/db/ops/write_ops_exec.cpp +++ b/src/mongo/db/ops/write_ops_exec.cpp @@ -520,8 +520,7 @@ WriteResult performInserts(OperationContext* opCtx, const auto stmtId = getStmtIdForWriteOp(opCtx, wholeOp, stmtIdIndex++); if (opCtx->getTxnNumber()) { if (!txnParticipant->inMultiDocumentTransaction() && - txnParticipant->checkStatementExecutedNoOplogEntryFetch(*opCtx->getTxnNumber(), - stmtId)) { + txnParticipant->checkStatementExecutedNoOplogEntryFetch(stmtId)) { containsRetry = true; RetryableWritesStats::get(opCtx)->incrementRetriedStatementsCount(); out.results.emplace_back(makeWriteResultForInsertOrDeleteRetry()); @@ -729,8 +728,7 @@ WriteResult performUpdates(OperationContext* opCtx, const write_ops::Update& who const auto stmtId = getStmtIdForWriteOp(opCtx, wholeOp, stmtIdIndex++); if (opCtx->getTxnNumber()) { if (!txnParticipant->inMultiDocumentTransaction()) { - if (auto entry = txnParticipant->checkStatementExecuted( - opCtx, *opCtx->getTxnNumber(), stmtId)) { + if (auto entry = txnParticipant->checkStatementExecuted(stmtId)) { containsRetry = true; RetryableWritesStats::get(opCtx)->incrementRetriedStatementsCount(); out.results.emplace_back(parseOplogEntryForUpdate(*entry)); @@ -869,8 +867,7 @@ WriteResult performDeletes(OperationContext* opCtx, const write_ops::Delete& who const auto stmtId = getStmtIdForWriteOp(opCtx, wholeOp, stmtIdIndex++); if (opCtx->getTxnNumber()) { if (!txnParticipant->inMultiDocumentTransaction() && - txnParticipant->checkStatementExecutedNoOplogEntryFetch(*opCtx->getTxnNumber(), - stmtId)) { + txnParticipant->checkStatementExecutedNoOplogEntryFetch(stmtId)) { containsRetry = true; RetryableWritesStats::get(opCtx)->incrementRetriedStatementsCount(); out.results.emplace_back(makeWriteResultForInsertOrDeleteRetry()); diff --git a/src/mongo/db/s/session_catalog_migration_destination.cpp b/src/mongo/db/s/session_catalog_migration_destination.cpp index 036e319c59e..0e04d7462c6 100644 --- a/src/mongo/db/s/session_catalog_migration_destination.cpp +++ b/src/mongo/db/s/session_catalog_migration_destination.cpp @@ -250,10 +250,27 @@ ProcessOplogResult processSessionOplog(OperationContext* opCtx, auto scopedSession = SessionCatalog::get(opCtx)->checkOutSession(opCtx, result.sessionId); auto const txnParticipant = TransactionParticipant::get(scopedSession.get()); txnParticipant->refreshFromStorageIfNeeded(); + txnParticipant->beginOrContinue(result.txnNum, boost::none, boost::none); - if (!txnParticipant->onMigrateBeginOnPrimary(opCtx, result.txnNum, stmtId)) { - // Don't continue migrating the transaction history - return lastResult; + try { + if (txnParticipant->checkStatementExecuted(stmtId)) { + // Skip the incoming statement because it has already been logged locally + return lastResult; + } + } catch (const DBException& ex) { + // If the transaction chain was truncated on the recipient shard, then we are most likely + // copying from a session that hasn't been touched on the recipient shard for a very long + // time but could be recent on the donor. + // + // We continue copying regardless to get the entire transaction from the donor. + if (ex.code() != ErrorCodes::IncompleteTransactionHistory) { + throw; + } + + if (stmtId == kIncompleteHistoryStmtId) { + // No need to log entries for transactions whose history has been truncated + return lastResult; + } } BSONObj object(result.isPrePostImage diff --git a/src/mongo/db/s/session_catalog_migration_destination_test.cpp b/src/mongo/db/s/session_catalog_migration_destination_test.cpp index ffc5cd400f1..e3d00084e90 100644 --- a/src/mongo/db/s/session_catalog_migration_destination_test.cpp +++ b/src/mongo/db/s/session_catalog_migration_destination_test.cpp @@ -199,7 +199,7 @@ public: TxnNumber txnNumber, StmtId stmtId) { const auto txnParticipant = TransactionParticipant::get(session); - auto oplog = txnParticipant->checkStatementExecuted(opCtx, txnNumber, stmtId); + auto oplog = txnParticipant->checkStatementExecuted(stmtId); ASSERT_TRUE(oplog); } @@ -209,7 +209,7 @@ public: StmtId stmtId, const repl::OplogEntry& expectedOplog) { const auto txnParticipant = TransactionParticipant::get(session); - auto oplog = txnParticipant->checkStatementExecuted(opCtx, txnNumber, stmtId); + auto oplog = txnParticipant->checkStatementExecuted(stmtId); ASSERT_TRUE(oplog); checkOplogWithNestedOplog(expectedOplog, *oplog); } @@ -1572,7 +1572,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, OplogEntriesWithIncompleteHistory checkStatementExecuted(opCtx, session.get(), 2, 23, oplogEntries[0]); checkStatementExecuted(opCtx, session.get(), 2, 5, oplogEntries[2]); - ASSERT_THROWS(txnParticipant->checkStatementExecuted(opCtx, 2, 38), AssertionException); + ASSERT_THROWS(txnParticipant->checkStatementExecuted(38), AssertionException); } TEST_F(SessionCatalogMigrationDestinationTest, diff --git a/src/mongo/db/session_catalog_mongod.cpp b/src/mongo/db/session_catalog_mongod.cpp index c2ef072d72a..01bc5424644 100644 --- a/src/mongo/db/session_catalog_mongod.cpp +++ b/src/mongo/db/session_catalog_mongod.cpp @@ -192,16 +192,15 @@ void MongoDSessionCatalog::invalidateSessions(OperationContext* opCtx, MongoDOperationContextSession::MongoDOperationContextSession(OperationContext* opCtx) : _operationContextSession(opCtx) { - if (!opCtx->getClient()->isInDirectClient()) { - const auto txnParticipant = TransactionParticipant::get(opCtx); - txnParticipant->refreshFromStorageIfNeeded(); - } + invariant(!opCtx->getClient()->isInDirectClient()); + + const auto txnParticipant = TransactionParticipant::get(opCtx); + txnParticipant->refreshFromStorageIfNeeded(); } MongoDOperationContextSession::~MongoDOperationContextSession() = default; void MongoDOperationContextSession::checkIn(OperationContext* opCtx) { - if (auto txnParticipant = TransactionParticipant::get(opCtx)) { txnParticipant->stashTransactionResources(opCtx); } diff --git a/src/mongo/db/transaction_participant.cpp b/src/mongo/db/transaction_participant.cpp index 4345613469c..aa01c5e0aa2 100644 --- a/src/mongo/db/transaction_participant.cpp +++ b/src/mongo/db/transaction_participant.cpp @@ -691,8 +691,7 @@ void TransactionParticipant::unstashTransactionResources(OperationContext* opCtx { stdx::lock_guard<stdx::mutex> lg(_mutex); - // Always check session's txnNumber and '_txnState', since they can be modified by session - // kill and migration, which do not check out the session. + _checkValid(lg); _checkIsActiveTransaction(lg, *opCtx->getTxnNumber(), false); // If this is not a multi-document transaction, there is nothing to unstash. @@ -1641,12 +1640,13 @@ void TransactionParticipant::onWriteOpCompletedOnPrimary( Date_t lastStmtIdWriteDate, boost::optional<DurableTxnStateEnum> txnState) { invariant(opCtx->lockState()->inAWriteUnitOfWork()); + invariant(txnNumber == _activeTxnNumber); stdx::unique_lock<stdx::mutex> ul(_mutex); // Sanity check that we don't double-execute statements for (const auto stmtId : stmtIdsWritten) { - const auto stmtOpTime = _checkStatementExecuted(ul, txnNumber, stmtId); + const auto stmtOpTime = _checkStatementExecuted(stmtId); if (stmtOpTime) { fassertOnRepeatedExecution( _sessionId(), txnNumber, stmtId, *stmtOpTime, lastStmtIdWriteOpTime); @@ -1665,39 +1665,13 @@ void TransactionParticipant::onWriteOpCompletedOnPrimary( opCtx, txnNumber, std::move(stmtIdsWritten), lastStmtIdWriteOpTime); } -bool TransactionParticipant::onMigrateBeginOnPrimary(OperationContext* opCtx, - TxnNumber txnNumber, - StmtId stmtId) { - beginOrContinue(txnNumber, boost::none, boost::none); - - try { - if (checkStatementExecuted(opCtx, txnNumber, stmtId)) { - return false; - } - } catch (const DBException& ex) { - // If the transaction chain was truncated on the recipient shard, then we are most likely - // copying from a session that hasn't been touched on the recipient shard for a very long - // time but could be recent on the donor. - // - // We continue copying regardless to get the entire transaction from the donor. - if (ex.code() != ErrorCodes::IncompleteTransactionHistory) { - throw; - } - - if (stmtId == kIncompleteHistoryStmtId) { - return false; - } - } - - return true; -} - void TransactionParticipant::onMigrateCompletedOnPrimary(OperationContext* opCtx, TxnNumber txnNumber, std::vector<StmtId> stmtIdsWritten, const repl::OpTime& lastStmtIdWriteOpTime, Date_t oplogLastStmtIdWriteDate) { invariant(opCtx->lockState()->inAWriteUnitOfWork()); + invariant(txnNumber == _activeTxnNumber); stdx::unique_lock<stdx::mutex> ul(_mutex); @@ -1805,18 +1779,15 @@ repl::OpTime TransactionParticipant::getLastWriteOpTime(TxnNumber txnNumber) con } boost::optional<repl::OplogEntry> TransactionParticipant::checkStatementExecuted( - OperationContext* opCtx, TxnNumber txnNumber, StmtId stmtId) const { - const auto stmtTimestamp = [&] { - stdx::lock_guard<stdx::mutex> lg(_mutex); - return _checkStatementExecuted(lg, txnNumber, stmtId); - }(); + StmtId stmtId) const { + const auto stmtTimestamp = _checkStatementExecuted(stmtId); if (!stmtTimestamp) return boost::none; TransactionHistoryIterator txnIter(*stmtTimestamp); while (txnIter.hasNext()) { - const auto entry = txnIter.next(opCtx); + const auto entry = txnIter.next(_opCtx()); invariant(entry.getStatementId()); if (*entry.getStatementId() == stmtId) return entry; @@ -1825,10 +1796,8 @@ boost::optional<repl::OplogEntry> TransactionParticipant::checkStatementExecuted MONGO_UNREACHABLE; } -bool TransactionParticipant::checkStatementExecutedNoOplogEntryFetch(TxnNumber txnNumber, - StmtId stmtId) const { - stdx::lock_guard<stdx::mutex> lg(_mutex); - return bool(_checkStatementExecuted(lg, txnNumber, stmtId)); +bool TransactionParticipant::checkStatementExecutedNoOplogEntryFetch(StmtId stmtId) const { + return bool(_checkStatementExecuted(stmtId)); } void TransactionParticipant::_checkValid(WithLock) const { @@ -1849,16 +1818,13 @@ void TransactionParticipant::_checkIsActiveTransaction(WithLock, TxnNumber txnNu txnNumber == _activeTxnNumber); } -boost::optional<repl::OpTime> TransactionParticipant::_checkStatementExecuted(WithLock wl, - TxnNumber txnNumber, - StmtId stmtId) const { - _checkValid(wl); - _checkIsActiveTransaction(wl, txnNumber); +boost::optional<repl::OpTime> TransactionParticipant::_checkStatementExecuted(StmtId stmtId) const { + invariant(_isValid); const auto it = _activeTxnCommittedStatements.find(stmtId); if (it == _activeTxnCommittedStatements.end()) { uassert(ErrorCodes::IncompleteTransactionHistory, - str::stream() << "Incomplete history detected for transaction " << txnNumber + str::stream() << "Incomplete history detected for transaction " << _activeTxnNumber << " on session " << _sessionId(), !_hasIncompleteHistory); @@ -1867,7 +1833,7 @@ boost::optional<repl::OpTime> TransactionParticipant::_checkStatementExecuted(Wi } invariant(_lastWrittenSessionRecord); - invariant(_lastWrittenSessionRecord->getTxnNum() == txnNumber); + invariant(_lastWrittenSessionRecord->getTxnNum() == _activeTxnNumber); return it->second; } diff --git a/src/mongo/db/transaction_participant.h b/src/mongo/db/transaction_participant.h index 56461dedd0a..234e27f2b44 100644 --- a/src/mongo/db/transaction_participant.h +++ b/src/mongo/db/transaction_participant.h @@ -327,15 +327,6 @@ public: boost::optional<DurableTxnStateEnum> txnState); /** - * Helper function to begin a migration on a primary node. - * - * Returns whether the specified statement should be migrated at all or skipped. - * - * Not called with session checked out. - */ - bool onMigrateBeginOnPrimary(OperationContext* opCtx, TxnNumber txnNumber, StmtId stmtId); - - /** * Called after an entry for the specified session and transaction has been written to the oplog * during chunk migration, while the node is still primary. Must be called while the caller is * still in the oplog write's WUOW. Updates the on-disk state of the session to match the @@ -362,9 +353,7 @@ public: * * Throws if the session has been invalidated or the active transaction number doesn't match. */ - boost::optional<repl::OplogEntry> checkStatementExecuted(OperationContext* opCtx, - TxnNumber txnNumber, - StmtId stmtId) const; + boost::optional<repl::OplogEntry> checkStatementExecuted(StmtId stmtId) const; /** * Checks whether the given statementId for the specified transaction has already executed @@ -374,7 +363,7 @@ public: * * Throws if the session has been invalidated or the active transaction number doesn't match. */ - bool checkStatementExecutedNoOplogEntryFetch(TxnNumber txnNumber, StmtId stmtId) const; + bool checkStatementExecutedNoOplogEntryFetch(StmtId stmtId) const; /** * Marks the session as requiring refresh. Used when the session state has been modified @@ -653,9 +642,7 @@ private: // a check that the caller operates on the transaction it thinks it is operating on. void _checkIsActiveTransaction(WithLock, TxnNumber txnNumber) const; - boost::optional<repl::OpTime> _checkStatementExecuted(WithLock, - TxnNumber txnNumber, - StmtId stmtId) const; + boost::optional<repl::OpTime> _checkStatementExecuted(StmtId stmtId) const; UpdateRequest _makeUpdateRequest(WithLock, TxnNumber newTxnNumber, diff --git a/src/mongo/db/transaction_participant_retryable_writes_test.cpp b/src/mongo/db/transaction_participant_retryable_writes_test.cpp index f3900a43b5f..638f931ef20 100644 --- a/src/mongo/db/transaction_participant_retryable_writes_test.cpp +++ b/src/mongo/db/transaction_participant_retryable_writes_test.cpp @@ -384,53 +384,40 @@ TEST_F(TransactionParticipantRetryableWritesTest, CheckStatementExecuted) { const TxnNumber txnNum = 100; txnParticipant->beginOrContinue(txnNum, boost::none, boost::none); - ASSERT(!txnParticipant->checkStatementExecuted(opCtx(), txnNum, 1000)); - ASSERT(!txnParticipant->checkStatementExecutedNoOplogEntryFetch(txnNum, 1000)); + ASSERT(!txnParticipant->checkStatementExecuted(1000)); + ASSERT(!txnParticipant->checkStatementExecutedNoOplogEntryFetch(1000)); const auto firstOpTime = writeTxnRecord(txnNum, 1000, {}, boost::none); - ASSERT(txnParticipant->checkStatementExecuted(opCtx(), txnNum, 1000)); - ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(txnNum, 1000)); + ASSERT(txnParticipant->checkStatementExecuted(1000)); + ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(1000)); - ASSERT(!txnParticipant->checkStatementExecuted(opCtx(), txnNum, 2000)); - ASSERT(!txnParticipant->checkStatementExecutedNoOplogEntryFetch(txnNum, 2000)); + ASSERT(!txnParticipant->checkStatementExecuted(2000)); + ASSERT(!txnParticipant->checkStatementExecutedNoOplogEntryFetch(2000)); writeTxnRecord(txnNum, 2000, firstOpTime, boost::none); - ASSERT(txnParticipant->checkStatementExecuted(opCtx(), txnNum, 2000)); - ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(txnNum, 2000)); + ASSERT(txnParticipant->checkStatementExecuted(2000)); + ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(2000)); // Invalidate the session and ensure the statements still check out txnParticipant->invalidate(); txnParticipant->refreshFromStorageIfNeeded(); - ASSERT(txnParticipant->checkStatementExecuted(opCtx(), txnNum, 1000)); - ASSERT(txnParticipant->checkStatementExecuted(opCtx(), txnNum, 2000)); + ASSERT(txnParticipant->checkStatementExecuted(1000)); + ASSERT(txnParticipant->checkStatementExecuted(2000)); - ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(txnNum, 1000)); - ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(txnNum, 2000)); + ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(1000)); + ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(2000)); } -TEST_F(TransactionParticipantRetryableWritesTest, CheckStatementExecutedForOldTransactionThrows) { - const auto txnParticipant = TransactionParticipant::get(opCtx()); - txnParticipant->refreshFromStorageIfNeeded(); - - const TxnNumber txnNum = 100; - txnParticipant->beginOrContinue(txnNum, boost::none, boost::none); - - ASSERT_THROWS_CODE(txnParticipant->checkStatementExecuted(opCtx(), txnNum - 1, 0), - AssertionException, - ErrorCodes::ConflictingOperationInProgress); -} - -TEST_F(TransactionParticipantRetryableWritesTest, - CheckStatementExecutedForInvalidatedTransactionThrows) { +DEATH_TEST_F(TransactionParticipantRetryableWritesTest, + CheckStatementExecutedForInvalidatedTransactionInvariants, + "Invariant failure _isValid") { const auto txnParticipant = TransactionParticipant::get(opCtx()); txnParticipant->invalidate(); - - ASSERT_THROWS_CODE(txnParticipant->checkStatementExecuted(opCtx(), 100, 0), - AssertionException, - ErrorCodes::ConflictingOperationInProgress); + txnParticipant->checkStatementExecuted(0); } -TEST_F(TransactionParticipantRetryableWritesTest, - WriteOpCompletedOnPrimaryForOldTransactionThrows) { +DEATH_TEST_F(TransactionParticipantRetryableWritesTest, + WriteOpCompletedOnPrimaryForOldTransactionInvariants, + "Invariant failure txnNumber == _activeTxnNumber") { const auto txnParticipant = TransactionParticipant::get(opCtx()); txnParticipant->refreshFromStorageIfNeeded(); @@ -453,15 +440,14 @@ TEST_F(TransactionParticipantRetryableWritesTest, AutoGetCollection autoColl(opCtx(), kNss, MODE_IX); WriteUnitOfWork wuow(opCtx()); const auto opTime = logOp(opCtx(), kNss, uuid, sessionId, txnNum - 1, 0); - ASSERT_THROWS_CODE(txnParticipant->onWriteOpCompletedOnPrimary( - opCtx(), txnNum - 1, {0}, opTime, Date_t::now(), boost::none), - AssertionException, - ErrorCodes::ConflictingOperationInProgress); + txnParticipant->onWriteOpCompletedOnPrimary( + opCtx(), txnNum - 1, {0}, opTime, Date_t::now(), boost::none); } } -TEST_F(TransactionParticipantRetryableWritesTest, - WriteOpCompletedOnPrimaryForInvalidatedTransactionThrows) { +DEATH_TEST_F(TransactionParticipantRetryableWritesTest, + WriteOpCompletedOnPrimaryForInvalidatedTransactionInvariants, + "Invariant failure txnNumber == _activeTxnNumber") { const auto txnParticipant = TransactionParticipant::get(opCtx()); txnParticipant->refreshFromStorageIfNeeded(); @@ -474,11 +460,8 @@ TEST_F(TransactionParticipantRetryableWritesTest, const auto opTime = logOp(opCtx(), kNss, uuid, *opCtx()->getLogicalSessionId(), txnNum, 0); txnParticipant->invalidate(); - - ASSERT_THROWS_CODE(txnParticipant->onWriteOpCompletedOnPrimary( - opCtx(), txnNum, {0}, opTime, Date_t::now(), boost::none), - AssertionException, - ErrorCodes::ConflictingOperationInProgress); + txnParticipant->onWriteOpCompletedOnPrimary( + opCtx(), txnNum, {0}, opTime, Date_t::now(), boost::none); } TEST_F(TransactionParticipantRetryableWritesTest, @@ -503,7 +486,7 @@ TEST_F(TransactionParticipantRetryableWritesTest, } txnParticipant->refreshFromStorageIfNeeded(); - ASSERT(txnParticipant->checkStatementExecuted(opCtx(), txnNum, 0)); + ASSERT(txnParticipant->checkStatementExecuted(0)); } TEST_F(TransactionParticipantRetryableWritesTest, IncompleteHistoryDueToOpLogTruncation) { @@ -561,17 +544,17 @@ TEST_F(TransactionParticipantRetryableWritesTest, IncompleteHistoryDueToOpLogTru const auto txnParticipant = TransactionParticipant::get(opCtx()); txnParticipant->refreshFromStorageIfNeeded(); - ASSERT_THROWS_CODE(txnParticipant->checkStatementExecuted(opCtx(), txnNum, 0), + ASSERT_THROWS_CODE(txnParticipant->checkStatementExecuted(0), AssertionException, ErrorCodes::IncompleteTransactionHistory); - ASSERT(txnParticipant->checkStatementExecuted(opCtx(), txnNum, 1)); - ASSERT(txnParticipant->checkStatementExecuted(opCtx(), txnNum, 2)); + ASSERT(txnParticipant->checkStatementExecuted(1)); + ASSERT(txnParticipant->checkStatementExecuted(2)); - ASSERT_THROWS_CODE(txnParticipant->checkStatementExecutedNoOplogEntryFetch(txnNum, 0), + ASSERT_THROWS_CODE(txnParticipant->checkStatementExecutedNoOplogEntryFetch(0), AssertionException, ErrorCodes::IncompleteTransactionHistory); - ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(txnNum, 1)); - ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(txnNum, 2)); + ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(1)); + ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(2)); } TEST_F(TransactionParticipantRetryableWritesTest, ErrorOnlyWhenStmtIdBeingCheckedIsNotInCache) { @@ -642,24 +625,24 @@ TEST_F(TransactionParticipantRetryableWritesTest, ErrorOnlyWhenStmtIdBeingChecke } { - auto oplog = txnParticipant->checkStatementExecuted(opCtx(), txnNum, 1); + auto oplog = txnParticipant->checkStatementExecuted(1); ASSERT_TRUE(oplog); ASSERT_EQ(firstOpTime, oplog->getOpTime()); } - ASSERT_THROWS(txnParticipant->checkStatementExecuted(opCtx(), txnNum, 2), AssertionException); + ASSERT_THROWS(txnParticipant->checkStatementExecuted(2), AssertionException); // Should have the same behavior after loading state from storage. txnParticipant->invalidate(); txnParticipant->refreshFromStorageIfNeeded(); { - auto oplog = txnParticipant->checkStatementExecuted(opCtx(), txnNum, 1); + auto oplog = txnParticipant->checkStatementExecuted(1); ASSERT_TRUE(oplog); ASSERT_EQ(firstOpTime, oplog->getOpTime()); } - ASSERT_THROWS(txnParticipant->checkStatementExecuted(opCtx(), txnNum, 2), AssertionException); + ASSERT_THROWS(txnParticipant->checkStatementExecuted(2), AssertionException); } } // namespace diff --git a/src/mongo/db/transaction_participant_test.cpp b/src/mongo/db/transaction_participant_test.cpp index 773b5c42b5a..6bc19f98e03 100644 --- a/src/mongo/db/transaction_participant_test.cpp +++ b/src/mongo/db/transaction_participant_test.cpp @@ -1150,8 +1150,6 @@ TEST_F(TxnParticipantTest, StashInNestedSessionIsANoop) { { // Make it look like we're in a DBDirectClient running a nested operation. DirectClientSetter inDirectClient(opCtx()); - MongoDOperationContextSession innerScopedSession(opCtx()); - txnParticipant->stashTransactionResources(opCtx()); // The stash was a noop, so the locker, RecoveryUnit, and WriteUnitOfWork on the |