diff options
author | Tess Avitabile <tess.avitabile@mongodb.com> | 2018-05-07 14:15:52 -0400 |
---|---|---|
committer | Tess Avitabile <tess.avitabile@mongodb.com> | 2018-05-08 13:52:43 -0400 |
commit | 98f28d452b9b330d6c1696d6d8207b582a5870fc (patch) | |
tree | a99763113d3c804ed7a8a0f41b1e522d0d0e1721 /src | |
parent | 13e636974a37588232ff33a422b94bbe77f351cc (diff) | |
download | mongo-98f28d452b9b330d6c1696d6d8207b582a5870fc.tar.gz |
SERVER-34811 Forbid reads and writes to the config, admin, and local databases within transactions
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/op_observer_impl_test.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/repl/do_txn_test.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/s/session_catalog_migration_destination_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_common.cpp | 52 | ||||
-rw-r--r-- | src/mongo/db/session.cpp | 36 | ||||
-rw-r--r-- | src/mongo/db/session.h | 4 | ||||
-rw-r--r-- | src/mongo/db/session_catalog.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/session_catalog.h | 4 | ||||
-rw-r--r-- | src/mongo/db/session_catalog_test.cpp | 24 | ||||
-rw-r--r-- | src/mongo/db/session_test.cpp | 85 |
10 files changed, 124 insertions, 99 deletions
diff --git a/src/mongo/db/op_observer_impl_test.cpp b/src/mongo/db/op_observer_impl_test.cpp index 0479e8198e5..3ca215f7133 100644 --- a/src/mongo/db/op_observer_impl_test.cpp +++ b/src/mongo/db/op_observer_impl_test.cpp @@ -307,7 +307,7 @@ public: NamespaceString nss, TxnNumber txnNum, StmtId stmtId) { - session->beginOrContinueTxn(opCtx, txnNum, boost::none, boost::none); + session->beginOrContinueTxn(opCtx, txnNum, boost::none, boost::none, "testDB", "insert"); { AutoGetCollection autoColl(opCtx, nss, MODE_IX); @@ -407,7 +407,9 @@ TEST_F(OpObserverLargeTransactionTest, TransactionTooLargeWhileCommitting) { OperationContextSession opSession(opCtx.get(), true /* checkOutSession */, false /* autocommit */, - true /* startTransaction*/); + true /* startTransaction */, + "testDB" /* dbName */, + "insert" /* cmdName */); session->unstashTransactionResources(opCtx.get(), "insert"); diff --git a/src/mongo/db/repl/do_txn_test.cpp b/src/mongo/db/repl/do_txn_test.cpp index d6dde975452..17ea4dc6d21 100644 --- a/src/mongo/db/repl/do_txn_test.cpp +++ b/src/mongo/db/repl/do_txn_test.cpp @@ -148,7 +148,9 @@ void DoTxnTest::setUp() { _ocs.emplace(_opCtx.get(), true /* checkOutSession */, false /* autocommit */, - true /* startTransaction */); + true /* startTransaction */, + "admin" /* dbName */, + "doTxn" /* cmdName */); OperationContextSession::get(opCtx())->unstashTransactionResources(opCtx(), "doTxn"); } 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 fdbe9167c58..010ae23f768 100644 --- a/src/mongo/db/s/session_catalog_migration_destination_test.cpp +++ b/src/mongo/db/s/session_catalog_migration_destination_test.cpp @@ -243,7 +243,7 @@ public: // up the session state and perform the insert. initializeOperationSessionInfo(innerOpCtx.get(), insertBuilder.obj(), true, true, true); OperationContextSession sessionTxnState( - innerOpCtx.get(), true, boost::none, boost::none); + innerOpCtx.get(), true, boost::none, boost::none, "testDB", "insert"); const auto reply = performInserts(innerOpCtx.get(), insertRequest); ASSERT(reply.results.size() == 1); diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp index 46554839027..10f26c5be5f 100644 --- a/src/mongo/db/service_entry_point_common.cpp +++ b/src/mongo/db/service_entry_point_common.cpp @@ -132,23 +132,6 @@ const StringMap<int> sessionCheckoutWhitelist = {{"abortTransaction", 1}, {"refreshLogicalSessionCacheNow", 1}, {"update", 1}}; -// The command names that are allowed in a multi-document transaction. -const StringMap<int> txnCmdWhitelist = {{"abortTransaction", 1}, - {"aggregate", 1}, - {"commitTransaction", 1}, - {"count", 1}, - {"delete", 1}, - {"distinct", 1}, - {"doTxn", 1}, - {"find", 1}, - {"findandmodify", 1}, - {"findAndModify", 1}, - {"geoSearch", 1}, - {"getMore", 1}, - {"insert", 1}, - {"prepareTransaction", 1}, - {"update", 1}}; - // The command names that are ignored by the 'failCommand' failpoint. const StringMap<int> failCommandIgnoreList = { {"buildInfo", 1}, {"configureFailPoint", 1}, {"isMaster", 1}, {"ping", 1}}; @@ -602,6 +585,12 @@ void execCommandDatabase(OperationContext* opCtx, replCoord->getReplicationMode() == repl::ReplicationCoordinator::modeReplSet, opCtx->getServiceContext()->getStorageEngine()->supportsDocLocking()); + const auto dbname = request.getDatabase().toString(); + uassert( + ErrorCodes::InvalidNamespace, + str::stream() << "Invalid database name: '" << dbname << "'", + NamespaceString::validDBName(dbname, NamespaceString::DollarInDbNameBehavior::Allow)); + // Session ids are forwarded in requests, so commands that require roundtrips between // servers may result in a deadlock when a server tries to check out a session it is already // using to service an earlier operation in the command's chain. To avoid this, only check @@ -624,12 +613,6 @@ void execCommandDatabase(OperationContext* opCtx, } } - uassert(50767, - str::stream() << "Cannot run '" << command->getName() - << "' in a multi-document transaction.", - !autocommitVal || - txnCmdWhitelist.find(command->getName()) != txnCmdWhitelist.cend()); - // Reject commands with 'txnNumber' that do not check out the Session, since no retryable // writes or transaction machinery will be used to execute commands that do not check out // the Session. Do not check this if we are in DBDirectClient because the outer command is @@ -644,23 +627,12 @@ void execCommandDatabase(OperationContext* opCtx, // This constructor will check out the session and start a transaction, if necessary. It // handles the appropriate state management for both multi-statement transactions and // retryable writes. - OperationContextSession sessionTxnState( - opCtx, shouldCheckoutSession, autocommitVal, startMultiDocTxn); - - // If we are in a multi-document transaction, ensure the command is allowed in this context. - // We do not check this in DBDirectClient, since 'aggregate' is allowed in transactions, but - // 'geoNear' is not, and 'aggregate' can run 'geoNear' in DBDirectClient. - if (!opCtx->getClient()->isInDirectClient()) { - auto session = OperationContextSession::get(opCtx); - invariant(!session || !session->inMultiDocumentTransaction() || - txnCmdWhitelist.find(command->getName()) != txnCmdWhitelist.cend()); - } - - const auto dbname = request.getDatabase().toString(); - uassert( - ErrorCodes::InvalidNamespace, - str::stream() << "Invalid database name: '" << dbname << "'", - NamespaceString::validDBName(dbname, NamespaceString::DollarInDbNameBehavior::Allow)); + OperationContextSession sessionTxnState(opCtx, + shouldCheckoutSession, + autocommitVal, + startMultiDocTxn, + dbname, + command->getName()); std::unique_ptr<MaintenanceModeSetter> mmSetter; diff --git a/src/mongo/db/session.cpp b/src/mongo/db/session.cpp index fa04cc871de..db85322dcb1 100644 --- a/src/mongo/db/session.cpp +++ b/src/mongo/db/session.cpp @@ -97,6 +97,27 @@ public: namespace { +// The command names that are allowed in a multi-document transaction. +const StringMap<int> txnCmdWhitelist = {{"abortTransaction", 1}, + {"aggregate", 1}, + {"commitTransaction", 1}, + {"count", 1}, + {"delete", 1}, + {"distinct", 1}, + {"doTxn", 1}, + {"find", 1}, + {"findandmodify", 1}, + {"findAndModify", 1}, + {"geoSearch", 1}, + {"getMore", 1}, + {"insert", 1}, + {"prepareTransaction", 1}, + {"update", 1}}; + +// The commands that can be run on the 'admin' database in multi-document transactions. +const StringMap<int> txnAdminCommands = { + {"abortTransaction", 1}, {"commitTransaction", 1}, {"doTxn", 1}, {"prepareTransaction", 1}}; + void fassertOnRepeatedExecution(const LogicalSessionId& lsid, TxnNumber txnNumber, StmtId stmtId, @@ -319,13 +340,26 @@ void Session::refreshFromStorageIfNeeded(OperationContext* opCtx) { void Session::beginOrContinueTxn(OperationContext* opCtx, TxnNumber txnNumber, boost::optional<bool> autocommit, - boost::optional<bool> startTransaction) { + boost::optional<bool> startTransaction, + StringData dbName, + StringData cmdName) { if (opCtx->getClient()->isInDirectClient()) { return; } invariant(!opCtx->lockState()->isLocked()); + uassert(50767, + str::stream() << "Cannot run '" << cmdName << "' in a multi-document transaction.", + !autocommit || txnCmdWhitelist.find(cmdName) != txnCmdWhitelist.cend()); + + uassert(50844, + str::stream() << "Cannot run command against the '" << dbName + << "' database in a transaction", + !autocommit || (dbName != "config"_sd && dbName != "local"_sd && + (dbName != "admin"_sd || + txnAdminCommands.find(cmdName) != txnAdminCommands.cend()))); + TxnNumber txnNumberAtStart; bool canKillCursors = false; { diff --git a/src/mongo/db/session.h b/src/mongo/db/session.h index 3ed07c8c470..38858c3a162 100644 --- a/src/mongo/db/session.h +++ b/src/mongo/db/session.h @@ -152,7 +152,9 @@ public: void beginOrContinueTxn(OperationContext* opCtx, TxnNumber txnNumber, boost::optional<bool> autocommit, - boost::optional<bool> startTransaction); + boost::optional<bool> startTransaction, + StringData dbName, + StringData cmdName); /** * Similar to beginOrContinueTxn except it is used specifically for shard migrations and does * not check or modify the autocommit parameter. diff --git a/src/mongo/db/session_catalog.cpp b/src/mongo/db/session_catalog.cpp index d70b68da890..6dd27f2f501 100644 --- a/src/mongo/db/session_catalog.cpp +++ b/src/mongo/db/session_catalog.cpp @@ -238,7 +238,9 @@ void SessionCatalog::_releaseSession(const LogicalSessionId& lsid) { OperationContextSession::OperationContextSession(OperationContext* opCtx, bool checkOutSession, boost::optional<bool> autocommit, - boost::optional<bool> startTransaction) + boost::optional<bool> startTransaction, + StringData dbName, + StringData cmdName) : _opCtx(opCtx) { if (!opCtx->getLogicalSessionId()) { @@ -267,7 +269,7 @@ OperationContextSession::OperationContextSession(OperationContext* opCtx, if (opCtx->getTxnNumber()) { checkedOutSession->get()->beginOrContinueTxn( - opCtx, *opCtx->getTxnNumber(), autocommit, startTransaction); + opCtx, *opCtx->getTxnNumber(), autocommit, startTransaction, dbName, cmdName); } } diff --git a/src/mongo/db/session_catalog.h b/src/mongo/db/session_catalog.h index fcc2164ca18..18e33d8b385 100644 --- a/src/mongo/db/session_catalog.h +++ b/src/mongo/db/session_catalog.h @@ -250,7 +250,9 @@ public: OperationContextSession(OperationContext* opCtx, bool checkOutSession, boost::optional<bool> autocommit, - boost::optional<bool> startTransaction); + boost::optional<bool> startTransaction, + StringData dbName, + StringData cmdName); ~OperationContextSession(); diff --git a/src/mongo/db/session_catalog_test.cpp b/src/mongo/db/session_catalog_test.cpp index 922e4143130..2dd8279a1c7 100644 --- a/src/mongo/db/session_catalog_test.cpp +++ b/src/mongo/db/session_catalog_test.cpp @@ -89,7 +89,7 @@ TEST_F(SessionCatalogTest, OperationContextCheckedOutSession) { const TxnNumber txnNum = 20; opCtx()->setTxnNumber(txnNum); - OperationContextSession ocs(opCtx(), true, boost::none, boost::none); + OperationContextSession ocs(opCtx(), true, boost::none, boost::none, "testDB", "insert"); auto session = OperationContextSession::get(opCtx()); ASSERT(session); ASSERT_EQ(*opCtx()->getLogicalSessionId(), session->getSessionId()); @@ -98,7 +98,7 @@ TEST_F(SessionCatalogTest, OperationContextCheckedOutSession) { TEST_F(SessionCatalogTest, OperationContextNonCheckedOutSession) { opCtx()->setLogicalSessionId(makeLogicalSessionIdForTest()); - OperationContextSession ocs(opCtx(), false, boost::none, boost::none); + OperationContextSession ocs(opCtx(), false, boost::none, boost::none, "testDB", "insert"); auto session = OperationContextSession::get(opCtx()); ASSERT(!session); @@ -117,7 +117,7 @@ TEST_F(SessionCatalogTest, GetOrCreateSessionAfterCheckOutSession) { opCtx()->setLogicalSessionId(lsid); boost::optional<OperationContextSession> ocs; - ocs.emplace(opCtx(), true, boost::none, false); + ocs.emplace(opCtx(), true, boost::none, false, "testDB", "insert"); stdx::async(stdx::launch::async, [&] { Client::initThreadIfNotAlready(); @@ -146,11 +146,13 @@ TEST_F(SessionCatalogTest, NestedOperationContextSession) { opCtx()->setLogicalSessionId(makeLogicalSessionIdForTest()); { - OperationContextSession outerScopedSession(opCtx(), true, boost::none, boost::none); + OperationContextSession outerScopedSession( + opCtx(), true, boost::none, boost::none, "testDB", "insert"); { DirectClientSetter inDirectClient(opCtx()); - OperationContextSession innerScopedSession(opCtx(), true, boost::none, boost::none); + OperationContextSession innerScopedSession( + opCtx(), true, boost::none, boost::none, "testDB", "insert"); auto session = OperationContextSession::get(opCtx()); ASSERT(session); @@ -173,7 +175,8 @@ TEST_F(SessionCatalogTest, StashInNestedSessionIsANoop) { opCtx()->setTxnNumber(1); { - OperationContextSession outerScopedSession(opCtx(), true, boost::none, boost::none); + OperationContextSession outerScopedSession( + opCtx(), true, boost::none, boost::none, "testDB", "find"); Locker* originalLocker = opCtx()->lockState(); RecoveryUnit* originalRecoveryUnit = opCtx()->recoveryUnit(); @@ -198,7 +201,8 @@ TEST_F(SessionCatalogTest, StashInNestedSessionIsANoop) { { // Make it look like we're in a DBDirectClient running a nested operation. DirectClientSetter inDirectClient(opCtx()); - OperationContextSession innerScopedSession(opCtx(), true, boost::none, boost::none); + OperationContextSession innerScopedSession( + opCtx(), true, boost::none, boost::none, "testDB", "find"); // Report to Session that there is a stashed cursor. If we were not in a nested session, // this would ensure that stashing is not a noop. @@ -220,7 +224,8 @@ TEST_F(SessionCatalogTest, UnstashInNestedSessionIsANoop) { opCtx()->setTxnNumber(1); { - OperationContextSession outerScopedSession(opCtx(), true, boost::none, boost::none); + OperationContextSession outerScopedSession( + opCtx(), true, boost::none, boost::none, "testDB", "find"); Locker* originalLocker = opCtx()->lockState(); RecoveryUnit* originalRecoveryUnit = opCtx()->recoveryUnit(); @@ -239,7 +244,8 @@ TEST_F(SessionCatalogTest, UnstashInNestedSessionIsANoop) { { // Make it look like we're in a DBDirectClient running a nested operation. DirectClientSetter inDirectClient(opCtx()); - OperationContextSession innerScopedSession(opCtx(), true, boost::none, boost::none); + OperationContextSession innerScopedSession( + opCtx(), true, boost::none, boost::none, "testDB", "find"); OperationContextSession::get(opCtx())->unstashTransactionResources(opCtx(), "find"); diff --git a/src/mongo/db/session_test.cpp b/src/mongo/db/session_test.cpp index fc07d137576..9b55555a2f6 100644 --- a/src/mongo/db/session_test.cpp +++ b/src/mongo/db/session_test.cpp @@ -176,7 +176,7 @@ TEST_F(SessionTest, SessionEntryNotWrittenOnBegin) { session.refreshFromStorageIfNeeded(opCtx()); const TxnNumber txnNum = 20; - session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none); + session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none, "testDB", "insert"); ASSERT_EQ(sessionId, session.getSessionId()); ASSERT(session.getLastWriteOpTime(txnNum).isNull()); @@ -194,7 +194,7 @@ TEST_F(SessionTest, SessionEntryWrittenAtFirstWrite) { session.refreshFromStorageIfNeeded(opCtx()); const TxnNumber txnNum = 21; - session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none); + session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none, "testDB", "insert"); const auto opTime = [&] { AutoGetCollection autoColl(opCtx(), kNss, MODE_IX); @@ -227,7 +227,7 @@ TEST_F(SessionTest, StartingNewerTransactionUpdatesThePersistedSession) { session.refreshFromStorageIfNeeded(opCtx()); const auto writeTxnRecordFn = [&](TxnNumber txnNum, StmtId stmtId, repl::OpTime prevOpTime) { - session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none); + session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none, "testDB", "insert"); AutoGetCollection autoColl(opCtx(), kNss, MODE_IX); WriteUnitOfWork wuow(opCtx()); @@ -266,9 +266,10 @@ TEST_F(SessionTest, StartingOldTxnShouldAssert) { session.refreshFromStorageIfNeeded(opCtx()); const TxnNumber txnNum = 20; - session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none); + session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none, "testDB", "insert"); - ASSERT_THROWS_CODE(session.beginOrContinueTxn(opCtx(), txnNum - 1, boost::none, boost::none), + ASSERT_THROWS_CODE(session.beginOrContinueTxn( + opCtx(), txnNum - 1, boost::none, boost::none, "testDB", "insert"), AssertionException, ErrorCodes::TransactionTooOld); ASSERT(session.getLastWriteOpTime(txnNum).isNull()); @@ -286,7 +287,7 @@ TEST_F(SessionTest, SessionTransactionsCollectionNotDefaultCreated) { ASSERT(client.runCommand(nss.db().toString(), BSON("drop" << nss.coll()), dropResult)); const TxnNumber txnNum = 21; - session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none); + session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none, "testDB", "insert"); AutoGetCollection autoColl(opCtx(), kNss, MODE_IX); WriteUnitOfWork wuow(opCtx()); @@ -301,7 +302,7 @@ TEST_F(SessionTest, CheckStatementExecuted) { session.refreshFromStorageIfNeeded(opCtx()); const TxnNumber txnNum = 100; - session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none); + session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none, "testDB", "insert"); const auto writeTxnRecordFn = [&](StmtId stmtId, repl::OpTime prevOpTime) { AutoGetCollection autoColl(opCtx(), kNss, MODE_IX); @@ -342,7 +343,7 @@ TEST_F(SessionTest, CheckStatementExecutedForOldTransactionThrows) { session.refreshFromStorageIfNeeded(opCtx()); const TxnNumber txnNum = 100; - session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none); + session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none, "testDB", "insert"); ASSERT_THROWS_CODE(session.checkStatementExecuted(opCtx(), txnNum - 1, 0), AssertionException, @@ -365,7 +366,7 @@ TEST_F(SessionTest, WriteOpCompletedOnPrimaryForOldTransactionThrows) { session.refreshFromStorageIfNeeded(opCtx()); const TxnNumber txnNum = 100; - session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none); + session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none, "testDB", "insert"); { AutoGetCollection autoColl(opCtx(), kNss, MODE_IX); @@ -392,7 +393,7 @@ TEST_F(SessionTest, WriteOpCompletedOnPrimaryForInvalidatedTransactionThrows) { session.refreshFromStorageIfNeeded(opCtx()); const TxnNumber txnNum = 100; - session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none); + session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none, "testDB", "insert"); AutoGetCollection autoColl(opCtx(), kNss, MODE_IX); WriteUnitOfWork wuow(opCtx()); @@ -412,7 +413,7 @@ TEST_F(SessionTest, WriteOpCompletedOnPrimaryCommitIgnoresInvalidation) { session.refreshFromStorageIfNeeded(opCtx()); const TxnNumber txnNum = 100; - session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none); + session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none, "testDB", "insert"); { AutoGetCollection autoColl(opCtx(), kNss, MODE_IX); @@ -507,7 +508,7 @@ TEST_F(SessionTest, ErrorOnlyWhenStmtIdBeingCheckedIsNotInCache) { Session session(sessionId); session.refreshFromStorageIfNeeded(opCtx()); - session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none); + session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none, "testDB", "insert"); auto firstOpTime = ([&]() { AutoGetCollection autoColl(opCtx(), kNss, MODE_IX); @@ -588,7 +589,7 @@ DEATH_TEST_F(SessionTest, CommitWithoutCursorKillFunctionInvariants, "_cursorKil const TxnNumber txnNum = 26; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "find"); session.unstashTransactionResources(opCtx(), "find"); session.commitTransaction(opCtx()); @@ -610,7 +611,7 @@ TEST_F(SessionTest, StashAndUnstashResources) { Session session(sessionId); session.refreshFromStorageIfNeeded(opCtx()); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "find"); repl::ReadConcernArgs readConcernArgs; ASSERT_OK(readConcernArgs.initialize(BSON("find" @@ -664,7 +665,7 @@ TEST_F(SessionTest, ReportStashedResources) { Session session(sessionId); session.refreshFromStorageIfNeeded(opCtx()); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "find"); repl::ReadConcernArgs readConcernArgs; ASSERT_OK(readConcernArgs.initialize(BSON("find" @@ -734,14 +735,14 @@ TEST_F(SessionTest, CannotSpecifyStartTransactionOnInProgressTxn) { const TxnNumber txnNum = 100; // Must specify startTransaction=true and autocommit=false to start a transaction. - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"); // Autocommit should be set to false and we should be in a mult-doc transaction. ASSERT_FALSE(session.getAutocommit()); ASSERT_TRUE(session.inSnapshotReadOrMultiDocumentTransaction()); // Cannot try to start a transaction that already started. - ASSERT_THROWS_CODE(session.beginOrContinueTxn(opCtx(), txnNum, false, true), + ASSERT_THROWS_CODE(session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"), AssertionException, ErrorCodes::ConflictingOperationInProgress); } @@ -757,7 +758,7 @@ TEST_F(SessionTest, AutocommitRequiredOnEveryTxnOp) { const TxnNumber txnNum = 100; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"); // We must have stashed transaction resources to do a second operation on the transaction. session.unstashTransactionResources(opCtx(), "insert"); @@ -769,17 +770,19 @@ TEST_F(SessionTest, AutocommitRequiredOnEveryTxnOp) { ASSERT_FALSE(session.getAutocommit()); // Omitting 'autocommit' after the first statement of a transaction should throw an error. - ASSERT_THROWS_CODE(session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none), - AssertionException, - ErrorCodes::InvalidOptions); + ASSERT_THROWS_CODE( + session.beginOrContinueTxn(opCtx(), txnNum, boost::none, boost::none, "testDB", "insert"), + AssertionException, + ErrorCodes::InvalidOptions); // Setting 'autocommit=true' should throw an error. - ASSERT_THROWS_CODE(session.beginOrContinueTxn(opCtx(), txnNum, true, boost::none), - AssertionException, - ErrorCodes::InvalidOptions); + ASSERT_THROWS_CODE( + session.beginOrContinueTxn(opCtx(), txnNum, true, boost::none, "testDB", "insert"), + AssertionException, + ErrorCodes::InvalidOptions); // Including autocommit=false should succeed. - session.beginOrContinueTxn(opCtx(), txnNum, false, boost::none); + session.beginOrContinueTxn(opCtx(), txnNum, false, boost::none, "testDB", "insert"); } TEST_F(SessionTest, SameTransactionPreservesStoredStatements) { @@ -792,7 +795,7 @@ TEST_F(SessionTest, SameTransactionPreservesStoredStatements) { const TxnNumber txnNum = 22; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"); // We must have stashed transaction resources to re-open the transaction. session.unstashTransactionResources(opCtx(), "insert"); @@ -807,7 +810,7 @@ TEST_F(SessionTest, SameTransactionPreservesStoredStatements) { ASSERT_BSONOBJ_EQ(operation.toBSON(), session.transactionOperationsForTest()[0].toBSON()); // Re-opening the same transaction should have no effect. - session.beginOrContinueTxn(opCtx(), txnNum, false, boost::none); + session.beginOrContinueTxn(opCtx(), txnNum, false, boost::none, "testDB", "insert"); ASSERT_BSONOBJ_EQ(operation.toBSON(), session.transactionOperationsForTest()[0].toBSON()); } @@ -821,7 +824,7 @@ TEST_F(SessionTest, AbortClearsStoredStatements) { const TxnNumber txnNum = 24; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"); session.unstashTransactionResources(opCtx(), "insert"); auto operation = repl::OplogEntry::makeInsertOperation(kNss, kUUID, BSON("TestValue" << 0)); session.addTransactionOperation(opCtx(), operation); @@ -846,7 +849,7 @@ TEST_F(SessionTest, EmptyTransactionCommit) { const TxnNumber txnNum = 25; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "admin", "commitTransaction"); session.unstashTransactionResources(opCtx(), "commitTransaction"); // The transaction machinery cannot store an empty locker. Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); @@ -867,7 +870,7 @@ TEST_F(SessionTest, EmptyTransactionAbort) { const TxnNumber txnNum = 26; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "admin", "abortTransaction"); session.unstashTransactionResources(opCtx(), "abortTransaction"); // The transaction machinery cannot store an empty locker. { Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); } @@ -886,7 +889,7 @@ TEST_F(SessionTest, ConcurrencyOfUnstashAndAbort) { const TxnNumber txnNum = 26; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "find"); // The transaction may be aborted without checking out the session. session.abortArbitraryTransaction(opCtx(), kKillCursors); @@ -907,7 +910,7 @@ TEST_F(SessionTest, ConcurrencyOfUnstashAndMigration) { const TxnNumber txnNum = 26; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"); session.unstashTransactionResources(opCtx(), "insert"); // The transaction machinery cannot store an empty locker. @@ -936,7 +939,7 @@ TEST_F(SessionTest, ConcurrencyOfStashAndAbort) { const TxnNumber txnNum = 26; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "find"); session.unstashTransactionResources(opCtx(), "find"); @@ -957,7 +960,7 @@ TEST_F(SessionTest, ConcurrencyOfStashAndMigration) { const TxnNumber txnNum = 26; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"); session.unstashTransactionResources(opCtx(), "insert"); auto operation = repl::OplogEntry::makeInsertOperation(kNss, kUUID, BSON("TestValue" << 0)); @@ -983,7 +986,7 @@ TEST_F(SessionTest, ConcurrencyOfAddTransactionOperationAndAbort) { const TxnNumber txnNum = 26; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"); session.unstashTransactionResources(opCtx(), "insert"); @@ -1007,7 +1010,7 @@ TEST_F(SessionTest, ConcurrencyOfAddTransactionOperationAndMigration) { const TxnNumber txnNum = 26; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "find"); session.unstashTransactionResources(opCtx(), "find"); auto operation = repl::OplogEntry::makeInsertOperation(kNss, kUUID, BSON("TestValue" << 0)); @@ -1034,7 +1037,7 @@ TEST_F(SessionTest, ConcurrencyOfEndTransactionAndRetrieveOperationsAndAbort) { const TxnNumber txnNum = 26; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"); session.unstashTransactionResources(opCtx(), "insert"); @@ -1057,7 +1060,7 @@ TEST_F(SessionTest, ConcurrencyOfEndTransactionAndRetrieveOperationsAndMigration const TxnNumber txnNum = 26; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"); session.unstashTransactionResources(opCtx(), "insert"); auto operation = repl::OplogEntry::makeInsertOperation(kNss, kUUID, BSON("TestValue" << 0)); @@ -1084,7 +1087,7 @@ TEST_F(SessionTest, ConcurrencyOfCommitTransactionAndAbort) { const TxnNumber txnNum = 26; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "admin", "commitTransaction"); session.unstashTransactionResources(opCtx(), "commitTransaction"); @@ -1106,7 +1109,7 @@ TEST_F(SessionTest, ConcurrencyOfCommitTransactionAndMigration) { const TxnNumber txnNum = 26; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"); session.unstashTransactionResources(opCtx(), "insert"); auto operation = repl::OplogEntry::makeInsertOperation(kNss, kUUID, BSON("TestValue" << 0)); @@ -1132,7 +1135,7 @@ TEST_F(SessionTest, TransactionTooLargeWhileBuilding) { const TxnNumber txnNum = 28; opCtx()->setLogicalSessionId(sessionId); opCtx()->setTxnNumber(txnNum); - session.beginOrContinueTxn(opCtx(), txnNum, false, true); + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"); session.unstashTransactionResources(opCtx(), "insert"); |