summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Schwerin <schwerin@mongodb.com>2019-02-15 11:48:14 -0500
committerAndy Schwerin <schwerin@mongodb.com>2019-02-15 11:48:50 -0500
commit1d246814a058073f0c26981fff5fe67c16af3593 (patch)
treec107be594a1391c59a327e0a6d0d99a986bd3553
parent77d9948209153dbbc3c654109583e5ebc5e57307 (diff)
downloadmongo-1d246814a058073f0c26981fff5fe67c16af3593.tar.gz
SERVER-38810 Use Session's concurrency control rules instead of internal mutexes in TransactionParticipant
This type simplifies and clarifies concurrency control in TransactionParticipant by: (1) Removing TP's own mutexes and using the Session concurrency control rules, instead. That is, certain state is only accessible when the Session is checked out, and requires no further locking. Other state is observable either while holding the client lock or while having checked out the Session. The latter type of state is modifiable only when having checked out the session and locked the client. (2) Separating the two types of state in (1) into separate sub-structures in TransactionParticipant, to make it clear who can access what state, (3) Putting all methods formerly on TransactionParticipant onto new member classes, TransactionParticipant::Participant and TransactionParticipant::Observer. The latter can only read the observable state from (1) above, and the participant may read or modify all of the state. The two types introduced by (3) are designed to enforce proper concurrency control by limiting access of their methods to the underlying TransactionParticipant member variables. The observer type has a private o() method which its other methods are required by convention to use in order to obtain read-only access to the Observable state of the TransactionParticipant. The participant type has the o() method plus an o(WithLock) method that allows mutation of the state while holding the client lock, and a p() method which allows reading and writing of the private state with no other locks. Please see the implementation in transaction_participant.cpp for examples. It is worth noting that with this change, locking the Client is not needed often and never for long, and there is no need for separate mutexes for participant state and monitoring state.
-rw-r--r--src/mongo/db/catalog/index_catalog_entry_impl.cpp14
-rw-r--r--src/mongo/db/commands/find_and_modify.cpp4
-rw-r--r--src/mongo/db/commands/find_cmd.cpp5
-rw-r--r--src/mongo/db/commands/run_aggregate.cpp4
-rw-r--r--src/mongo/db/commands/txn_cmds.cpp12
-rw-r--r--src/mongo/db/commands/write_commands/write_commands.cpp2
-rw-r--r--src/mongo/db/db_raii.cpp2
-rw-r--r--src/mongo/db/kill_sessions_local.cpp114
-rw-r--r--src/mongo/db/op_observer_impl.cpp49
-rw-r--r--src/mongo/db/op_observer_impl_test.cpp130
-rw-r--r--src/mongo/db/ops/write_ops_exec.cpp33
-rw-r--r--src/mongo/db/pipeline/process_interface_shardsvr.cpp2
-rw-r--r--src/mongo/db/pipeline/process_interface_standalone.cpp4
-rw-r--r--src/mongo/db/repl/apply_ops.cpp6
-rw-r--r--src/mongo/db/repl/do_txn.cpp6
-rw-r--r--src/mongo/db/repl/do_txn_test.cpp4
-rw-r--r--src/mongo/db/repl/mock_repl_coord_server_fixture.cpp8
-rw-r--r--src/mongo/db/repl/mock_repl_coord_server_fixture.h1
-rw-r--r--src/mongo/db/repl/oplog.cpp2
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl.cpp2
-rw-r--r--src/mongo/db/repl/transaction_oplog_application.cpp8
-rw-r--r--src/mongo/db/s/session_catalog_migration_destination.cpp8
-rw-r--r--src/mongo/db/s/session_catalog_migration_destination_test.cpp52
-rw-r--r--src/mongo/db/s/txn_two_phase_commit_cmds.cpp10
-rw-r--r--src/mongo/db/service_entry_point_common.cpp19
-rw-r--r--src/mongo/db/session.h4
-rw-r--r--src/mongo/db/session_catalog.cpp20
-rw-r--r--src/mongo/db/session_catalog.h15
-rw-r--r--src/mongo/db/session_catalog_mongod.cpp28
-rw-r--r--src/mongo/db/transaction_participant.cpp1112
-rw-r--r--src/mongo/db/transaction_participant.h1314
-rw-r--r--src/mongo/db/transaction_participant_retryable_writes_test.cpp175
-rw-r--r--src/mongo/db/transaction_participant_test.cpp1446
-rw-r--r--src/mongo/dbtests/storage_timestamp_tests.cpp26
34 files changed, 2081 insertions, 2560 deletions
diff --git a/src/mongo/db/catalog/index_catalog_entry_impl.cpp b/src/mongo/db/catalog/index_catalog_entry_impl.cpp
index 422cec2a952..2a860ed856d 100644
--- a/src/mongo/db/catalog/index_catalog_entry_impl.cpp
+++ b/src/mongo/db/catalog/index_catalog_entry_impl.cpp
@@ -154,7 +154,7 @@ bool IndexCatalogEntryImpl::isReady(OperationContext* opCtx) const {
// minimumSnapshotVersion on a collection. This means we are unprotected from reading
// out-of-sync index catalog entries. To fix this, we uassert if we detect that the
// in-memory catalog is out-of-sync with the on-disk catalog.
- if (txnParticipant && txnParticipant->inMultiDocumentTransaction()) {
+ if (txnParticipant && txnParticipant.inMultiDocumentTransaction()) {
if (!_catalogIsPresent(opCtx) || _catalogIsReady(opCtx) != _isReady) {
uasserted(ErrorCodes::SnapshotUnavailable,
str::stream() << "Unable to read from a snapshot due to pending collection"
@@ -180,11 +180,11 @@ bool IndexCatalogEntryImpl::isMultikey(OperationContext* opCtx) const {
// and the read-path will query this state before determining there is no interesting multikey
// state. Note, it's always legal, though potentially wasteful, to return `true`.
auto txnParticipant = TransactionParticipant::get(opCtx);
- if (!txnParticipant || !txnParticipant->inMultiDocumentTransaction()) {
+ if (!txnParticipant || !txnParticipant.inMultiDocumentTransaction()) {
return false;
}
- for (const MultikeyPathInfo& path : txnParticipant->getUncommittedMultikeyPathInfos()) {
+ for (const MultikeyPathInfo& path : txnParticipant.getUncommittedMultikeyPathInfos()) {
if (path.nss == NamespaceString(_ns) && path.indexName == _descriptor->indexName()) {
return true;
}
@@ -197,12 +197,12 @@ MultikeyPaths IndexCatalogEntryImpl::getMultikeyPaths(OperationContext* opCtx) c
stdx::lock_guard<stdx::mutex> lk(_indexMultikeyPathsMutex);
auto txnParticipant = TransactionParticipant::get(opCtx);
- if (!txnParticipant || !txnParticipant->inMultiDocumentTransaction()) {
+ if (!txnParticipant || !txnParticipant.inMultiDocumentTransaction()) {
return _indexMultikeyPaths;
}
MultikeyPaths ret = _indexMultikeyPaths;
- for (const MultikeyPathInfo& path : txnParticipant->getUncommittedMultikeyPathInfos()) {
+ for (const MultikeyPathInfo& path : txnParticipant.getUncommittedMultikeyPathInfos()) {
if (path.nss == NamespaceString(_ns) && path.indexName == _descriptor->indexName()) {
MultikeyPathTracker::mergeMultikeyPaths(&ret, path.multikeyPaths);
}
@@ -330,8 +330,8 @@ void IndexCatalogEntryImpl::setMultikey(OperationContext* opCtx,
// Keep multikey changes in memory to correctly service later reads using this index.
auto txnParticipant = TransactionParticipant::get(opCtx);
- if (txnParticipant && txnParticipant->inMultiDocumentTransaction()) {
- txnParticipant->addUncommittedMultikeyPathInfo(
+ if (txnParticipant && txnParticipant.inMultiDocumentTransaction()) {
+ txnParticipant.addUncommittedMultikeyPathInfo(
MultikeyPathInfo{_collection->ns(), _descriptor->indexName(), std::move(paths)});
}
}
diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp
index a74bb8fa063..beaa98a29d4 100644
--- a/src/mongo/db/commands/find_and_modify.cpp
+++ b/src/mongo/db/commands/find_and_modify.cpp
@@ -308,7 +308,7 @@ public:
maybeDisableValidation.emplace(opCtx);
const auto txnParticipant = TransactionParticipant::get(opCtx);
- const auto inTransaction = txnParticipant && txnParticipant->inMultiDocumentTransaction();
+ const auto inTransaction = txnParticipant && txnParticipant.inMultiDocumentTransaction();
uassert(50781,
str::stream() << "Cannot write to system collection " << nsString.ns()
<< " within a transaction.",
@@ -324,7 +324,7 @@ public:
const auto stmtId = 0;
if (opCtx->getTxnNumber() && !inTransaction) {
const auto txnParticipant = TransactionParticipant::get(opCtx);
- if (auto entry = txnParticipant->checkStatementExecuted(stmtId)) {
+ if (auto entry = txnParticipant.checkStatementExecuted(opCtx, stmtId)) {
RetryableWritesStats::get(opCtx)->incrementRetriedCommandsCount();
RetryableWritesStats::get(opCtx)->incrementRetriedStatementsCount();
parseOplogEntryForFindAndModify(opCtx, args, *entry, &result);
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index e724e5698dc..4ea78429b54 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -255,12 +255,11 @@ public:
uassert(ErrorCodes::InvalidOptions,
"It is illegal to open a tailable cursor in a transaction",
!txnParticipant ||
- !(txnParticipant->inMultiDocumentTransaction() && qr->isTailable()));
+ !(txnParticipant.inMultiDocumentTransaction() && qr->isTailable()));
uassert(ErrorCodes::OperationNotSupportedInTransaction,
"The 'readOnce' option is not supported within a transaction.",
- !txnParticipant ||
- !txnParticipant->inActiveOrKilledMultiDocumentTransaction() ||
+ !txnParticipant || !txnParticipant.inActiveOrKilledMultiDocumentTransaction() ||
!qr->isReadOnce());
uassert(ErrorCodes::InvalidOptions,
diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp
index 3d0ba7fadda..d2bd92e3036 100644
--- a/src/mongo/db/commands/run_aggregate.cpp
+++ b/src/mongo/db/commands/run_aggregate.cpp
@@ -349,7 +349,7 @@ boost::intrusive_ptr<ExpressionContext> makeExpressionContext(
expCtx->tempDir = storageGlobalParams.dbpath + "/_tmp";
auto txnParticipant = TransactionParticipant::get(opCtx);
expCtx->inMultiDocumentTransaction =
- txnParticipant && txnParticipant->inMultiDocumentTransaction();
+ txnParticipant && txnParticipant.inMultiDocumentTransaction();
return expCtx;
}
@@ -418,7 +418,7 @@ Status runAggregate(OperationContext* opCtx,
auto txnParticipant = TransactionParticipant::get(opCtx);
// If we are in a multi-document transaction, we intercept the 'readConcern'
// assertion in order to provide a more descriptive error message and code.
- if (txnParticipant && txnParticipant->inMultiDocumentTransaction()) {
+ if (txnParticipant && txnParticipant.inMultiDocumentTransaction()) {
return {ErrorCodes::OperationNotSupportedInTransaction,
ex.toStatus("Operation not permitted in transaction").reason()};
}
diff --git a/src/mongo/db/commands/txn_cmds.cpp b/src/mongo/db/commands/txn_cmds.cpp
index 7b84b3a210b..ca3cf1edf62 100644
--- a/src/mongo/db/commands/txn_cmds.cpp
+++ b/src/mongo/db/commands/txn_cmds.cpp
@@ -93,7 +93,7 @@ public:
<< opCtx->getTxnNumber() << " on session " << opCtx->getLogicalSessionId()->toBSON();
// commitTransaction is retryable.
- if (txnParticipant->transactionIsCommitted()) {
+ if (txnParticipant.transactionIsCommitted()) {
// We set the client last op to the last optime observed by the system to ensure that
// we wait for the specified write concern on an optime greater than or equal to the
// commit oplog entry.
@@ -109,7 +109,7 @@ public:
uassert(ErrorCodes::NoSuchTransaction,
"Transaction isn't in progress",
- txnParticipant->inMultiDocumentTransaction());
+ txnParticipant.inMultiDocumentTransaction());
CurOpFailpointHelpers::waitWhileFailPointEnabled(
&hangBeforeCommitingTxn, opCtx, "hangBeforeCommitingTxn");
@@ -117,10 +117,10 @@ public:
auto optionalCommitTimestamp = cmd.getCommitTimestamp();
if (optionalCommitTimestamp) {
// commitPreparedTransaction will throw if the transaction is not prepared.
- txnParticipant->commitPreparedTransaction(opCtx, optionalCommitTimestamp.get(), {});
+ txnParticipant.commitPreparedTransaction(opCtx, optionalCommitTimestamp.get(), {});
} else {
// commitUnpreparedTransaction will throw if the transaction is prepared.
- txnParticipant->commitUnpreparedTransaction(opCtx);
+ txnParticipant.commitUnpreparedTransaction(opCtx);
}
if (MONGO_FAIL_POINT(participantReturnNetworkErrorForCommitAfterExecutingCommitLogic)) {
uasserted(ErrorCodes::HostUnreachable,
@@ -172,12 +172,12 @@ public:
uassert(ErrorCodes::NoSuchTransaction,
"Transaction isn't in progress",
- txnParticipant->inMultiDocumentTransaction());
+ txnParticipant.inMultiDocumentTransaction());
CurOpFailpointHelpers::waitWhileFailPointEnabled(
&hangBeforeAbortingTxn, opCtx, "hangBeforeAbortingTxn");
- txnParticipant->abortActiveTransaction(opCtx);
+ txnParticipant.abortActiveTransaction(opCtx);
if (MONGO_FAIL_POINT(participantReturnNetworkErrorForAbortAfterExecutingAbortLogic)) {
uasserted(ErrorCodes::HostUnreachable,
diff --git a/src/mongo/db/commands/write_commands/write_commands.cpp b/src/mongo/db/commands/write_commands/write_commands.cpp
index 5d10a84b0b3..df205c914b0 100644
--- a/src/mongo/db/commands/write_commands/write_commands.cpp
+++ b/src/mongo/db/commands/write_commands/write_commands.cpp
@@ -262,7 +262,7 @@ private:
void _transactionChecks(OperationContext* opCtx) const {
auto txnParticipant = TransactionParticipant::get(opCtx);
- if (!txnParticipant || !txnParticipant->inMultiDocumentTransaction())
+ if (!txnParticipant || !txnParticipant.inMultiDocumentTransaction())
return;
uassert(50791,
str::stream() << "Cannot write to system collection " << ns().toString()
diff --git a/src/mongo/db/db_raii.cpp b/src/mongo/db/db_raii.cpp
index 34908ecc23d..fa9bd68f338 100644
--- a/src/mongo/db/db_raii.cpp
+++ b/src/mongo/db/db_raii.cpp
@@ -334,7 +334,7 @@ LockMode getLockModeForQuery(OperationContext* opCtx, const boost::optional<Name
// Use IX locks for autocommit:false multi-statement transactions; otherwise, use IS locks.
auto txnParticipant = TransactionParticipant::get(opCtx);
- if (txnParticipant && txnParticipant->inMultiDocumentTransaction()) {
+ if (txnParticipant && txnParticipant.inMultiDocumentTransaction()) {
uassert(51071,
"Cannot query system.views within a transaction",
!nss || !nss->isSystemDotViews());
diff --git a/src/mongo/db/kill_sessions_local.cpp b/src/mongo/db/kill_sessions_local.cpp
index 97329a27666..e998ad84cd4 100644
--- a/src/mongo/db/kill_sessions_local.cpp
+++ b/src/mongo/db/kill_sessions_local.cpp
@@ -86,16 +86,16 @@ void killSessionsAction(
void killSessionsAbortUnpreparedTransactions(OperationContext* opCtx,
const SessionKiller::Matcher& matcher,
ErrorCodes::Error reason) {
- killSessionsAction(
- opCtx,
- matcher,
- [](const ObservableSession& session) {
- return !TransactionParticipant::get(session.get())->transactionIsPrepared();
- },
- [](OperationContext* opCtx, const SessionToKill& session) {
- TransactionParticipant::get(session.get())->abortArbitraryTransaction();
- },
- reason);
+ killSessionsAction(opCtx,
+ matcher,
+ [](const ObservableSession& session) {
+ return !TransactionParticipant::get(session).transactionIsPrepared();
+ },
+ [](OperationContext* opCtx, const SessionToKill& session) {
+ TransactionParticipant::get(session).abortTransactionIfNotPrepared(
+ opCtx);
+ },
+ reason);
}
SessionKiller::Result killSessionsLocal(OperationContext* opCtx,
@@ -119,31 +119,15 @@ void killAllExpiredTransactions(OperationContext* opCtx) {
[when = opCtx->getServiceContext()->getPreciseClockSource()->now()](
const ObservableSession& session) {
- return TransactionParticipant::get(session.get())->expired();
+ return TransactionParticipant::get(session).expiredAsOf(when);
},
[](OperationContext* opCtx, const SessionToKill& session) {
- auto txnParticipant = TransactionParticipant::get(session.get());
-
- LOG(0)
- << "Aborting transaction with txnNumber " << txnParticipant->getActiveTxnNumber()
+ auto txnParticipant = TransactionParticipant::get(session);
+ log()
+ << "Aborting transaction with txnNumber " << txnParticipant.getActiveTxnNumber()
<< " on session " << session.getSessionId().getId()
<< " because it has been running for longer than 'transactionLifetimeLimitSeconds'";
-
- // The try/catch block below is necessary because expiredAsOf() in the filterFn above
- // could return true for expired, but unprepared transaction, but by the time we get to
- // actually kill it, the participant could theoretically become prepared (being under
- // the SessionCatalog mutex doesn't prevent the concurrently running thread from doing
- // preparing the participant).
- //
- // Then when the execution reaches the killSessionFn, it would find the transaction is
- // prepared and not allowed to be killed, which would cause the exception below
- try {
- txnParticipant->abortArbitraryTransaction();
- } catch (const DBException& ex) {
- // TODO(schwerin): Can we catch a more specific exception?
- warning() << "May have failed to abort expired transaction on session "
- << session.getSessionId().getId() << " due to " << redact(ex.toStatus());
- }
+ txnParticipant.abortTransactionIfNotPrepared(opCtx);
},
ErrorCodes::ExceededTimeLimit);
}
@@ -155,7 +139,7 @@ void killSessionsLocalShutdownAllTransactions(OperationContext* opCtx) {
matcherAllSessions,
[](const ObservableSession&) { return true; },
[](OperationContext* opCtx, const SessionToKill& session) {
- TransactionParticipant::get(session.get())->shutdown();
+ TransactionParticipant::get(session).shutdown(opCtx);
},
ErrorCodes::InterruptedAtShutdown);
}
@@ -163,18 +147,18 @@ void killSessionsLocalShutdownAllTransactions(OperationContext* opCtx) {
void killSessionsAbortAllPreparedTransactions(OperationContext* opCtx) {
SessionKiller::Matcher matcherAllSessions(
KillAllSessionsByPatternSet{makeKillAllSessionsByPattern(opCtx)});
- killSessionsAction(
- opCtx,
- matcherAllSessions,
- [](const ObservableSession& session) {
- // Filter for sessions that have a prepared transaction.
- return TransactionParticipant::get(session.get())->transactionIsPrepared();
- },
- [](OperationContext* opCtx, const SessionToKill& session) {
- // Abort the prepared transaction and invalidate the session it is
- // associated with.
- TransactionParticipant::get(session.get())->abortPreparedTransactionForRollback();
- });
+ killSessionsAction(opCtx,
+ matcherAllSessions,
+ [](const ObservableSession& session) {
+ // Filter for sessions that have a prepared transaction.
+ return TransactionParticipant::get(session).transactionIsPrepared();
+ },
+ [](OperationContext* opCtx, const SessionToKill& session) {
+ // Abort the prepared transaction and invalidate the session it is
+ // associated with.
+ TransactionParticipant::get(session).abortPreparedTransactionForRollback(
+ opCtx);
+ });
}
void yieldLocksForPreparedTransactions(OperationContext* opCtx) {
@@ -187,26 +171,28 @@ void yieldLocksForPreparedTransactions(OperationContext* opCtx) {
// to yield their locks.
SessionKiller::Matcher matcherAllSessions(
KillAllSessionsByPatternSet{makeKillAllSessionsByPattern(newOpCtx.get())});
- killSessionsAction(
- newOpCtx.get(),
- matcherAllSessions,
- [](const ObservableSession& session) {
- return TransactionParticipant::get(session.get())->transactionIsPrepared();
- },
- [](OperationContext* killerOpCtx, const SessionToKill& session) {
- auto const txnParticipant = TransactionParticipant::get(session.get());
- // Yield locks for prepared transactions.
- // When scanning and killing operations, all prepared transactions are included in the
- // list. Even though new sessions may be created after the scan, none of them can become
- // prepared during stepdown, since the RSTL has been enqueued, preventing any new
- // writes.
- if (txnParticipant->transactionIsPrepared()) {
- LOG(3) << "Yielding locks of prepared transaction. SessionId: "
- << session.getSessionId().getId()
- << " TxnNumber: " << txnParticipant->getActiveTxnNumber();
- txnParticipant->refreshLocksForPreparedTransaction(killerOpCtx, true);
- }
- });
+ killSessionsAction(newOpCtx.get(),
+ matcherAllSessions,
+ [](const ObservableSession& session) {
+ return TransactionParticipant::get(session).transactionIsPrepared();
+ },
+ [](OperationContext* killerOpCtx, const SessionToKill& session) {
+ auto txnParticipant = TransactionParticipant::get(session);
+ // Yield locks for prepared transactions.
+ // When scanning and killing operations, all prepared transactions are
+ // included in the
+ // list. Even though new sessions may be created after the scan, none of
+ // them can become
+ // prepared during stepdown, since the RSTL has been enqueued, preventing
+ // any new
+ // writes.
+ if (txnParticipant.transactionIsPrepared()) {
+ LOG(3) << "Yielding locks of prepared transaction. SessionId: "
+ << session.getSessionId().getId()
+ << " TxnNumber: " << txnParticipant.getActiveTxnNumber();
+ txnParticipant.refreshLocksForPreparedTransaction(killerOpCtx, true);
+ }
+ });
}
} // namespace mongo
diff --git a/src/mongo/db/op_observer_impl.cpp b/src/mongo/db/op_observer_impl.cpp
index 8e932a80356..829905e322e 100644
--- a/src/mongo/db/op_observer_impl.cpp
+++ b/src/mongo/db/op_observer_impl.cpp
@@ -127,16 +127,16 @@ void onWriteOpCompleted(OperationContext* opCtx,
if (lastStmtIdWriteOpTime.isNull())
return;
- const auto txnParticipant = TransactionParticipant::get(opCtx);
+ auto txnParticipant = TransactionParticipant::get(opCtx);
if (!txnParticipant)
return;
- txnParticipant->onWriteOpCompletedOnPrimary(opCtx,
- *opCtx->getTxnNumber(),
- std::move(stmtIdsWritten),
- lastStmtIdWriteOpTime,
- lastStmtIdWriteDate,
- txnState);
+ txnParticipant.onWriteOpCompletedOnPrimary(opCtx,
+ *opCtx->getTxnNumber(),
+ std::move(stmtIdsWritten),
+ lastStmtIdWriteOpTime,
+ lastStmtIdWriteDate,
+ txnState);
}
/**
@@ -192,7 +192,7 @@ OpTimeBundle replLogUpdate(OperationContext* opCtx, const OplogUpdateEntryArgs&
if (txnParticipant) {
sessionInfo.setSessionId(*opCtx->getLogicalSessionId());
sessionInfo.setTxnNumber(*opCtx->getTxnNumber());
- oplogLink.prevOpTime = txnParticipant->getLastWriteOpTime();
+ oplogLink.prevOpTime = txnParticipant.getLastWriteOpTime();
}
OpTimeBundle opTimes;
@@ -256,7 +256,7 @@ OpTimeBundle replLogDelete(OperationContext* opCtx,
if (txnParticipant) {
sessionInfo.setSessionId(*opCtx->getLogicalSessionId());
sessionInfo.setTxnNumber(*opCtx->getTxnNumber());
- oplogLink.prevOpTime = txnParticipant->getLastWriteOpTime();
+ oplogLink.prevOpTime = txnParticipant.getLastWriteOpTime();
}
OpTimeBundle opTimes;
@@ -481,9 +481,9 @@ void OpObserverImpl::onInserts(OperationContext* opCtx,
std::vector<InsertStatement>::const_iterator first,
std::vector<InsertStatement>::const_iterator last,
bool fromMigrate) {
- const auto txnParticipant = TransactionParticipant::get(opCtx);
+ auto txnParticipant = TransactionParticipant::get(opCtx);
const bool inMultiDocumentTransaction = txnParticipant && opCtx->writesAreReplicated() &&
- txnParticipant->inMultiDocumentTransaction();
+ txnParticipant.inMultiDocumentTransaction();
Date_t lastWriteDate;
@@ -499,7 +499,7 @@ void OpObserverImpl::onInserts(OperationContext* opCtx,
}
for (auto iter = first; iter != last; iter++) {
auto operation = OplogEntry::makeInsertOperation(nss, uuid, iter->doc);
- txnParticipant->addTransactionOperation(opCtx, operation);
+ txnParticipant.addTransactionOperation(opCtx, operation);
}
} else {
lastWriteDate = getWallClockTimeForOpLog(opCtx);
@@ -564,15 +564,15 @@ void OpObserverImpl::onUpdate(OperationContext* opCtx, const OplogUpdateEntryArg
return;
}
- const auto txnParticipant = TransactionParticipant::get(opCtx);
+ auto txnParticipant = TransactionParticipant::get(opCtx);
const bool inMultiDocumentTransaction = txnParticipant && opCtx->writesAreReplicated() &&
- txnParticipant->inMultiDocumentTransaction();
+ txnParticipant.inMultiDocumentTransaction();
OpTimeBundle opTime;
if (inMultiDocumentTransaction) {
auto operation = OplogEntry::makeUpdateOperation(
args.nss, args.uuid, args.updateArgs.update, args.updateArgs.criteria);
- txnParticipant->addTransactionOperation(opCtx, operation);
+ txnParticipant.addTransactionOperation(opCtx, operation);
} else {
opTime = replLogUpdate(opCtx, args);
onWriteOpCompleted(opCtx,
@@ -625,15 +625,15 @@ void OpObserverImpl::onDelete(OperationContext* opCtx,
auto& documentKey = documentKeyDecoration(opCtx);
invariant(!documentKey.isEmpty());
- const auto txnParticipant = TransactionParticipant::get(opCtx);
+ auto txnParticipant = TransactionParticipant::get(opCtx);
const bool inMultiDocumentTransaction = txnParticipant && opCtx->writesAreReplicated() &&
- txnParticipant->inMultiDocumentTransaction();
+ txnParticipant.inMultiDocumentTransaction();
OpTimeBundle opTime;
if (inMultiDocumentTransaction) {
auto operation =
OplogEntry::makeDeleteOperation(nss, uuid, deletedDoc ? deletedDoc.get() : documentKey);
- txnParticipant->addTransactionOperation(opCtx, operation);
+ txnParticipant.addTransactionOperation(opCtx, operation);
} else {
opTime = replLogDelete(opCtx, nss, uuid, stmtId, fromMigrate, deletedDoc);
onWriteOpCompleted(opCtx,
@@ -1000,8 +1000,8 @@ OpTimeBundle logApplyOpsForTransaction(OperationContext* opCtx,
sessionInfo.setSessionId(*opCtx->getLogicalSessionId());
sessionInfo.setTxnNumber(*opCtx->getTxnNumber());
- const auto txnParticipant = TransactionParticipant::get(opCtx);
- oplogLink.prevOpTime = txnParticipant->getLastWriteOpTime();
+ auto txnParticipant = TransactionParticipant::get(opCtx);
+ oplogLink.prevOpTime = txnParticipant.getLastWriteOpTime();
// Until we support multiple oplog entries per transaction, prevOpTime should always be null.
invariant(oplogLink.prevOpTime.isNull());
@@ -1043,8 +1043,8 @@ void logCommitOrAbortForPreparedTransaction(OperationContext* opCtx,
sessionInfo.setSessionId(*opCtx->getLogicalSessionId());
sessionInfo.setTxnNumber(*opCtx->getTxnNumber());
- const auto txnParticipant = TransactionParticipant::get(opCtx);
- oplogLink.prevOpTime = txnParticipant->getLastWriteOpTime();
+ auto txnParticipant = TransactionParticipant::get(opCtx);
+ oplogLink.prevOpTime = txnParticipant.getLastWriteOpTime();
const StmtId stmtId(1);
const auto wallClockTime = getWallClockTimeForOpLog(opCtx);
@@ -1123,7 +1123,6 @@ void OpObserverImpl::onTransactionPrepare(OperationContext* opCtx,
const OplogSlot& prepareOpTime,
std::vector<repl::ReplOperation>& statements) {
invariant(opCtx->getTxnNumber());
-
invariant(!prepareOpTime.opTime.isNull());
// Don't write oplog entry on secondaries.
@@ -1159,11 +1158,11 @@ void OpObserverImpl::onTransactionAbort(OperationContext* opCtx,
return;
}
- const auto txnParticipant = TransactionParticipant::get(opCtx);
+ auto txnParticipant = TransactionParticipant::get(opCtx);
invariant(txnParticipant);
if (!abortOplogEntryOpTime) {
- invariant(!txnParticipant->transactionIsCommitted());
+ invariant(!txnParticipant.transactionIsCommitted());
return;
}
diff --git a/src/mongo/db/op_observer_impl_test.cpp b/src/mongo/db/op_observer_impl_test.cpp
index 46f0bcf1da2..48877eee1a0 100644
--- a/src/mongo/db/op_observer_impl_test.cpp
+++ b/src/mongo/db/op_observer_impl_test.cpp
@@ -438,17 +438,17 @@ public:
* statement id.
*/
void simulateSessionWrite(OperationContext* opCtx,
- TransactionParticipant* txnParticipant,
+ TransactionParticipant::Participant txnParticipant,
NamespaceString nss,
TxnNumber txnNum,
StmtId stmtId) {
- txnParticipant->beginOrContinue(txnNum, boost::none, boost::none);
+ txnParticipant.beginOrContinue(opCtx, txnNum, boost::none, boost::none);
{
AutoGetCollection autoColl(opCtx, nss, MODE_IX);
WriteUnitOfWork wuow(opCtx);
auto opTime = repl::OpTime(Timestamp(10, 1), 1); // Dummy timestamp.
- txnParticipant->onWriteOpCompletedOnPrimary(
+ txnParticipant.onWriteOpCompletedOnPrimary(
opCtx, txnNum, {stmtId}, opTime, Date_t::now(), boost::none);
wuow.commit();
}
@@ -468,14 +468,14 @@ TEST_F(OpObserverSessionCatalogRollbackTest,
auto opCtx = cc().makeOperationContext();
opCtx->setLogicalSessionId(sessionId);
MongoDOperationContextSession ocs(opCtx.get());
- const auto txnParticipant = TransactionParticipant::get(opCtx.get());
- txnParticipant->refreshFromStorageIfNeeded();
+ auto txnParticipant = TransactionParticipant::get(opCtx.get());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx.get());
// Simulate a write occurring on that session
simulateSessionWrite(opCtx.get(), txnParticipant, nss, txnNum, stmtId);
// Check that the statement executed
- ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(stmtId));
+ ASSERT(txnParticipant.checkStatementExecutedNoOplogEntryFetch(stmtId));
}
// Because there are no sessions to rollback, the OpObserver should not invalidate the in-memory
@@ -492,8 +492,8 @@ TEST_F(OpObserverSessionCatalogRollbackTest,
auto opCtx = cc().makeOperationContext();
opCtx->setLogicalSessionId(sessionId);
MongoDOperationContextSession ocs(opCtx.get());
- const auto txnParticipant = TransactionParticipant::get(opCtx.get());
- ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(stmtId));
+ auto txnParticipant = TransactionParticipant::get(opCtx.get());
+ ASSERT(txnParticipant.checkStatementExecutedNoOplogEntryFetch(stmtId));
}
}
@@ -558,8 +558,8 @@ public:
opCtx()->setTxnNumber(txnNum());
_sessionCheckout = std::make_unique<MongoDOperationContextSession>(opCtx());
- const auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), false, true);
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.beginOrContinue(opCtx(), *opCtx()->getTxnNumber(), false, true);
}
void tearDown() override {
@@ -599,12 +599,12 @@ protected:
ASSERT_EQ(txnState != boost::none,
txnRecordObj.hasField(SessionTxnRecord::kStateFieldName));
- const auto txnParticipant = TransactionParticipant::get(session());
+ auto txnParticipant = TransactionParticipant::get(opCtx());
if (!opTime.isNull()) {
ASSERT_EQ(opTime, txnRecord.getLastWriteOpTime());
- ASSERT_EQ(opTime, txnParticipant->getLastWriteOpTime());
+ ASSERT_EQ(opTime, txnParticipant.getLastWriteOpTime());
} else {
- ASSERT_EQ(txnRecord.getLastWriteOpTime(), txnParticipant->getLastWriteOpTime());
+ ASSERT_EQ(txnRecord.getLastWriteOpTime(), txnParticipant.getLastWriteOpTime());
}
}
@@ -668,7 +668,7 @@ TEST_F(OpObserverLargeTransactionTest, TransactionTooLargeWhileCommitting) {
auto uuid = CollectionUUID::gen();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
// This size is crafted such that two operations of this size are not too big to fit in a single
// oplog entry, but two operations plus oplog overhead are too big to fit in a single oplog
@@ -681,13 +681,13 @@ TEST_F(OpObserverLargeTransactionTest, TransactionTooLargeWhileCommitting) {
BSON(
"_id" << 0 << "data"
<< BSONBinData(halfTransactionData.get(), kHalfTransactionSize, BinDataGeneral)));
- txnParticipant->addTransactionOperation(opCtx(), operation);
- txnParticipant->addTransactionOperation(opCtx(), operation);
+ txnParticipant.addTransactionOperation(opCtx(), operation);
+ txnParticipant.addTransactionOperation(opCtx(), operation);
ASSERT_THROWS_CODE(opObserver().onTransactionCommit(
opCtx(),
boost::none,
boost::none,
- txnParticipant->retrieveCompletedTransactionOperations(opCtx())),
+ txnParticipant.retrieveCompletedTransactionOperations(opCtx())),
AssertionException,
ErrorCodes::TransactionTooLarge);
}
@@ -698,7 +698,7 @@ TEST_F(OpObserverTransactionTest, TransactionalPrepareTest) {
auto uuid1 = CollectionUUID::gen();
auto uuid2 = CollectionUUID::gen();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
WriteUnitOfWork wuow(opCtx());
AutoGetCollection autoColl1(opCtx(), nss1, MODE_IX);
@@ -729,12 +729,12 @@ TEST_F(OpObserverTransactionTest, TransactionalPrepareTest) {
<< "x"));
opObserver().onDelete(opCtx(), nss1, uuid1, 0, false, boost::none);
- txnParticipant->transitionToPreparedforTest();
+ txnParticipant.transitionToPreparedforTest(opCtx());
{
WriteUnitOfWork wuow(opCtx());
OplogSlot slot = repl::getNextOpTime(opCtx());
opObserver().onTransactionPrepare(
- opCtx(), slot, txnParticipant->retrieveCompletedTransactionOperations(opCtx()));
+ opCtx(), slot, txnParticipant.retrieveCompletedTransactionOperations(opCtx()));
opCtx()->recoveryUnit()->setPrepareTimestamp(slot.opTime.getTimestamp());
}
@@ -794,7 +794,7 @@ TEST_F(OpObserverTransactionTest, TransactionalPreparedCommitTest) {
<< "x");
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
std::vector<InsertStatement> insert;
insert.emplace_back(0, doc);
@@ -806,11 +806,11 @@ TEST_F(OpObserverTransactionTest, TransactionalPreparedCommitTest) {
AutoGetCollection autoColl(opCtx(), nss, MODE_IX);
opObserver().onInserts(opCtx(), nss, uuid, insert.begin(), insert.end(), false);
- txnParticipant->transitionToPreparedforTest();
+ txnParticipant.transitionToPreparedforTest(opCtx());
const auto prepareSlot = repl::getNextOpTime(opCtx());
prepareTimestamp = prepareSlot.opTime.getTimestamp();
opObserver().onTransactionPrepare(
- opCtx(), prepareSlot, txnParticipant->retrieveCompletedTransactionOperations(opCtx()));
+ opCtx(), prepareSlot, txnParticipant.retrieveCompletedTransactionOperations(opCtx()));
commitSlot = repl::getNextOpTime(opCtx());
}
@@ -819,12 +819,12 @@ TEST_F(OpObserverTransactionTest, TransactionalPreparedCommitTest) {
opCtx()->setWriteUnitOfWork(nullptr);
opCtx()->lockState()->unsetMaxLockTimeout();
- txnParticipant->transitionToCommittingWithPrepareforTest();
+ txnParticipant.transitionToCommittingWithPrepareforTest(opCtx());
opObserver().onTransactionCommit(
opCtx(),
commitSlot,
prepareTimestamp,
- txnParticipant->retrieveCompletedTransactionOperations(opCtx()));
+ txnParticipant.retrieveCompletedTransactionOperations(opCtx()));
repl::OplogInterfaceLocal oplogInterface(opCtx(), NamespaceString::kRsOplogNamespace.ns());
auto oplogIter = oplogInterface.makeIterator();
@@ -867,7 +867,7 @@ TEST_F(OpObserverTransactionTest, TransactionalPreparedAbortTest) {
<< "x");
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
std::vector<InsertStatement> insert;
insert.emplace_back(0, doc);
@@ -878,10 +878,10 @@ TEST_F(OpObserverTransactionTest, TransactionalPreparedAbortTest) {
AutoGetCollection autoColl(opCtx(), nss, MODE_IX);
opObserver().onInserts(opCtx(), nss, uuid, insert.begin(), insert.end(), false);
- txnParticipant->transitionToPreparedforTest();
+ txnParticipant.transitionToPreparedforTest(opCtx());
const auto prepareSlot = repl::getNextOpTime(opCtx());
opObserver().onTransactionPrepare(
- opCtx(), prepareSlot, txnParticipant->retrieveCompletedTransactionOperations(opCtx()));
+ opCtx(), prepareSlot, txnParticipant.retrieveCompletedTransactionOperations(opCtx()));
abortSlot = repl::getNextOpTime(opCtx());
}
@@ -889,7 +889,7 @@ TEST_F(OpObserverTransactionTest, TransactionalPreparedAbortTest) {
opCtx()->setWriteUnitOfWork(nullptr);
opCtx()->lockState()->unsetMaxLockTimeout();
opObserver().onTransactionAbort(opCtx(), abortSlot);
- txnParticipant->transitionToAbortedWithPrepareforTest();
+ txnParticipant.transitionToAbortedWithPrepareforTest(opCtx());
repl::OplogInterfaceLocal oplogInterface(opCtx(), NamespaceString::kRsOplogNamespace.ns());
auto oplogIter = oplogInterface.makeIterator();
@@ -929,7 +929,7 @@ TEST_F(OpObserverTransactionTest, TransactionalUnpreparedAbortTest) {
const NamespaceString nss("testDB", "testColl");
const auto uuid = CollectionUUID::gen();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
std::vector<InsertStatement> insert;
insert.emplace_back(0,
@@ -941,7 +941,7 @@ TEST_F(OpObserverTransactionTest, TransactionalUnpreparedAbortTest) {
AutoGetCollection autoColl(opCtx(), nss, MODE_IX);
opObserver().onInserts(opCtx(), nss, uuid, insert.begin(), insert.end(), false);
- txnParticipant->transitionToAbortedWithoutPrepareforTest();
+ txnParticipant.transitionToAbortedWithoutPrepareforTest(opCtx());
opObserver().onTransactionAbort(opCtx(), boost::none);
}
@@ -953,14 +953,14 @@ TEST_F(OpObserverTransactionTest, TransactionalUnpreparedAbortTest) {
TEST_F(OpObserverTransactionTest, PreparingEmptyTransactionLogsEmptyApplyOps) {
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
- txnParticipant->transitionToPreparedforTest();
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.transitionToPreparedforTest(opCtx());
{
WriteUnitOfWork wuow(opCtx());
OplogSlot slot = repl::getNextOpTime(opCtx());
opObserver().onTransactionPrepare(
- opCtx(), slot, txnParticipant->retrieveCompletedTransactionOperations(opCtx()));
+ opCtx(), slot, txnParticipant.retrieveCompletedTransactionOperations(opCtx()));
opCtx()->recoveryUnit()->setPrepareTimestamp(slot.opTime.getTimestamp());
}
@@ -977,8 +977,8 @@ TEST_F(OpObserverTransactionTest, PreparingEmptyTransactionLogsEmptyApplyOps) {
TEST_F(OpObserverTransactionTest, PreparingTransactionWritesToTransactionTable) {
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
- txnParticipant->transitionToPreparedforTest();
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.transitionToPreparedforTest(opCtx());
repl::OpTime prepareOpTime;
{
@@ -986,41 +986,41 @@ TEST_F(OpObserverTransactionTest, PreparingTransactionWritesToTransactionTable)
OplogSlot slot = repl::getNextOpTime(opCtx());
prepareOpTime = slot.opTime;
opObserver().onTransactionPrepare(
- opCtx(), slot, txnParticipant->retrieveCompletedTransactionOperations(opCtx()));
+ opCtx(), slot, txnParticipant.retrieveCompletedTransactionOperations(opCtx()));
opCtx()->recoveryUnit()->setPrepareTimestamp(slot.opTime.getTimestamp());
}
ASSERT_EQ(prepareOpTime.getTimestamp(), opCtx()->recoveryUnit()->getPrepareTimestamp());
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
assertTxnRecord(txnNum(), prepareOpTime, DurableTxnStateEnum::kPrepared);
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
}
TEST_F(OpObserverTransactionTest, AbortingUnpreparedTransactionDoesNotWriteToTransactionTable) {
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
opObserver().onTransactionAbort(opCtx(), boost::none);
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
// Abort the storage-transaction without calling the OpObserver.
- txnParticipant->shutdown();
+ txnParticipant.shutdown(opCtx());
assertNoTxnRecord();
}
TEST_F(OpObserverTransactionTest, AbortingPreparedTransactionWritesToTransactionTable) {
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
OplogSlot abortSlot;
{
WriteUnitOfWork wuow(opCtx());
OplogSlot slot = repl::getNextOpTime(opCtx());
opObserver().onTransactionPrepare(
- opCtx(), slot, txnParticipant->retrieveCompletedTransactionOperations(opCtx()));
+ opCtx(), slot, txnParticipant.retrieveCompletedTransactionOperations(opCtx()));
opCtx()->recoveryUnit()->setPrepareTimestamp(slot.opTime.getTimestamp());
- txnParticipant->transitionToPreparedforTest();
+ txnParticipant.transitionToPreparedforTest(opCtx());
abortSlot = repl::getNextOpTime(opCtx());
}
@@ -1028,12 +1028,12 @@ TEST_F(OpObserverTransactionTest, AbortingPreparedTransactionWritesToTransaction
opCtx()->setWriteUnitOfWork(nullptr);
opCtx()->lockState()->unsetMaxLockTimeout();
opObserver().onTransactionAbort(opCtx(), abortSlot);
- txnParticipant->transitionToAbortedWithPrepareforTest();
+ txnParticipant.transitionToAbortedWithPrepareforTest(opCtx());
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
// Abort the storage-transaction without calling the OpObserver.
- txnParticipant->shutdown();
+ txnParticipant.shutdown(opCtx());
assertTxnRecord(txnNum(), {}, DurableTxnStateEnum::kAborted);
}
@@ -1042,7 +1042,7 @@ TEST_F(OpObserverTransactionTest, CommittingUnpreparedNonEmptyTransactionWritesT
const NamespaceString nss("testDB", "testColl");
const auto uuid = CollectionUUID::gen();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
std::vector<InsertStatement> insert;
insert.emplace_back(0,
@@ -1058,7 +1058,7 @@ TEST_F(OpObserverTransactionTest, CommittingUnpreparedNonEmptyTransactionWritesT
opCtx(),
boost::none,
boost::none,
- txnParticipant->retrieveCompletedTransactionOperations(opCtx()));
+ txnParticipant.retrieveCompletedTransactionOperations(opCtx()));
opCtx()->getWriteUnitOfWork()->commit();
assertTxnRecord(txnNum(), {}, DurableTxnStateEnum::kCommitted);
@@ -1067,25 +1067,25 @@ TEST_F(OpObserverTransactionTest, CommittingUnpreparedNonEmptyTransactionWritesT
TEST_F(OpObserverTransactionTest,
CommittingUnpreparedEmptyTransactionDoesNotWriteToTransactionTable) {
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
opObserver().onTransactionCommit(
opCtx(),
boost::none,
boost::none,
- txnParticipant->retrieveCompletedTransactionOperations(opCtx()));
+ txnParticipant.retrieveCompletedTransactionOperations(opCtx()));
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
// Abort the storage-transaction without calling the OpObserver.
- txnParticipant->shutdown();
+ txnParticipant.shutdown(opCtx());
assertNoTxnRecord();
}
TEST_F(OpObserverTransactionTest, CommittingPreparedTransactionWritesToTransactionTable) {
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
repl::OpTime prepareOpTime;
{
@@ -1093,9 +1093,9 @@ TEST_F(OpObserverTransactionTest, CommittingPreparedTransactionWritesToTransacti
OplogSlot slot = repl::getNextOpTime(opCtx());
prepareOpTime = slot.opTime;
opObserver().onTransactionPrepare(
- opCtx(), slot, txnParticipant->retrieveCompletedTransactionOperations(opCtx()));
+ opCtx(), slot, txnParticipant.retrieveCompletedTransactionOperations(opCtx()));
opCtx()->recoveryUnit()->setPrepareTimestamp(slot.opTime.getTimestamp());
- txnParticipant->transitionToPreparedforTest();
+ txnParticipant.transitionToPreparedforTest(opCtx());
}
OplogSlot commitSlot = repl::getNextOpTime(opCtx());
@@ -1106,12 +1106,12 @@ TEST_F(OpObserverTransactionTest, CommittingPreparedTransactionWritesToTransacti
opCtx()->setWriteUnitOfWork(nullptr);
opCtx()->lockState()->unsetMaxLockTimeout();
- txnParticipant->transitionToCommittingWithPrepareforTest();
+ txnParticipant.transitionToCommittingWithPrepareforTest(opCtx());
opObserver().onTransactionCommit(
opCtx(),
commitSlot,
prepareOpTime.getTimestamp(),
- txnParticipant->retrieveCompletedTransactionOperations(opCtx()));
+ txnParticipant.retrieveCompletedTransactionOperations(opCtx()));
assertTxnRecord(txnNum(), commitOpTime, DurableTxnStateEnum::kCommitted);
}
@@ -1122,7 +1122,7 @@ TEST_F(OpObserverTransactionTest, TransactionalInsertTest) {
auto uuid1 = CollectionUUID::gen();
auto uuid2 = CollectionUUID::gen();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
std::vector<InsertStatement> inserts1;
inserts1.emplace_back(0,
@@ -1147,7 +1147,7 @@ TEST_F(OpObserverTransactionTest, TransactionalInsertTest) {
opCtx(),
boost::none,
boost::none,
- txnParticipant->retrieveCompletedTransactionOperations(opCtx()));
+ txnParticipant.retrieveCompletedTransactionOperations(opCtx()));
auto oplogEntryObj = getSingleOplogEntry(opCtx());
checkCommonFields(oplogEntryObj);
OplogEntry oplogEntry = assertGet(OplogEntry::parse(oplogEntryObj));
@@ -1199,7 +1199,7 @@ TEST_F(OpObserverTransactionTest, TransactionalUpdateTest) {
auto uuid1 = CollectionUUID::gen();
auto uuid2 = CollectionUUID::gen();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "update");
+ txnParticipant.unstashTransactionResources(opCtx(), "update");
CollectionUpdateArgs updateArgs1;
updateArgs1.stmtId = 0;
@@ -1228,7 +1228,7 @@ TEST_F(OpObserverTransactionTest, TransactionalUpdateTest) {
opCtx(),
boost::none,
boost::none,
- txnParticipant->retrieveCompletedTransactionOperations(opCtx()));
+ txnParticipant.retrieveCompletedTransactionOperations(opCtx()));
auto oplogEntry = getSingleOplogEntry(opCtx());
checkCommonFields(oplogEntry);
auto o = oplogEntry.getObjectField("o");
@@ -1266,7 +1266,7 @@ TEST_F(OpObserverTransactionTest, TransactionalDeleteTest) {
auto uuid2 = CollectionUUID::gen();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "delete");
+ txnParticipant.unstashTransactionResources(opCtx(), "delete");
WriteUnitOfWork wuow(opCtx());
AutoGetCollection autoColl1(opCtx(), nss1, MODE_IX);
@@ -1285,7 +1285,7 @@ TEST_F(OpObserverTransactionTest, TransactionalDeleteTest) {
opCtx(),
boost::none,
boost::none,
- txnParticipant->retrieveCompletedTransactionOperations(opCtx()));
+ txnParticipant.retrieveCompletedTransactionOperations(opCtx()));
auto oplogEntry = getSingleOplogEntry(opCtx());
checkCommonFields(oplogEntry);
auto o = oplogEntry.getObjectField("o");
diff --git a/src/mongo/db/ops/write_ops_exec.cpp b/src/mongo/db/ops/write_ops_exec.cpp
index b093faea33f..3cd83ef34e6 100644
--- a/src/mongo/db/ops/write_ops_exec.cpp
+++ b/src/mongo/db/ops/write_ops_exec.cpp
@@ -200,7 +200,7 @@ void assertCanWrite_inlock(OperationContext* opCtx, const NamespaceString& ns) {
void makeCollection(OperationContext* opCtx, const NamespaceString& ns) {
auto txnParticipant = TransactionParticipant::get(opCtx);
- auto inTransaction = txnParticipant && txnParticipant->inMultiDocumentTransaction();
+ auto inTransaction = txnParticipant && txnParticipant.inMultiDocumentTransaction();
uassert(ErrorCodes::OperationNotSupportedInTransaction,
str::stream() << "Cannot create namespace " << ns.ns()
<< " in multi-document transaction.",
@@ -238,16 +238,15 @@ bool handleError(OperationContext* opCtx,
}
auto txnParticipant = TransactionParticipant::get(opCtx);
- if (txnParticipant && txnParticipant->inActiveOrKilledMultiDocumentTransaction()) {
+ if (txnParticipant && txnParticipant.inActiveOrKilledMultiDocumentTransaction()) {
if (isTransientTransactionError(
ex.code(), false /* hasWriteConcernError */, false /* isCommitTransaction */)) {
// Tell the client to try the whole txn again, by returning ok: 0 with errorLabels.
throw;
}
-
// If we are in a transaction, we must fail the whole batch.
out->results.emplace_back(ex.toStatus());
- txnParticipant->abortActiveTransaction(opCtx);
+ txnParticipant.abortActiveTransaction(opCtx);
return false;
}
@@ -333,7 +332,7 @@ void insertDocuments(OperationContext* opCtx,
if (supportsDocLocking()) {
auto replCoord = repl::ReplicationCoordinator::get(opCtx);
auto txnParticipant = TransactionParticipant::get(opCtx);
- auto inTransaction = txnParticipant && txnParticipant->inMultiDocumentTransaction();
+ auto inTransaction = txnParticipant && txnParticipant.inMultiDocumentTransaction();
if (!inTransaction && !replCoord->isOplogDisabledFor(opCtx, collection->ns())) {
// Populate 'slots' with new optimes for each insert.
@@ -406,7 +405,7 @@ bool insertBatchAndHandleErrors(OperationContext* opCtx,
try {
acquireCollection();
auto txnParticipant = TransactionParticipant::get(opCtx);
- auto inTxn = txnParticipant && txnParticipant->inActiveOrKilledMultiDocumentTransaction();
+ auto inTxn = txnParticipant && txnParticipant.inActiveOrKilledMultiDocumentTransaction();
if (!collection->getCollection()->isCapped() && !inTxn && batch.size() > 1) {
// First try doing it all together. If all goes well, this is all we need to do.
// See Collection::_insertDocuments for why we do all capped inserts one-at-a-time.
@@ -494,7 +493,7 @@ WriteResult performInserts(OperationContext* opCtx,
// transaction.
auto txnParticipant = TransactionParticipant::get(opCtx);
invariant(!opCtx->lockState()->inAWriteUnitOfWork() ||
- (txnParticipant && txnParticipant->inActiveOrKilledMultiDocumentTransaction()));
+ (txnParticipant && txnParticipant.inActiveOrKilledMultiDocumentTransaction()));
auto& curOp = *CurOp::get(opCtx);
ON_BLOCK_EXIT([&] {
// This is the only part of finishCurOp we need to do for inserts because they reuse the
@@ -548,8 +547,8 @@ WriteResult performInserts(OperationContext* opCtx,
} else {
const auto stmtId = getStmtIdForWriteOp(opCtx, wholeOp, stmtIdIndex++);
if (opCtx->getTxnNumber()) {
- if (!txnParticipant->inMultiDocumentTransaction() &&
- txnParticipant->checkStatementExecutedNoOplogEntryFetch(stmtId)) {
+ if (!txnParticipant.inMultiDocumentTransaction() &&
+ txnParticipant.checkStatementExecutedNoOplogEntryFetch(stmtId)) {
containsRetry = true;
RetryableWritesStats::get(opCtx)->incrementRetriedStatementsCount();
out.results.emplace_back(makeWriteResultForInsertOrDeleteRetry());
@@ -698,7 +697,7 @@ static SingleWriteResult performSingleUpdateOpWithDupKeyRetry(OperationContext*
auto txnParticipant = TransactionParticipant::get(opCtx);
uassert(ErrorCodes::InvalidOptions,
"Cannot use (or request) retryable writes with multi=true",
- (txnParticipant && txnParticipant->inMultiDocumentTransaction()) ||
+ (txnParticipant && txnParticipant.inMultiDocumentTransaction()) ||
!opCtx->getTxnNumber() || !op.getMulti());
UpdateRequest request(ns);
@@ -752,7 +751,7 @@ WriteResult performUpdates(OperationContext* opCtx, const write_ops::Update& who
// transaction.
auto txnParticipant = TransactionParticipant::get(opCtx);
invariant(!opCtx->lockState()->inAWriteUnitOfWork() ||
- (txnParticipant && txnParticipant->inActiveOrKilledMultiDocumentTransaction()));
+ (txnParticipant && txnParticipant.inActiveOrKilledMultiDocumentTransaction()));
uassertStatusOK(userAllowedWriteNS(wholeOp.getNamespace()));
DisableDocumentValidationIfTrue docValidationDisabler(
@@ -769,8 +768,8 @@ WriteResult performUpdates(OperationContext* opCtx, const write_ops::Update& who
for (auto&& singleOp : wholeOp.getUpdates()) {
const auto stmtId = getStmtIdForWriteOp(opCtx, wholeOp, stmtIdIndex++);
if (opCtx->getTxnNumber()) {
- if (!txnParticipant->inMultiDocumentTransaction()) {
- if (auto entry = txnParticipant->checkStatementExecuted(stmtId)) {
+ if (!txnParticipant.inMultiDocumentTransaction()) {
+ if (auto entry = txnParticipant.checkStatementExecuted(opCtx, stmtId)) {
containsRetry = true;
RetryableWritesStats::get(opCtx)->incrementRetriedStatementsCount();
out.results.emplace_back(parseOplogEntryForUpdate(*entry));
@@ -812,7 +811,7 @@ static SingleWriteResult performSingleDeleteOp(OperationContext* opCtx,
auto txnParticipant = TransactionParticipant::get(opCtx);
uassert(ErrorCodes::InvalidOptions,
"Cannot use (or request) retryable writes with limit=0",
- (txnParticipant && txnParticipant->inMultiDocumentTransaction()) ||
+ (txnParticipant && txnParticipant.inMultiDocumentTransaction()) ||
!opCtx->getTxnNumber() || !op.getMulti());
globalOpCounters.gotDelete();
@@ -905,7 +904,7 @@ WriteResult performDeletes(OperationContext* opCtx, const write_ops::Delete& who
// transaction.
auto txnParticipant = TransactionParticipant::get(opCtx);
invariant(!opCtx->lockState()->inAWriteUnitOfWork() ||
- (txnParticipant && txnParticipant->inActiveOrKilledMultiDocumentTransaction()));
+ (txnParticipant && txnParticipant.inActiveOrKilledMultiDocumentTransaction()));
uassertStatusOK(userAllowedWriteNS(wholeOp.getNamespace()));
DisableDocumentValidationIfTrue docValidationDisabler(
@@ -922,8 +921,8 @@ WriteResult performDeletes(OperationContext* opCtx, const write_ops::Delete& who
for (auto&& singleOp : wholeOp.getDeletes()) {
const auto stmtId = getStmtIdForWriteOp(opCtx, wholeOp, stmtIdIndex++);
if (opCtx->getTxnNumber()) {
- if (!txnParticipant->inMultiDocumentTransaction() &&
- txnParticipant->checkStatementExecutedNoOplogEntryFetch(stmtId)) {
+ if (!txnParticipant.inMultiDocumentTransaction() &&
+ txnParticipant.checkStatementExecutedNoOplogEntryFetch(stmtId)) {
containsRetry = true;
RetryableWritesStats::get(opCtx)->incrementRetriedStatementsCount();
out.results.emplace_back(makeWriteResultForInsertOrDeleteRetry());
diff --git a/src/mongo/db/pipeline/process_interface_shardsvr.cpp b/src/mongo/db/pipeline/process_interface_shardsvr.cpp
index 45197fd9c4e..befc7a78231 100644
--- a/src/mongo/db/pipeline/process_interface_shardsvr.cpp
+++ b/src/mongo/db/pipeline/process_interface_shardsvr.cpp
@@ -170,7 +170,7 @@ unique_ptr<Pipeline, PipelineDeleter> MongoInterfaceShardServer::attachCursorSou
// cache, and attempt to do a network request while holding locks.
// TODO: SERVER-39162 allow $lookup in sharded transactions.
auto txnParticipant = TransactionParticipant::get(expCtx->opCtx);
- const bool inTxn = txnParticipant && txnParticipant->inMultiDocumentTransaction();
+ const bool inTxn = txnParticipant && txnParticipant.inMultiDocumentTransaction();
const bool isSharded = [&]() {
if (inTxn || !ShardingState::get(expCtx->opCtx)->enabled()) {
diff --git a/src/mongo/db/pipeline/process_interface_standalone.cpp b/src/mongo/db/pipeline/process_interface_standalone.cpp
index e394a715592..f282b1928fb 100644
--- a/src/mongo/db/pipeline/process_interface_standalone.cpp
+++ b/src/mongo/db/pipeline/process_interface_standalone.cpp
@@ -537,7 +537,7 @@ BSONObj MongoInterfaceStandalone::_reportCurrentOpForClient(
if (clientOpCtx) {
if (auto txnParticipant = TransactionParticipant::get(clientOpCtx)) {
- txnParticipant->reportUnstashedState(clientOpCtx, &builder);
+ txnParticipant.reportUnstashedState(clientOpCtx, &builder);
}
// Append lock stats before returning.
@@ -569,7 +569,7 @@ void MongoInterfaceStandalone::_reportCurrentOpsForIdleSessions(OperationContext
sessionCatalog->scanSessions(
{std::move(sessionFilter)},
[&](const ObservableSession& session) {
- auto op = TransactionParticipant::get(session.get())->reportStashedState();
+ auto op = TransactionParticipant::get(session).reportStashedState(opCtx);
if (!op.isEmpty()) {
ops->emplace_back(op);
}
diff --git a/src/mongo/db/repl/apply_ops.cpp b/src/mongo/db/repl/apply_ops.cpp
index 99c5febc9ba..df196691c2f 100644
--- a/src/mongo/db/repl/apply_ops.cpp
+++ b/src/mongo/db/repl/apply_ops.cpp
@@ -298,7 +298,7 @@ Status _applyPrepareTransaction(OperationContext* opCtx,
MongoDOperationContextSessionWithoutRefresh sessionCheckout(opCtx);
auto transaction = TransactionParticipant::get(opCtx);
- transaction->unstashTransactionResources(opCtx, "prepareTransaction");
+ transaction.unstashTransactionResources(opCtx, "prepareTransaction");
// Apply the operations via applysOps functionality.
int numApplied = 0;
@@ -315,8 +315,8 @@ Status _applyPrepareTransaction(OperationContext* opCtx,
return status;
}
invariant(!entry.getOpTime().isNull());
- transaction->prepareTransaction(opCtx, entry.getOpTime());
- transaction->stashTransactionResources(opCtx);
+ transaction.prepareTransaction(opCtx, entry.getOpTime());
+ transaction.stashTransactionResources(opCtx);
return Status::OK();
}
diff --git a/src/mongo/db/repl/do_txn.cpp b/src/mongo/db/repl/do_txn.cpp
index 6da0da73846..8cf1ea35f32 100644
--- a/src/mongo/db/repl/do_txn.cpp
+++ b/src/mongo/db/repl/do_txn.cpp
@@ -263,7 +263,7 @@ Status doTxn(OperationContext* opCtx,
BSONObjBuilder* result) {
auto txnParticipant = TransactionParticipant::get(opCtx);
uassert(ErrorCodes::InvalidOptions, "doTxn must be run within a transaction", txnParticipant);
- invariant(txnParticipant->inMultiDocumentTransaction());
+ invariant(txnParticipant.inMultiDocumentTransaction());
invariant(opCtx->getWriteUnitOfWork());
uassert(
ErrorCodes::InvalidOptions, "doTxn supports only CRUD opts.", _areOpsCrudOnly(doTxnCmd));
@@ -294,10 +294,10 @@ Status doTxn(OperationContext* opCtx,
numApplied = 0;
uassertStatusOK(_doTxn(opCtx, dbName, doTxnCmd, &intermediateResult, &numApplied));
- txnParticipant->commitUnpreparedTransaction(opCtx);
+ txnParticipant.commitUnpreparedTransaction(opCtx);
result->appendElements(intermediateResult.obj());
} catch (const DBException& ex) {
- txnParticipant->abortActiveUnpreparedOrStashPreparedTransaction(opCtx);
+ txnParticipant.abortActiveUnpreparedOrStashPreparedTransaction(opCtx);
BSONArrayBuilder ab;
++numApplied;
for (int j = 0; j < numApplied; j++)
diff --git a/src/mongo/db/repl/do_txn_test.cpp b/src/mongo/db/repl/do_txn_test.cpp
index 39356ad3f00..8bd9f3c6d39 100644
--- a/src/mongo/db/repl/do_txn_test.cpp
+++ b/src/mongo/db/repl/do_txn_test.cpp
@@ -167,8 +167,8 @@ void DoTxnTest::setUp() {
_ocs.emplace(_opCtx.get());
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), false, true);
- txnParticipant->unstashTransactionResources(opCtx(), "doTxn");
+ txnParticipant.beginOrContinue(opCtx(), *opCtx()->getTxnNumber(), false, true);
+ txnParticipant.unstashTransactionResources(opCtx(), "doTxn");
}
void DoTxnTest::tearDown() {
diff --git a/src/mongo/db/repl/mock_repl_coord_server_fixture.cpp b/src/mongo/db/repl/mock_repl_coord_server_fixture.cpp
index ef2ff337799..72311bd0ff4 100644
--- a/src/mongo/db/repl/mock_repl_coord_server_fixture.cpp
+++ b/src/mongo/db/repl/mock_repl_coord_server_fixture.cpp
@@ -92,14 +92,6 @@ void MockReplCoordServerFixture::setUp() {
stdx::make_unique<repl::DropPendingCollectionReaper>(repl::StorageInterface::get(service)));
}
-void MockReplCoordServerFixture::tearDown() {
- // ServiceContextMongoDTest::tearDown() will try to create it's own opCtx, and it's not
- // allowed to have 2 present per client, so destroy this one.
- _opCtx.reset();
-
- ServiceContextMongoDTest::tearDown();
-}
-
void MockReplCoordServerFixture::insertOplogEntry(const repl::OplogEntry& entry) {
AutoGetCollection autoColl(opCtx(), NamespaceString::kRsOplogNamespace, MODE_IX);
auto coll = autoColl.getCollection();
diff --git a/src/mongo/db/repl/mock_repl_coord_server_fixture.h b/src/mongo/db/repl/mock_repl_coord_server_fixture.h
index eb91bda6989..9bac2e16d74 100644
--- a/src/mongo/db/repl/mock_repl_coord_server_fixture.h
+++ b/src/mongo/db/repl/mock_repl_coord_server_fixture.h
@@ -48,7 +48,6 @@ class StorageInterfaceMock;
class MockReplCoordServerFixture : public ServiceContextMongoDTest {
public:
void setUp() override;
- void tearDown() override;
/**
* Helper method for inserting new entries to the oplog. This completely bypasses
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index cd8abb1f2d3..bb31fced7b3 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/oplog.cpp
@@ -633,7 +633,7 @@ std::vector<OpTime> logInsertOps(OperationContext* opCtx,
if (txnParticipant) {
sessionInfo.setSessionId(*opCtx->getLogicalSessionId());
sessionInfo.setTxnNumber(*opCtx->getTxnNumber());
- oplogLink.prevOpTime = txnParticipant->getLastWriteOpTime();
+ oplogLink.prevOpTime = txnParticipant.getLastWriteOpTime();
}
auto timestamps = stdx::make_unique<Timestamp[]>(count);
diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp
index b1fdd0ecdad..f32cc8b7142 100644
--- a/src/mongo/db/repl/replication_coordinator_impl.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl.cpp
@@ -2179,7 +2179,7 @@ Status ReplicationCoordinatorImpl::checkCanServeReadsFor_UNSAFE(OperationContext
}
auto txnParticipant = TransactionParticipant::get(opCtx);
- if (txnParticipant && txnParticipant->inMultiDocumentTransaction()) {
+ if (txnParticipant && txnParticipant.inMultiDocumentTransaction()) {
if (!_readWriteAbility->canAcceptNonLocalWrites_UNSAFE() && !getTestCommandsEnabled()) {
return Status(ErrorCodes::NotMaster,
"Multi-document transactions are only allowed on replica set primaries.");
diff --git a/src/mongo/db/repl/transaction_oplog_application.cpp b/src/mongo/db/repl/transaction_oplog_application.cpp
index 41c62b5e528..5ae5575ae5c 100644
--- a/src/mongo/db/repl/transaction_oplog_application.cpp
+++ b/src/mongo/db/repl/transaction_oplog_application.cpp
@@ -113,8 +113,8 @@ Status applyCommitTransaction(OperationContext* opCtx,
auto transaction = TransactionParticipant::get(opCtx);
invariant(transaction);
- transaction->unstashTransactionResources(opCtx, "commitTransaction");
- transaction->commitPreparedTransaction(
+ transaction.unstashTransactionResources(opCtx, "commitTransaction");
+ transaction.commitPreparedTransaction(
opCtx, commitCommand.getCommitTimestamp(), entry.getOpTime());
return Status::OK();
}
@@ -147,8 +147,8 @@ Status applyAbortTransaction(OperationContext* opCtx,
MongoDOperationContextSessionWithoutRefresh sessionCheckout(opCtx);
auto transaction = TransactionParticipant::get(opCtx);
- transaction->unstashTransactionResources(opCtx, "abortTransaction");
- transaction->abortActiveTransaction(opCtx);
+ transaction.unstashTransactionResources(opCtx, "abortTransaction");
+ transaction.abortActiveTransaction(opCtx);
return Status::OK();
}
diff --git a/src/mongo/db/s/session_catalog_migration_destination.cpp b/src/mongo/db/s/session_catalog_migration_destination.cpp
index 8f60227431c..572b10eddcf 100644
--- a/src/mongo/db/s/session_catalog_migration_destination.cpp
+++ b/src/mongo/db/s/session_catalog_migration_destination.cpp
@@ -251,10 +251,10 @@ ProcessOplogResult processSessionOplog(const BSONObj& oplogBSON,
opCtx->setTxnNumber(result.txnNum);
MongoDOperationContextSession ocs(opCtx);
auto txnParticipant = TransactionParticipant::get(opCtx);
- txnParticipant->beginOrContinue(result.txnNum, boost::none, boost::none);
+ txnParticipant.beginOrContinue(opCtx, result.txnNum, boost::none, boost::none);
try {
- if (txnParticipant->checkStatementExecuted(stmtId)) {
+ if (txnParticipant.checkStatementExecuted(opCtx, stmtId)) {
// Skip the incoming statement because it has already been logged locally
return lastResult;
}
@@ -278,7 +278,7 @@ ProcessOplogResult processSessionOplog(const BSONObj& oplogBSON,
? oplogEntry.getObject()
: BSON(SessionCatalogMigrationDestination::kSessionMigrateOplogTag << 1));
auto oplogLink = extractPrePostImageTs(lastResult, oplogEntry);
- oplogLink.prevOpTime = txnParticipant->getLastWriteOpTime();
+ oplogLink.prevOpTime = txnParticipant.getLastWriteOpTime();
writeConflictRetry(
opCtx,
@@ -319,7 +319,7 @@ ProcessOplogResult processSessionOplog(const BSONObj& oplogBSON,
// Do not call onWriteOpCompletedOnPrimary if we inserted a pre/post image, because the
// next oplog will contain the real operation
if (!result.isPrePostImage) {
- txnParticipant->onMigrateCompletedOnPrimary(
+ txnParticipant.onMigrateCompletedOnPrimary(
opCtx, result.txnNum, {stmtId}, oplogOpTime, *oplogEntry.getWallClockTime());
}
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 5211c61447c..3b3bb1e3381 100644
--- a/src/mongo/db/s/session_catalog_migration_destination_test.cpp
+++ b/src/mongo/db/s/session_catalog_migration_destination_test.cpp
@@ -170,7 +170,7 @@ public:
opCtx->setTxnNumber(txnNum);
MongoDOperationContextSession ocs(opCtx);
auto txnParticipant = TransactionParticipant::get(opCtx);
- txnParticipant->beginOrContinue(txnNum, boost::none, boost::none);
+ txnParticipant.beginOrContinue(opCtx, txnNum, boost::none, boost::none);
}
void checkOplog(const repl::OplogEntry& originalOplog, const repl::OplogEntry& oplogToCheck) {
@@ -196,7 +196,7 @@ public:
void checkStatementExecuted(OperationContext* opCtx, TxnNumber txnNumber, StmtId stmtId) {
auto txnParticipant = TransactionParticipant::get(opCtx);
- auto oplog = txnParticipant->checkStatementExecuted(stmtId);
+ auto oplog = txnParticipant.checkStatementExecuted(opCtx, stmtId);
ASSERT_TRUE(oplog);
}
@@ -205,7 +205,7 @@ public:
StmtId stmtId,
const repl::OplogEntry& expectedOplog) {
const auto txnParticipant = TransactionParticipant::get(opCtx);
- auto oplog = txnParticipant->checkStatementExecuted(stmtId);
+ auto oplog = txnParticipant.checkStatementExecuted(opCtx, stmtId);
ASSERT_TRUE(oplog);
checkOplogWithNestedOplog(expectedOplog, *oplog);
}
@@ -241,7 +241,8 @@ public:
innerOpCtx.get(), insertBuilder.obj(), true, true, true, true);
MongoDOperationContextSession sessionTxnState(innerOpCtx.get());
auto txnParticipant = TransactionParticipant::get(innerOpCtx.get());
- txnParticipant->beginOrContinue(*sessionInfo.getTxnNumber(), boost::none, boost::none);
+ txnParticipant.beginOrContinue(
+ innerOpCtx.get(), *sessionInfo.getTxnNumber(), boost::none, boost::none);
const auto reply = performInserts(innerOpCtx.get(), insertRequest);
ASSERT(reply.results.size() == 1);
@@ -355,7 +356,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, OplogEntriesWithSameTxn) {
MongoDOperationContextSession ocs(opCtx);
auto txnParticipant = TransactionParticipant::get(opCtx);
- TransactionHistoryIterator historyIter(txnParticipant->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
checkOplogWithNestedOplog(oplog3, historyIter.next(opCtx));
@@ -419,7 +420,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, ShouldOnlyStoreHistoryOfLatestTxn
MongoDOperationContextSession ocs(opCtx);
auto txnParticipant = TransactionParticipant::get(opCtx);
- TransactionHistoryIterator historyIter(txnParticipant->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
checkOplogWithNestedOplog(oplog3, historyIter.next(opCtx));
@@ -473,7 +474,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, OplogEntriesWithSameTxnInSeparate
MongoDOperationContextSession ocs(opCtx);
auto txnParticipant = TransactionParticipant::get(opCtx);
- TransactionHistoryIterator historyIter(txnParticipant->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
checkOplogWithNestedOplog(oplog3, historyIter.next(opCtx));
@@ -543,8 +544,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, OplogEntriesWithDifferentSession)
MongoDOperationContextSession ocs(opCtx);
auto txnParticipant = TransactionParticipant::get(opCtx);
-
- TransactionHistoryIterator historyIter(txnParticipant->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
checkOplogWithNestedOplog(oplog1, historyIter.next(opCtx));
ASSERT_FALSE(historyIter.hasNext());
@@ -562,7 +562,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, OplogEntriesWithDifferentSession)
auto txnParticipant = TransactionParticipant::get(opCtx2.get());
- TransactionHistoryIterator historyIter(txnParticipant->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
checkOplogWithNestedOplog(oplog3, historyIter.next(opCtx2.get()));
@@ -627,8 +627,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, ShouldNotNestAlreadyNestedOplog)
MongoDOperationContextSession ocs(opCtx);
auto txnParticipant = TransactionParticipant::get(opCtx);
-
- TransactionHistoryIterator historyIter(txnParticipant->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
checkOplog(oplog2, historyIter.next(opCtx));
@@ -681,7 +680,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, ShouldBeAbleToHandlePreImageFindA
auto txnParticipant = TransactionParticipant::get(opCtx);
- TransactionHistoryIterator historyIter(txnParticipant->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
auto nextOplog = historyIter.next(opCtx);
@@ -771,8 +770,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, ShouldBeAbleToHandlePostImageFind
MongoDOperationContextSession ocs(opCtx);
auto txnParticipant = TransactionParticipant::get(opCtx);
-
- TransactionHistoryIterator historyIter(txnParticipant->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
auto nextOplog = historyIter.next(opCtx);
@@ -866,7 +864,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, ShouldBeAbleToHandleFindAndModify
auto txnParticipant = TransactionParticipant::get(opCtx);
- TransactionHistoryIterator historyIter(txnParticipant->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
auto nextOplog = historyIter.next(opCtx);
@@ -968,7 +966,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, OlderTxnShouldBeIgnored) {
auto txnParticipant = TransactionParticipant::get(opCtx);
- TransactionHistoryIterator historyIter(txnParticipant->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
auto oplog = historyIter.next(opCtx);
ASSERT_BSONOBJ_EQ(BSON("_id"
@@ -1031,8 +1029,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, NewerTxnWriteShouldNotBeOverwritt
MongoDOperationContextSession ocs(opCtx);
auto txnParticipant = TransactionParticipant::get(opCtx);
-
- TransactionHistoryIterator historyIter(txnParticipant->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
auto oplog = historyIter.next(opCtx);
ASSERT_BSONOBJ_EQ(BSON("_id"
@@ -1214,7 +1211,7 @@ TEST_F(SessionCatalogMigrationDestinationTest,
auto txnParticipant = TransactionParticipant::get(opCtx);
- TransactionHistoryIterator historyIter(txnParticipant->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
checkOplogWithNestedOplog(oplog2, historyIter.next(opCtx));
@@ -1519,8 +1516,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, ShouldIgnoreAlreadyExecutedStatem
MongoDOperationContextSession ocs(opCtx);
auto txnParticipant = TransactionParticipant::get(opCtx);
-
- TransactionHistoryIterator historyIter(txnParticipant->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
checkOplogWithNestedOplog(oplog3, historyIter.next(opCtx));
@@ -1587,7 +1583,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, OplogEntriesWithIncompleteHistory
auto txnParticipant = TransactionParticipant::get(opCtx);
- TransactionHistoryIterator historyIter(txnParticipant->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
checkOplogWithNestedOplog(oplogEntries[2], historyIter.next(opCtx));
@@ -1601,7 +1597,7 @@ TEST_F(SessionCatalogMigrationDestinationTest, OplogEntriesWithIncompleteHistory
checkStatementExecuted(opCtx, 2, 23, oplogEntries[0]);
checkStatementExecuted(opCtx, 2, 5, oplogEntries[2]);
- ASSERT_THROWS(txnParticipant->checkStatementExecuted(38), AssertionException);
+ ASSERT_THROWS(txnParticipant.checkStatementExecuted(opCtx, 38), AssertionException);
}
TEST_F(SessionCatalogMigrationDestinationTest,
@@ -1619,8 +1615,8 @@ TEST_F(SessionCatalogMigrationDestinationTest,
MongoDOperationContextSession ocs(opCtx);
auto txnParticipant = TransactionParticipant::get(opCtx);
- txnParticipant->refreshFromStorageIfNeeded();
- txnParticipant->beginOrContinue(3, boost::none, boost::none);
+ txnParticipant.refreshFromStorageIfNeeded(opCtx);
+ txnParticipant.beginOrContinue(opCtx, 3, boost::none, boost::none);
}
OperationSessionInfo sessionInfo2;
@@ -1676,7 +1672,7 @@ TEST_F(SessionCatalogMigrationDestinationTest,
setUpSessionWithTxn(opCtx, *sessionInfo1.getSessionId(), 3);
MongoDOperationContextSession ocs(opCtx);
auto txnParticipant1 = TransactionParticipant::get(opCtx);
- ASSERT(txnParticipant1->getLastWriteOpTime().isNull());
+ ASSERT(txnParticipant1.getLastWriteOpTime().isNull());
}
// Check session 2 was correctly updated
@@ -1689,7 +1685,7 @@ TEST_F(SessionCatalogMigrationDestinationTest,
MongoDOperationContextSession ocs(opCtx);
auto txnParticipant2 = TransactionParticipant::get(opCtx);
- TransactionHistoryIterator historyIter(txnParticipant2->getLastWriteOpTime());
+ TransactionHistoryIterator historyIter(txnParticipant2.getLastWriteOpTime());
ASSERT_TRUE(historyIter.hasNext());
checkOplogWithNestedOplog(oplogEntries[2], historyIter.next(opCtx));
diff --git a/src/mongo/db/s/txn_two_phase_commit_cmds.cpp b/src/mongo/db/s/txn_two_phase_commit_cmds.cpp
index db3c1d7c7cf..276c569fd66 100644
--- a/src/mongo/db/s/txn_two_phase_commit_cmds.cpp
+++ b/src/mongo/db/s/txn_two_phase_commit_cmds.cpp
@@ -88,7 +88,7 @@ public:
"'prepareTransaction' is not supported for replica sets with arbiters",
!replCoord->setContainsArbiter());
- const auto txnParticipant = TransactionParticipant::get(opCtx);
+ auto txnParticipant = TransactionParticipant::get(opCtx);
uassert(ErrorCodes::CommandFailed,
"prepareTransaction must be run within a transaction",
txnParticipant);
@@ -105,11 +105,11 @@ public:
uassert(ErrorCodes::NoSuchTransaction,
"Transaction isn't in progress",
- txnParticipant->inMultiDocumentTransaction());
+ txnParticipant.inMultiDocumentTransaction());
- if (txnParticipant->transactionIsPrepared()) {
+ if (txnParticipant.transactionIsPrepared()) {
auto& replClient = repl::ReplClientInfo::forClient(opCtx->getClient());
- auto prepareOpTime = txnParticipant->getPrepareOpTime();
+ auto prepareOpTime = txnParticipant.getPrepareOpTime();
// Set the client optime to be prepareOpTime if it's not already later than
// prepareOpTime. This ensures that we wait for writeConcern and that prepareOpTime
@@ -133,7 +133,7 @@ public:
return PrepareTimestamp(prepareOpTime.getTimestamp());
}
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx, {});
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx, {});
if (MONGO_FAIL_POINT(
participantReturnNetworkErrorForPrepareAfterExecutingPrepareLogic)) {
uasserted(ErrorCodes::HostUnreachable,
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index a02c831bfb0..776bdbe5aef 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -372,13 +372,14 @@ void appendClusterAndOperationTime(OperationContext* opCtx,
void invokeWithSessionCheckedOut(OperationContext* opCtx,
CommandInvocation* invocation,
- TransactionParticipant* txnParticipant,
+ TransactionParticipant::Participant txnParticipant,
const OperationSessionInfoFromClient& sessionOptions,
rpc::ReplyBuilderInterface* replyBuilder) {
if (!opCtx->getClient()->isInDirectClient()) {
- txnParticipant->beginOrContinue(*sessionOptions.getTxnNumber(),
- sessionOptions.getAutocommit(),
- sessionOptions.getStartTransaction());
+ txnParticipant.beginOrContinue(opCtx,
+ *sessionOptions.getTxnNumber(),
+ sessionOptions.getAutocommit(),
+ sessionOptions.getStartTransaction());
// Create coordinator if needed. If "startTransaction" is present, it must be true.
if (sessionOptions.getStartTransaction()) {
// If this shard has been selected as the coordinator, set up the coordinator state
@@ -386,18 +387,18 @@ void invokeWithSessionCheckedOut(OperationContext* opCtx,
if (sessionOptions.getCoordinator() == boost::optional<bool>(true)) {
createTransactionCoordinator(opCtx, *sessionOptions.getTxnNumber());
}
- } else if (txnParticipant->inMultiDocumentTransaction()) {
+ } else if (txnParticipant.inMultiDocumentTransaction()) {
const auto& readConcernArgs = repl::ReadConcernArgs::get(opCtx);
uassert(ErrorCodes::InvalidOptions,
"Only the first command in a transaction may specify a readConcern",
readConcernArgs.isEmpty());
}
- txnParticipant->unstashTransactionResources(opCtx, invocation->definition()->getName());
+ txnParticipant.unstashTransactionResources(opCtx, invocation->definition()->getName());
}
auto guard = makeGuard([&txnParticipant, opCtx] {
- txnParticipant->abortActiveUnpreparedOrStashPreparedTransaction(opCtx);
+ txnParticipant.abortActiveUnpreparedOrStashPreparedTransaction(opCtx);
});
try {
@@ -420,7 +421,7 @@ void invokeWithSessionCheckedOut(OperationContext* opCtx,
// If this shard has completed an earlier statement for this transaction, it must already be
// in the transaction's participant list, so it is guaranteed to learn its outcome.
- txnParticipant->stashTransactionResources(opCtx);
+ txnParticipant.stashTransactionResources(opCtx);
guard.dismiss();
throw;
}
@@ -433,7 +434,7 @@ void invokeWithSessionCheckedOut(OperationContext* opCtx,
}
// Stash or commit the transaction when the command succeeds.
- txnParticipant->stashTransactionResources(opCtx);
+ txnParticipant.stashTransactionResources(opCtx);
guard.dismiss();
}
diff --git a/src/mongo/db/session.h b/src/mongo/db/session.h
index 53b9343ea54..bd9d4d0dcbd 100644
--- a/src/mongo/db/session.h
+++ b/src/mongo/db/session.h
@@ -68,8 +68,8 @@ private:
// A pointer back to the currently running operation on this Session, or nullptr if there
// is no operation currently running for the Session.
//
- // May be read by holders of the SessionCatalog mutex. May only be set when clear or cleared
- // when set, and the opCtx being set or cleared must have its client locked at the time.
+ // This field is only safe to read or write while holding the SessionCatalog::_mutex. In
+ // practice, it is only used inside of the SessionCatalog itself.
OperationContext* _checkoutOpCtx{nullptr};
// Counter indicating the number of times ObservableSession::kill has been called on this
diff --git a/src/mongo/db/session_catalog.cpp b/src/mongo/db/session_catalog.cpp
index 2ea94f9d5c6..cade0e53405 100644
--- a/src/mongo/db/session_catalog.cpp
+++ b/src/mongo/db/session_catalog.cpp
@@ -89,10 +89,7 @@ SessionCatalog::ScopedCheckedOutSession SessionCatalog::_checkOutSession(Operati
return !osession.currentOperation() && !osession._killed();
});
- {
- stdx::lock_guard<Client> lockClient(*opCtx->getClient());
- sri->session._checkoutOpCtx = opCtx;
- }
+ sri->session._checkoutOpCtx = opCtx;
return ScopedCheckedOutSession(
*this, std::move(sri), boost::none /* Not checked out for kill */);
@@ -114,10 +111,7 @@ SessionCatalog::SessionToKill SessionCatalog::checkOutSessionForKill(OperationCo
return !ObservableSession(ul, sri->session).currentOperation();
});
- {
- stdx::lock_guard<Client> lockClient(*opCtx->getClient());
- sri->session._checkoutOpCtx = opCtx;
- }
+ sri->session._checkoutOpCtx = opCtx;
return SessionToKill(ScopedCheckedOutSession(*this, std::move(sri), std::move(killToken)));
}
@@ -162,10 +156,7 @@ void SessionCatalog::_releaseSession(std::shared_ptr<SessionCatalog::SessionRunt
// operation context (meaning checked-out)
invariant(_sessions[sri->session.getSessionId()] == sri);
invariant(sri->session._checkoutOpCtx);
- {
- stdx::lock_guard<Client> lockClient(*sri->session._checkoutOpCtx->getClient());
- sri->session._checkoutOpCtx = nullptr;
- }
+ sri->session._checkoutOpCtx = nullptr;
sri->availableCondVar.notify_all();
if (killToken) {
@@ -185,10 +176,9 @@ SessionCatalog::KillToken ObservableSession::kill(ErrorCodes::Error reason) cons
// For currently checked-out sessions, interrupt the operation context so that the current owner
// can release the session
if (firstKiller && _session->_checkoutOpCtx) {
- stdx::lock_guard<Client> lg(*_session->_checkoutOpCtx->getClient());
-
+ invariant(_clientLock);
const auto serviceContext = _session->_checkoutOpCtx->getServiceContext();
- serviceContext->killOperation(lg, _session->_checkoutOpCtx, reason);
+ serviceContext->killOperation(_clientLock, _session->_checkoutOpCtx, reason);
}
return SessionCatalog::KillToken(getSessionId());
diff --git a/src/mongo/db/session_catalog.h b/src/mongo/db/session_catalog.h
index 7309b3e7ec3..23d656579f5 100644
--- a/src/mongo/db/session_catalog.h
+++ b/src/mongo/db/session_catalog.h
@@ -248,8 +248,8 @@ public:
/**
* Increments the number of "killers" for this session and returns a 'kill token' to to be
* passed later on to 'checkOutSessionForKill' method of the SessionCatalog in order to permit
- * the caller to execute any kill cleanup tasks. This token is later on passed to
- * '_markNotKilled' in order to decrement the number of "killers".
+ * the caller to execute any kill cleanup tasks. This token is later used to decrement the
+ * number of "killers".
*
* Marking session as killed is an internal property only that will cause any further calls to
* 'checkOutSession' to block until 'checkOutSessionForKill' is called the same number of times
@@ -277,23 +277,18 @@ private:
return {};
}
- ObservableSession(WithLock wl, Session& session) : _session(&session) {}
+ ObservableSession(WithLock wl, Session& session)
+ : _session(&session), _clientLock(_lockClientForSession(std::move(wl), _session)) {}
/**
* Returns whether 'kill' has been called on this session.
*/
bool _killed() const;
- /**
- * Used by the session catalog when checking a session back in after a call to 'kill'. See the
- * comments for 'kill for more details.
- */
- void _markNotKilled(WithLock sessionCatalogLock, SessionCatalog::KillToken killToken);
-
Session* _session;
+ stdx::unique_lock<Client> _clientLock;
};
-
/**
* Scoped object, which checks out the session specified in the passed operation context and stores
* it for later access by the command. The session is installed at construction time and is removed
diff --git a/src/mongo/db/session_catalog_mongod.cpp b/src/mongo/db/session_catalog_mongod.cpp
index ddc8c2aa6bc..7ed1d5359af 100644
--- a/src/mongo/db/session_catalog_mongod.cpp
+++ b/src/mongo/db/session_catalog_mongod.cpp
@@ -92,8 +92,7 @@ void killSessionTokensFunction(
for (auto& sessionKillToken : *sessionKillTokens) {
auto session = catalog->checkOutSessionForKill(opCtx, std::move(sessionKillToken));
- auto const txnParticipant = TransactionParticipant::get(session.get());
- txnParticipant->invalidate();
+ TransactionParticipant::get(session).invalidate(opCtx);
}
}));
}
@@ -117,12 +116,12 @@ void MongoDSessionCatalog::onStepUp(OperationContext* opCtx) {
SessionKiller::Matcher matcher(
KillAllSessionsByPatternSet{makeKillAllSessionsByPattern(opCtx)});
catalog->scanSessions(matcher, [&](const ObservableSession& session) {
- const auto txnParticipant = TransactionParticipant::get(session.get());
- if (!txnParticipant->inMultiDocumentTransaction()) {
+ const auto txnParticipant = TransactionParticipant::get(session);
+ if (!txnParticipant.inMultiDocumentTransaction()) {
sessionKillTokens->emplace_back(session.kill());
}
- if (txnParticipant->transactionIsPrepared()) {
+ if (txnParticipant.transactionIsPrepared()) {
sessionIdToReacquireLocks.emplace_back(session.getSessionId());
}
});
@@ -136,11 +135,10 @@ void MongoDSessionCatalog::onStepUp(OperationContext* opCtx) {
auto newOpCtx = cc().makeOperationContext();
newOpCtx->setLogicalSessionId(sessionId);
MongoDOperationContextSession ocs(newOpCtx.get());
- auto txnParticipant =
- TransactionParticipant::get(OperationContextSession::get(newOpCtx.get()));
+ auto txnParticipant = TransactionParticipant::get(newOpCtx.get());
LOG(3) << "Restoring locks of prepared transaction. SessionId: " << sessionId.getId()
- << " TxnNumber: " << txnParticipant->getActiveTxnNumber();
- txnParticipant->refreshLocksForPreparedTransaction(newOpCtx.get(), false);
+ << " TxnNumber: " << txnParticipant.getActiveTxnNumber();
+ txnParticipant.refreshLocksForPreparedTransaction(newOpCtx.get(), false);
}
}
@@ -220,15 +218,15 @@ MongoDOperationContextSession::MongoDOperationContextSession(OperationContext* o
: _operationContextSession(opCtx) {
invariant(!opCtx->getClient()->isInDirectClient());
- const auto txnParticipant = TransactionParticipant::get(opCtx);
- txnParticipant->refreshFromStorageIfNeeded();
+ auto txnParticipant = TransactionParticipant::get(opCtx);
+ txnParticipant.refreshFromStorageIfNeeded(opCtx);
}
MongoDOperationContextSession::~MongoDOperationContextSession() = default;
void MongoDOperationContextSession::checkIn(OperationContext* opCtx) {
if (auto txnParticipant = TransactionParticipant::get(opCtx)) {
- txnParticipant->stashTransactionResources(opCtx);
+ txnParticipant.stashTransactionResources(opCtx);
}
OperationContextSession::checkIn(opCtx);
@@ -238,7 +236,7 @@ void MongoDOperationContextSession::checkOut(OperationContext* opCtx, const std:
OperationContextSession::checkOut(opCtx);
if (auto txnParticipant = TransactionParticipant::get(opCtx)) {
- txnParticipant->unstashTransactionResources(opCtx, cmdName);
+ txnParticipant.unstashTransactionResources(opCtx, cmdName);
}
}
@@ -248,8 +246,8 @@ MongoDOperationContextSessionWithoutRefresh::MongoDOperationContextSessionWithou
invariant(!opCtx->getClient()->isInDirectClient());
const auto clientTxnNumber = *opCtx->getTxnNumber();
- const auto txnParticipant = TransactionParticipant::get(opCtx);
- txnParticipant->beginOrContinueTransactionUnconditionally(clientTxnNumber);
+ auto txnParticipant = TransactionParticipant::get(opCtx);
+ txnParticipant.beginOrContinueTransactionUnconditionally(opCtx, clientTxnNumber);
}
MongoDOperationContextSessionWithoutRefresh::~MongoDOperationContextSessionWithoutRefresh() =
diff --git a/src/mongo/db/transaction_participant.cpp b/src/mongo/db/transaction_participant.cpp
index 8443a5bbd1a..ce890615628 100644
--- a/src/mongo/db/transaction_participant.cpp
+++ b/src/mongo/db/transaction_participant.cpp
@@ -289,22 +289,20 @@ MONGO_FAIL_POINT_DEFINE(onPrimaryTransactionalWrite);
const BSONObj TransactionParticipant::kDeadEndSentinel(BSON("$incompleteOplogHistory" << 1));
-TransactionParticipant::TransactionParticipant() = default;
-TransactionParticipant::~TransactionParticipant() = default;
+TransactionParticipant::Observer::Observer(const ObservableSession& osession)
+ : Observer(&getTransactionParticipant(osession.get())) {}
-TransactionParticipant* TransactionParticipant::get(OperationContext* opCtx) {
- auto session = OperationContextSession::get(opCtx);
- if (!session) {
- return nullptr;
- }
-
- return get(session);
-}
+TransactionParticipant::Participant::Participant(OperationContext* opCtx)
+ : Observer([opCtx]() -> TransactionParticipant* {
+ if (auto session = OperationContextSession::get(opCtx)) {
+ return &getTransactionParticipant(session);
+ }
+ return nullptr;
+ }()) {}
-TransactionParticipant* TransactionParticipant::get(Session* session) {
- return &getTransactionParticipant(session);
-}
+TransactionParticipant::Participant::Participant(const SessionToKill& session)
+ : Observer(&getTransactionParticipant(session.get())) {}
void TransactionParticipant::performNoopWriteForNoSuchTransaction(OperationContext* opCtx) {
repl::ReplicationCoordinator* replCoord =
@@ -337,51 +335,49 @@ void TransactionParticipant::performNoopWriteForNoSuchTransaction(OperationConte
}
}
-const LogicalSessionId& TransactionParticipant::_sessionId() const {
- const auto* owningSession = getTransactionParticipant.owner(this);
+const LogicalSessionId& TransactionParticipant::Observer::_sessionId() const {
+ const auto* owningSession = getTransactionParticipant.owner(_tp);
return owningSession->getSessionId();
}
-OperationContext* TransactionParticipant::_opCtx() const {
- const auto* owningSession = getTransactionParticipant.owner(this);
- auto* opCtx = owningSession->currentOperation_forTest();
- invariant(opCtx);
- return opCtx;
-}
-
-void TransactionParticipant::_beginOrContinueRetryableWrite(WithLock wl, TxnNumber txnNumber) {
- if (txnNumber > _activeTxnNumber) {
+void TransactionParticipant::Participant::_beginOrContinueRetryableWrite(OperationContext* opCtx,
+ TxnNumber txnNumber) {
+ if (txnNumber > o().activeTxnNumber) {
// New retryable write.
- _setNewTxnNumber(wl, txnNumber);
- _autoCommit = boost::none;
+ _setNewTxnNumber(opCtx, txnNumber);
+ p().autoCommit = boost::none;
} else {
// Retrying a retryable write.
uassert(ErrorCodes::InvalidOptions,
"Must specify autocommit=false on all operations of a multi-statement transaction.",
- _txnState.isNone(wl));
- invariant(_autoCommit == boost::none);
+ o().txnState.isNone());
+ invariant(p().autoCommit == boost::none);
}
}
-void TransactionParticipant::_continueMultiDocumentTransaction(WithLock wl, TxnNumber txnNumber) {
+void TransactionParticipant::Participant::_continueMultiDocumentTransaction(OperationContext* opCtx,
+ TxnNumber txnNumber) {
uassert(ErrorCodes::NoSuchTransaction,
str::stream()
<< "Given transaction number "
<< txnNumber
<< " does not match any in-progress transactions. The active transaction number is "
- << _activeTxnNumber,
- txnNumber == _activeTxnNumber && !_txnState.isNone(wl));
+ << o().activeTxnNumber,
+ txnNumber == o().activeTxnNumber && !o().txnState.isNone());
- if (_txnState.isInProgress(wl) && !_txnResourceStash) {
+ if (o().txnState.isInProgress() && !o().txnResourceStash) {
// This indicates that the first command in the transaction failed but did not implicitly
// abort the transaction. It is not safe to continue the transaction, in particular because
// we have not saved the readConcern from the first statement of the transaction. Mark the
// transaction as active here, since _abortTransactionOnSession() will assume we are
// aborting an active transaction since there are no stashed resources.
- _transactionMetricsObserver.onUnstash(
- ServerTransactionsMetrics::get(getGlobalServiceContext()),
- getGlobalServiceContext()->getTickSource());
- _abortTransactionOnSession(wl);
+ {
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).transactionMetricsObserver.onUnstash(
+ ServerTransactionsMetrics::get(opCtx->getServiceContext()),
+ opCtx->getServiceContext()->getTickSource());
+ }
+ _abortTransactionOnSession(opCtx);
uasserted(ErrorCodes::NoSuchTransaction,
str::stream() << "Transaction " << txnNumber << " has been aborted.");
@@ -390,54 +386,51 @@ void TransactionParticipant::_continueMultiDocumentTransaction(WithLock wl, TxnN
return;
}
-void TransactionParticipant::_beginMultiDocumentTransaction(WithLock wl, TxnNumber txnNumber) {
+void TransactionParticipant::Participant::_beginMultiDocumentTransaction(OperationContext* opCtx,
+ TxnNumber txnNumber) {
// Aborts any in-progress txns.
- _setNewTxnNumber(wl, txnNumber);
- _autoCommit = false;
+ _setNewTxnNumber(opCtx, txnNumber);
+ p().autoCommit = false;
- _txnState.transitionTo(wl, TransactionState::kInProgress);
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).txnState.transitionTo(TransactionState::kInProgress);
// Start tracking various transactions metrics.
//
// We measure the start time in both microsecond and millisecond resolution. The TickSource
// provides microsecond resolution to record the duration of the transaction. The start "wall
// clock" time can be considered an approximation to the microsecond measurement.
- auto now = getGlobalServiceContext()->getPreciseClockSource()->now();
- auto tickSource = getGlobalServiceContext()->getTickSource();
+ auto now = opCtx->getServiceContext()->getPreciseClockSource()->now();
+ auto tickSource = opCtx->getServiceContext()->getTickSource();
- _transactionExpireDate = now + Seconds(transactionLifetimeLimitSeconds.load());
+ o(lk).transactionExpireDate = now + Seconds(transactionLifetimeLimitSeconds.load());
- {
- stdx::lock_guard<stdx::mutex> lm(_metricsMutex);
- _transactionMetricsObserver.onStart(
- ServerTransactionsMetrics::get(getGlobalServiceContext()),
- *_autoCommit,
- tickSource,
- now,
- *_transactionExpireDate);
- }
- invariant(_transactionOperations.empty());
+ o(lk).transactionMetricsObserver.onStart(
+ ServerTransactionsMetrics::get(opCtx->getServiceContext()),
+ *p().autoCommit,
+ tickSource,
+ now,
+ *o().transactionExpireDate);
+ invariant(p().transactionOperations.empty());
}
-void TransactionParticipant::beginOrContinue(TxnNumber txnNumber,
- boost::optional<bool> autocommit,
- boost::optional<bool> startTransaction) {
- stdx::lock_guard<stdx::mutex> lg(_mutex);
- _checkValid(lg);
-
+void TransactionParticipant::Participant::beginOrContinue(OperationContext* opCtx,
+ TxnNumber txnNumber,
+ boost::optional<bool> autocommit,
+ boost::optional<bool> startTransaction) {
uassert(ErrorCodes::TransactionTooOld,
str::stream() << "Cannot start transaction " << txnNumber << " on session "
<< _sessionId()
<< " because a newer transaction "
- << _activeTxnNumber
+ << o().activeTxnNumber
<< " has already started.",
- txnNumber >= _activeTxnNumber);
+ txnNumber >= o().activeTxnNumber);
// Requests without an autocommit field are interpreted as retryable writes. They cannot specify
// startTransaction, which is verified earlier when parsing the request.
if (!autocommit) {
invariant(!startTransaction);
- _beginOrContinueRetryableWrite(lg, txnNumber);
+ _beginOrContinueRetryableWrite(opCtx, txnNumber);
return;
}
@@ -447,7 +440,7 @@ void TransactionParticipant::beginOrContinue(TxnNumber txnNumber,
invariant(*autocommit == false);
if (!startTransaction) {
- _continueMultiDocumentTransaction(lg, txnNumber);
+ _continueMultiDocumentTransaction(opCtx, txnNumber);
return;
}
@@ -456,7 +449,7 @@ void TransactionParticipant::beginOrContinue(TxnNumber txnNumber,
// as true, which is verified earlier, when parsing the request.
invariant(*startTransaction);
- if (txnNumber == _activeTxnNumber) {
+ if (txnNumber == o().activeTxnNumber) {
// Servers in a sharded cluster can start a new transaction at the active transaction number
// to allow internal retries by routers on re-targeting errors, like
// StaleShard/DatabaseVersion or SnapshotTooOld.
@@ -473,29 +466,29 @@ void TransactionParticipant::beginOrContinue(TxnNumber txnNumber,
str::stream() << "Cannot start a transaction at given transaction number "
<< txnNumber
<< " a transaction with the same number is in state "
- << _txnState.toString(),
- _txnState.isInSet(lg, restartableStates));
+ << o().txnState.toString(),
+ o().txnState.isInSet(restartableStates));
}
- _beginMultiDocumentTransaction(lg, txnNumber);
+ _beginMultiDocumentTransaction(opCtx, txnNumber);
}
-void TransactionParticipant::beginOrContinueTransactionUnconditionally(TxnNumber txnNumber) {
- stdx::lock_guard<stdx::mutex> lg(_mutex);
+void TransactionParticipant::Participant::beginOrContinueTransactionUnconditionally(
+ OperationContext* opCtx, TxnNumber txnNumber) {
// We don't check or fetch any on-disk state, so treat the transaction as 'valid' for the
// purposes of this method and continue the transaction unconditionally
- _isValid = true;
+ p().isValid = true;
- if (_activeTxnNumber != txnNumber) {
- _beginMultiDocumentTransaction(lg, txnNumber);
+ if (o().activeTxnNumber != txnNumber) {
+ _beginMultiDocumentTransaction(opCtx, txnNumber);
}
}
-void TransactionParticipant::_setSpeculativeTransactionOpTime(
- WithLock, OperationContext* opCtx, SpeculativeTransactionOpTime opTimeChoice) {
+void TransactionParticipant::Participant::_setSpeculativeTransactionOpTime(
+ OperationContext* opCtx, SpeculativeTransactionOpTime opTimeChoice) {
repl::ReplicationCoordinator* replCoord =
- repl::ReplicationCoordinator::get(opCtx->getClient()->getServiceContext());
+ repl::ReplicationCoordinator::get(opCtx->getServiceContext());
opCtx->recoveryUnit()->setTimestampReadSource(
opTimeChoice == SpeculativeTransactionOpTime::kAllCommitted
? RecoveryUnit::ReadSource::kAllCommittedSnapshot
@@ -504,23 +497,22 @@ void TransactionParticipant::_setSpeculativeTransactionOpTime(
auto readTimestamp = repl::StorageInterface::get(opCtx)->getPointInTimeReadTimestamp(opCtx);
// Transactions do not survive term changes, so combining "getTerm" here with the
// recovery unit timestamp does not cause races.
- _speculativeTransactionReadOpTime = {readTimestamp, replCoord->getTerm()};
- stdx::lock_guard<stdx::mutex> lm(_metricsMutex);
- _transactionMetricsObserver.onChooseReadTimestamp(readTimestamp);
+ p().speculativeTransactionReadOpTime = {readTimestamp, replCoord->getTerm()};
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).transactionMetricsObserver.onChooseReadTimestamp(readTimestamp);
}
-void TransactionParticipant::_setSpeculativeTransactionReadTimestamp(WithLock,
- OperationContext* opCtx,
- Timestamp timestamp) {
+void TransactionParticipant::Participant::_setSpeculativeTransactionReadTimestamp(
+ OperationContext* opCtx, Timestamp timestamp) {
// Read concern code should have already set the timestamp on the recovery unit.
invariant(timestamp == opCtx->recoveryUnit()->getPointInTimeReadTimestamp());
repl::ReplicationCoordinator* replCoord =
repl::ReplicationCoordinator::get(opCtx->getClient()->getServiceContext());
opCtx->recoveryUnit()->preallocateSnapshot();
- _speculativeTransactionReadOpTime = {timestamp, replCoord->getTerm()};
- stdx::lock_guard<stdx::mutex> lm(_metricsMutex);
- _transactionMetricsObserver.onChooseReadTimestamp(timestamp);
+ p().speculativeTransactionReadOpTime = {timestamp, replCoord->getTerm()};
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).transactionMetricsObserver.onChooseReadTimestamp(timestamp);
}
TransactionParticipant::OplogSlotReserver::OplogSlotReserver(OperationContext* opCtx)
@@ -591,9 +583,11 @@ TransactionParticipant::OplogSlotReserver::~OplogSlotReserver() {
replCoord->attemptToAdvanceStableTimestamp();
}
-TransactionParticipant::TxnResources::TxnResources(OperationContext* opCtx, StashStyle stashStyle) {
- // We must lock the Client to change the Locker on the OperationContext.
- stdx::lock_guard<Client> lk(*opCtx->getClient());
+TransactionParticipant::TxnResources::TxnResources(WithLock wl,
+ OperationContext* opCtx,
+ StashStyle stashStyle) noexcept {
+ // We must hold the Client lock to change the Locker on the OperationContext. Hence the
+ // WithLock.
_ruState = opCtx->getWriteUnitOfWork()->release();
opCtx->setWriteUnitOfWork(nullptr);
@@ -686,123 +680,122 @@ void TransactionParticipant::TxnResources::release(OperationContext* opCtx) {
TransactionParticipant::SideTransactionBlock::SideTransactionBlock(OperationContext* opCtx)
: _opCtx(opCtx) {
if (_opCtx->getWriteUnitOfWork()) {
+ stdx::lock_guard<Client> lk(*_opCtx->getClient());
_txnResources = TransactionParticipant::TxnResources(
- _opCtx, TxnResources::StashStyle::kSideTransaction);
+ lk, _opCtx, TxnResources::StashStyle::kSideTransaction);
}
}
TransactionParticipant::SideTransactionBlock::~SideTransactionBlock() {
if (_txnResources) {
- // Restore the transaction state onto '_opCtx'.
_txnResources->release(_opCtx);
}
}
-void TransactionParticipant::_stashActiveTransaction(WithLock, OperationContext* opCtx) {
- if (_inShutdown) {
+void TransactionParticipant::Participant::_stashActiveTransaction(OperationContext* opCtx) {
+ if (p().inShutdown) {
return;
}
- invariant(_activeTxnNumber == opCtx->getTxnNumber());
+ invariant(o().activeTxnNumber == opCtx->getTxnNumber());
+
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
{
- stdx::lock_guard<stdx::mutex> lm(_metricsMutex);
auto tickSource = opCtx->getServiceContext()->getTickSource();
- _transactionMetricsObserver.onStash(ServerTransactionsMetrics::get(opCtx), tickSource);
- _transactionMetricsObserver.onTransactionOperation(
+ o(lk).transactionMetricsObserver.onStash(ServerTransactionsMetrics::get(opCtx), tickSource);
+ o(lk).transactionMetricsObserver.onTransactionOperation(
opCtx->getClient(),
CurOp::get(opCtx)->debug().additiveMetrics,
CurOp::get(opCtx)->debug().storageStats);
}
- invariant(!_txnResourceStash);
+ invariant(!o().txnResourceStash);
auto stashStyle = opCtx->writesAreReplicated() ? TxnResources::StashStyle::kPrimary
: TxnResources::StashStyle::kSecondary;
- _txnResourceStash = TxnResources(opCtx, stashStyle);
+ o(lk).txnResourceStash = TxnResources(lk, opCtx, stashStyle);
}
-void TransactionParticipant::stashTransactionResources(OperationContext* opCtx) {
+void TransactionParticipant::Participant::stashTransactionResources(OperationContext* opCtx) {
if (opCtx->getClient()->isInDirectClient()) {
return;
}
-
invariant(opCtx->getTxnNumber());
- stdx::unique_lock<stdx::mutex> lg(_mutex);
-
- // Always check session's txnNumber, since it can be modified by migration, which does not
- // check out the session. We intentionally do not error if the transaction is aborted, since we
- // expect this function to be called at the end of the 'abortTransaction' command.
- _checkIsActiveTransaction(lg, *opCtx->getTxnNumber(), false);
- if (!_txnState.inMultiDocumentTransaction(lg)) {
- // Not in a multi-document transaction: nothing to do.
- return;
+ if (o().txnState.inMultiDocumentTransaction()) {
+ _stashActiveTransaction(opCtx);
}
-
- _stashActiveTransaction(lg, opCtx);
}
-void TransactionParticipant::unstashTransactionResources(OperationContext* opCtx,
- const std::string& cmdName) {
+void TransactionParticipant::Participant::_releaseTransactionResourcesToOpCtx(
+ OperationContext* opCtx) {
+ // Transaction resources already exist for this transaction. Transfer them from the
+ // stash to the operation context.
+ //
+ // Because TxnResources::release must acquire the Client lock midway through, and because we
+ // must hold the Client clock to mutate txnResourceStash, we jump through some hoops here to
+ // move the TxnResources in txnResourceStash into a local variable that can be manipulated
+ // without holding the Client lock.
+ [&]() noexcept {
+ using std::swap;
+ boost::optional<TxnResources> trs;
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ swap(trs, o(lk).txnResourceStash);
+ return std::move(*trs);
+ }
+ ().release(opCtx);
+}
+
+void TransactionParticipant::Participant::unstashTransactionResources(OperationContext* opCtx,
+ const std::string& cmdName) {
invariant(!opCtx->getClient()->isInDirectClient());
invariant(opCtx->getTxnNumber());
- {
- stdx::lock_guard<stdx::mutex> lg(_mutex);
-
- _checkValid(lg);
- _checkIsActiveTransaction(lg, *opCtx->getTxnNumber(), false);
-
- // If this is not a multi-document transaction, there is nothing to unstash.
- if (_txnState.isNone(lg)) {
- invariant(!_txnResourceStash);
- return;
- }
+ // If this is not a multi-document transaction, there is nothing to unstash.
+ if (o().txnState.isNone()) {
+ invariant(!o().txnResourceStash);
+ return;
+ }
- _checkIsCommandValidWithTxnState(lg, *opCtx->getTxnNumber(), cmdName);
+ _checkIsCommandValidWithTxnState(*opCtx->getTxnNumber(), cmdName);
+ if (o().txnResourceStash) {
+ _releaseTransactionResourcesToOpCtx(opCtx);
+ stdx::lock_guard<Client> lg(*opCtx->getClient());
+ o(lg).transactionMetricsObserver.onUnstash(ServerTransactionsMetrics::get(opCtx),
+ opCtx->getServiceContext()->getTickSource());
+ return;
+ }
- if (_txnResourceStash) {
- // Transaction resources already exist for this transaction. Transfer them from the
- // stash to the operation context.
- _txnResourceStash->release(opCtx);
- _txnResourceStash = boost::none;
- stdx::lock_guard<stdx::mutex> lm(_metricsMutex);
- _transactionMetricsObserver.onUnstash(ServerTransactionsMetrics::get(opCtx),
- opCtx->getServiceContext()->getTickSource());
- return;
- }
+ // If we have no transaction resources then we cannot be prepared. If we're not in progress,
+ // we don't do anything else.
+ invariant(!o().txnState.isPrepared());
- // If we have no transaction resources then we cannot be prepared. If we're not in progress,
- // we don't do anything else.
- invariant(!_txnState.isPrepared(lg));
+ if (!o().txnState.isInProgress()) {
+ // At this point we're either committed and this is a 'commitTransaction' command, or we
+ // are in the process of committing.
+ return;
+ }
- if (!_txnState.isInProgress(lg)) {
- // At this point we're either committed and this is a 'commitTransaction' command, or we
- // are in the process of committing.
- return;
- }
+ // All locks of transactions must be acquired inside the global WUOW so that we can
+ // yield and restore all locks on state transition. Otherwise, we'd have to remember
+ // which locks are managed by WUOW.
+ invariant(!opCtx->lockState()->isLocked());
- // All locks of transactions must be acquired inside the global WUOW so that we can
- // yield and restore all locks on state transition. Otherwise, we'd have to remember
- // which locks are managed by WUOW.
- invariant(!opCtx->lockState()->isLocked());
-
- // Stashed transaction resources do not exist for this in-progress multi-document
- // transaction. Set up the transaction resources on the opCtx.
- opCtx->setWriteUnitOfWork(std::make_unique<WriteUnitOfWork>(opCtx));
-
- // If maxTransactionLockRequestTimeoutMillis is set, then we will ensure no
- // future lock request waits longer than maxTransactionLockRequestTimeoutMillis
- // to acquire a lock. This is to avoid deadlocks and minimize non-transaction
- // operation performance degradations.
- auto maxTransactionLockMillis = maxTransactionLockRequestTimeoutMillis.load();
- if (opCtx->writesAreReplicated() && maxTransactionLockMillis >= 0) {
- opCtx->lockState()->setMaxLockTimeout(Milliseconds(maxTransactionLockMillis));
- }
+ // Stashed transaction resources do not exist for this in-progress multi-document
+ // transaction. Set up the transaction resources on the opCtx.
+ opCtx->setWriteUnitOfWork(std::make_unique<WriteUnitOfWork>(opCtx));
- // On secondaries, max lock timeout must not be set.
- invariant(opCtx->writesAreReplicated() || !opCtx->lockState()->hasMaxLockTimeout());
+ // If maxTransactionLockRequestTimeoutMillis is set, then we will ensure no
+ // future lock request waits longer than maxTransactionLockRequestTimeoutMillis
+ // to acquire a lock. This is to avoid deadlocks and minimize non-transaction
+ // operation performance degradations.
+ auto maxTransactionLockMillis = maxTransactionLockRequestTimeoutMillis.load();
+ if (opCtx->writesAreReplicated() && maxTransactionLockMillis >= 0) {
+ opCtx->lockState()->setMaxLockTimeout(Milliseconds(maxTransactionLockMillis));
}
+ // On secondaries, max lock timeout must not be set.
+ invariant(opCtx->writesAreReplicated() || !opCtx->lockState()->hasMaxLockTimeout());
+
// Storage engine transactions may be started in a lazy manner. By explicitly
// starting here we ensure that a point-in-time snapshot is established during the
// first operation of a transaction.
@@ -813,33 +806,28 @@ void TransactionParticipant::unstashTransactionResources(OperationContext* opCtx
// not deadlock-safe to upgrade IS to IX.
Lock::GlobalLock(opCtx, MODE_IX);
- {
- // Set speculative execution. This must be done after the global lock is acquired, because
- // we need to check that we are primary.
- stdx::lock_guard<stdx::mutex> lg(_mutex);
- const auto& readConcernArgs = repl::ReadConcernArgs::get(opCtx);
- // TODO(SERVER-38203): We cannot wait for write concern on secondaries, so we do not set the
- // speculative optime on secondaries either. This means that reads done in transactions on
- // secondaries will not wait for the read snapshot to become majority-committed.
- repl::ReplicationCoordinator* replCoord =
- repl::ReplicationCoordinator::get(opCtx->getClient()->getServiceContext());
- if (replCoord->canAcceptWritesForDatabase(
- opCtx, NamespaceString::kSessionTransactionsTableNamespace.db())) {
- if (readConcernArgs.getArgsAtClusterTime()) {
- _setSpeculativeTransactionReadTimestamp(
- lg, opCtx, readConcernArgs.getArgsAtClusterTime()->asTimestamp());
- } else {
- _setSpeculativeTransactionOpTime(
- lg,
- opCtx,
- readConcernArgs.getOriginalLevel() ==
- repl::ReadConcernLevel::kSnapshotReadConcern
- ? SpeculativeTransactionOpTime::kAllCommitted
- : SpeculativeTransactionOpTime::kLastApplied);
- }
+ // Set speculative execution. This must be done after the global lock is acquired, because
+ // we need to check that we are primary.
+ const auto& readConcernArgs = repl::ReadConcernArgs::get(opCtx);
+ // TODO(SERVER-38203): We cannot wait for write concern on secondaries, so we do not set the
+ // speculative optime on secondaries either. This means that reads done in transactions on
+ // secondaries will not wait for the read snapshot to become majority-committed.
+ repl::ReplicationCoordinator* replCoord =
+ repl::ReplicationCoordinator::get(opCtx->getServiceContext());
+ if (replCoord->canAcceptWritesForDatabase(
+ opCtx, NamespaceString::kSessionTransactionsTableNamespace.db())) {
+ if (readConcernArgs.getArgsAtClusterTime()) {
+ _setSpeculativeTransactionReadTimestamp(
+ opCtx, readConcernArgs.getArgsAtClusterTime()->asTimestamp());
} else {
- opCtx->recoveryUnit()->preallocateSnapshot();
+ _setSpeculativeTransactionOpTime(opCtx,
+ readConcernArgs.getOriginalLevel() ==
+ repl::ReadConcernLevel::kSnapshotReadConcern
+ ? SpeculativeTransactionOpTime::kAllCommitted
+ : SpeculativeTransactionOpTime::kLastApplied);
}
+ } else {
+ opCtx->recoveryUnit()->preallocateSnapshot();
}
// The Client lock must not be held when executing this failpoint as it will block currentOp
@@ -850,28 +838,24 @@ void TransactionParticipant::unstashTransactionResources(OperationContext* opCtx
}
{
- stdx::lock_guard<stdx::mutex> lg(_mutex);
- stdx::lock_guard<stdx::mutex> lm(_metricsMutex);
- _transactionMetricsObserver.onUnstash(ServerTransactionsMetrics::get(opCtx),
- opCtx->getServiceContext()->getTickSource());
+ stdx::lock_guard<Client> lg(*opCtx->getClient());
+ o(lg).transactionMetricsObserver.onUnstash(ServerTransactionsMetrics::get(opCtx),
+ opCtx->getServiceContext()->getTickSource());
}
}
-void TransactionParticipant::refreshLocksForPreparedTransaction(OperationContext* opCtx,
- bool yieldLocks) {
+void TransactionParticipant::Participant::refreshLocksForPreparedTransaction(
+ OperationContext* opCtx, bool yieldLocks) {
// The opCtx will be used to swap locks, so it cannot hold any lock.
invariant(!opCtx->lockState()->isRSTLLocked());
invariant(!opCtx->lockState()->isLocked());
- stdx::unique_lock<stdx::mutex> lk(_mutex);
// The node must have txn resource.
- invariant(_txnResourceStash);
- invariant(_txnState.isPrepared(lk));
+ invariant(o().txnResourceStash);
+ invariant(o().txnState.isPrepared());
- // Transfer the txn resource from the stash to the operation context.
- _txnResourceStash->release(opCtx);
- _txnResourceStash = boost::none;
+ _releaseTransactionResourcesToOpCtx(opCtx);
// Snapshot transactions don't conflict with PBWM lock on both primary and secondary.
invariant(!opCtx->lockState()->shouldConflictWithSecondaryBatchApplication());
@@ -879,25 +863,17 @@ void TransactionParticipant::refreshLocksForPreparedTransaction(OperationContext
// Transfer the txn resource back from the operation context to the stash.
auto stashStyle =
yieldLocks ? TxnResources::StashStyle::kSecondary : TxnResources::StashStyle::kPrimary;
- _txnResourceStash = TxnResources(opCtx, stashStyle);
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).txnResourceStash = TxnResources(lk, opCtx, stashStyle);
}
-Timestamp TransactionParticipant::prepareTransaction(OperationContext* opCtx,
- boost::optional<repl::OpTime> prepareOptime) {
- stdx::unique_lock<stdx::mutex> lk(_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.
- _checkIsActiveTransaction(lk, *opCtx->getTxnNumber(), true);
+Timestamp TransactionParticipant::Participant::prepareTransaction(
+ OperationContext* opCtx, boost::optional<repl::OpTime> prepareOptime) {
auto abortGuard = makeGuard([&] {
// Prepare transaction on secondaries should always succeed.
invariant(!prepareOptime);
- if (lk.owns_lock()) {
- lk.unlock();
- }
-
try {
// This shouldn't cause deadlocks with other prepared txns, because the acquisition
// of RSTL lock inside abortActiveTransaction will be no-op since we already have it.
@@ -915,14 +891,18 @@ Timestamp TransactionParticipant::prepareTransaction(OperationContext* opCtx,
}
});
- _txnState.transitionTo(lk, TransactionState::kPrepared);
-
boost::optional<OplogSlotReserver> oplogSlotReserver;
OplogSlot prepareOplogSlot;
+ {
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).txnState.transitionTo(TransactionState::kPrepared);
+ }
+
if (prepareOptime) {
// On secondary, we just prepare the transaction and discard the buffered ops.
prepareOplogSlot = OplogSlot(*prepareOptime, 0);
- _prepareOpTime = *prepareOptime;
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).prepareOpTime = *prepareOptime;
} else {
// On primary, we reserve an optime, prepare the transaction and write the oplog entry.
//
@@ -933,10 +913,14 @@ Timestamp TransactionParticipant::prepareTransaction(OperationContext* opCtx,
// corresponding oplog hole) will vanish.
oplogSlotReserver.emplace(opCtx);
prepareOplogSlot = oplogSlotReserver->getReservedOplogSlot();
- invariant(_prepareOpTime.isNull(),
+ invariant(o().prepareOpTime.isNull(),
str::stream() << "This transaction has already reserved a prepareOpTime at: "
- << _prepareOpTime.toString());
- _prepareOpTime = prepareOplogSlot.opTime;
+ << o().prepareOpTime.toString());
+
+ {
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).prepareOpTime = prepareOplogSlot.opTime;
+ }
if (MONGO_FAIL_POINT(hangAfterReservingPrepareTimestamp)) {
// This log output is used in js tests so please leave it.
@@ -949,30 +933,29 @@ Timestamp TransactionParticipant::prepareTransaction(OperationContext* opCtx,
opCtx->recoveryUnit()->setPrepareTimestamp(prepareOplogSlot.opTime.getTimestamp());
opCtx->getWriteUnitOfWork()->prepare();
- // We need to unlock the session to run the opObserver onTransactionPrepare, which calls back
- // into the session.
- lk.unlock();
opCtx->getServiceContext()->getOpObserver()->onTransactionPrepare(
opCtx, prepareOplogSlot, retrieveCompletedTransactionOperations(opCtx));
abortGuard.dismiss();
- invariant(!_oldestOplogEntryOpTime,
+ // For prepared transactions, we must update ServerTransactionMetrics with the prepare optime
+ // before the prepare oplog entry is written so that we don't incorrectly advance the stable
+ // timestamp.
+ invariant(!p().oldestOplogEntryOpTime,
str::stream() << "This transaction's oldest oplog entry Timestamp has already "
<< "been set to: "
- << _oldestOplogEntryOpTime->toString());
+ << p().oldestOplogEntryOpTime->toString());
// Keep track of the Timestamp from the first oplog entry written by this transaction.
- _oldestOplogEntryOpTime = prepareOplogSlot.opTime;
+ p().oldestOplogEntryOpTime = prepareOplogSlot.opTime;
// Maintain the OpTime of the oldest active oplog entry for this transaction. We currently
// only write an oplog entry for an in progress transaction when it is in the prepare state
// but this will change when we allow multiple oplog entries per transaction.
{
- stdx::lock_guard<stdx::mutex> lm(_metricsMutex);
- const auto tickSource = getGlobalServiceContext()->getTickSource();
- _transactionMetricsObserver.onPrepare(ServerTransactionsMetrics::get(opCtx),
- *_oldestOplogEntryOpTime,
- tickSource->getTicks());
+ const auto ticks = opCtx->getServiceContext()->getTickSource()->getTicks();
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).transactionMetricsObserver.onPrepare(
+ ServerTransactionsMetrics::get(opCtx), *p().oldestOplogEntryOpTime, ticks);
}
if (MONGO_FAIL_POINT(hangAfterSettingPrepareStartTime)) {
@@ -990,21 +973,16 @@ Timestamp TransactionParticipant::prepareTransaction(OperationContext* opCtx,
return prepareOplogSlot.opTime.getTimestamp();
}
-void TransactionParticipant::addTransactionOperation(OperationContext* opCtx,
- const repl::ReplOperation& operation) {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
-
- // Always check _getSession()'s txnNumber and '_txnState', since they can be modified by session
- // kill and migration, which do not check out the session.
- _checkIsActiveTransaction(lk, *opCtx->getTxnNumber(), true);
+void TransactionParticipant::Participant::addTransactionOperation(
+ OperationContext* opCtx, const repl::ReplOperation& operation) {
// Ensure that we only ever add operations to an in progress transaction.
- invariant(_txnState.isInProgress(lk), str::stream() << "Current state: " << _txnState);
+ invariant(o().txnState.isInProgress(), str::stream() << "Current state: " << o().txnState);
- invariant(_autoCommit && !*_autoCommit && _activeTxnNumber != kUninitializedTxnNumber);
+ invariant(p().autoCommit && !*p().autoCommit && o().activeTxnNumber != kUninitializedTxnNumber);
invariant(opCtx->lockState()->inAWriteUnitOfWork());
- _transactionOperations.push_back(operation);
- _transactionOperationBytes += repl::OplogEntry::getReplOperationSize(operation);
+ p().transactionOperations.push_back(operation);
+ p().transactionOperationBytes += repl::OplogEntry::getReplOperationSize(operation);
// _transactionOperationBytes is based on the in-memory size of the operation. With overhead,
// we expect the BSON size of the operation to be larger, so it's possible to make a transaction
// just a bit too large and have it fail only in the commit. It's still useful to fail early
@@ -1013,91 +991,67 @@ void TransactionParticipant::addTransactionOperation(OperationContext* opCtx,
str::stream() << "Total size of all transaction operations must be less than "
<< BSONObjMaxInternalSize
<< ". Actual size is "
- << _transactionOperationBytes,
+ << p().transactionOperationBytes,
useMultipleOplogEntryFormatForTransactions ||
- _transactionOperationBytes <= BSONObjMaxInternalSize);
+ p().transactionOperationBytes <= BSONObjMaxInternalSize);
}
-std::vector<repl::ReplOperation>& TransactionParticipant::retrieveCompletedTransactionOperations(
+std::vector<repl::ReplOperation>&
+TransactionParticipant::Participant::retrieveCompletedTransactionOperations(
OperationContext* opCtx) {
- stdx::lock_guard<stdx::mutex> lk(_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.
- _checkIsActiveTransaction(lk, *opCtx->getTxnNumber(), true);
// Ensure that we only ever retrieve a transaction's completed operations when in progress,
// committing with prepare, or prepared.
- invariant(_txnState.isInSet(lk,
- TransactionState::kInProgress |
- TransactionState::kCommittingWithPrepare |
- TransactionState::kPrepared),
- str::stream() << "Current state: " << _txnState);
+ invariant(o().txnState.isInSet(TransactionState::kInProgress |
+ TransactionState::kCommittingWithPrepare |
+ TransactionState::kPrepared),
+ str::stream() << "Current state: " << o().txnState);
- return _transactionOperations;
+ return p().transactionOperations;
}
-void TransactionParticipant::clearOperationsInMemory(OperationContext* opCtx) {
- stdx::lock_guard<stdx::mutex> lk(_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.
- _checkIsActiveTransaction(lk, *opCtx->getTxnNumber(), true);
-
+void TransactionParticipant::Participant::clearOperationsInMemory(OperationContext* opCtx) {
// Ensure that we only ever end a transaction when committing with prepare or in progress.
- invariant(_txnState.isInSet(
- lk, TransactionState::kCommittingWithPrepare | TransactionState::kInProgress),
- str::stream() << "Current state: " << _txnState);
-
- invariant(_autoCommit);
- _transactionOperationBytes = 0;
- _transactionOperations.clear();
+ invariant(o().txnState.isInSet(TransactionState::kCommittingWithPrepare |
+ TransactionState::kInProgress),
+ str::stream() << "Current state: " << o().txnState);
+ invariant(p().autoCommit);
+ p().transactionOperationBytes = 0;
+ p().transactionOperations.clear();
}
-void TransactionParticipant::commitUnpreparedTransaction(OperationContext* opCtx) {
- stdx::unique_lock<stdx::mutex> lk(_mutex);
- _checkIsActiveTransaction(lk, *opCtx->getTxnNumber(), true);
-
+void TransactionParticipant::Participant::commitUnpreparedTransaction(OperationContext* opCtx) {
uassert(ErrorCodes::InvalidOptions,
"commitTransaction must provide commitTimestamp to prepared transaction.",
- !_txnState.isPrepared(lk));
+ !o().txnState.isPrepared());
// TODO SERVER-37129: Remove this invariant once we allow transactions larger than 16MB.
- invariant(!_oldestOplogEntryOpTime,
+ invariant(!p().oldestOplogEntryOpTime,
str::stream() << "The oldest oplog entry Timestamp should not have been set because "
<< "this transaction is not prepared. But, it is currently "
- << _oldestOplogEntryOpTime->toString());
+ << p().oldestOplogEntryOpTime->toString());
- // We need to unlock the session to run the opObserver onTransactionCommit, which calls back
- // into the session.
- lk.unlock();
auto opObserver = opCtx->getServiceContext()->getOpObserver();
invariant(opObserver);
opObserver->onTransactionCommit(
opCtx, boost::none, boost::none, retrieveCompletedTransactionOperations(opCtx));
-
clearOperationsInMemory(opCtx);
+ {
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ // The oplog entry is written in the same WUOW with the data change for unprepared
+ // transactions. We can still consider the state is InProgress until now, since no
+ // externally visible changes have been made yet by the commit operation. If anything throws
+ // before this point in the function, entry point will abort the transaction.
+ o(lk).txnState.transitionTo(TransactionState::kCommittingWithoutPrepare);
+ }
- lk.lock();
- _checkIsActiveTransaction(lk, *opCtx->getTxnNumber(), true);
-
- // The oplog entry is written in the same WUOW with the data change for unprepared transactions.
- // We can still consider the state is InProgress until now, since no externally visible changes
- // have been made yet by the commit operation. If anything throws before this point in the
- // function, entry point will abort the transaction.
- _txnState.transitionTo(lk, TransactionState::kCommittingWithoutPrepare);
-
- lk.unlock();
_commitStorageTransaction(opCtx);
- lk.lock();
- _checkIsActiveTransaction(lk, *opCtx->getTxnNumber(), false);
- invariant(_txnState.isCommittingWithoutPrepare(lk),
- str::stream() << "Current State: " << _txnState);
-
- _finishCommitTransaction(lk, opCtx);
+ invariant(o().txnState.isCommittingWithoutPrepare(),
+ str::stream() << "Current State: " << o().txnState);
+ _finishCommitTransaction(opCtx);
}
-void TransactionParticipant::commitPreparedTransaction(
+void TransactionParticipant::Participant::commitPreparedTransaction(
OperationContext* opCtx,
Timestamp commitTimestamp,
boost::optional<repl::OpTime> commitOplogEntryOpTime) {
@@ -1112,22 +1066,22 @@ void TransactionParticipant::commitPreparedTransaction(
replCoord->canAcceptWritesForDatabase(opCtx, "admin"));
}
- stdx::unique_lock<stdx::mutex> lk(_mutex);
- _checkIsActiveTransaction(lk, *opCtx->getTxnNumber(), true);
-
uassert(ErrorCodes::InvalidOptions,
"commitTransaction cannot provide commitTimestamp to unprepared transaction.",
- _txnState.isPrepared(lk));
+ o().txnState.isPrepared());
uassert(
ErrorCodes::InvalidOptions, "'commitTimestamp' cannot be null", !commitTimestamp.isNull());
uassert(ErrorCodes::InvalidOptions,
"'commitTimestamp' must be greater than the 'prepareTimestamp'",
- commitTimestamp > _prepareOpTime.getTimestamp());
+ commitTimestamp > o().prepareOpTime.getTimestamp());
- _txnState.transitionTo(lk, TransactionState::kCommittingWithPrepare);
- opCtx->recoveryUnit()->setCommitTimestamp(commitTimestamp);
+ {
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).txnState.transitionTo(TransactionState::kCommittingWithPrepare);
+ }
try {
+ opCtx->recoveryUnit()->setCommitTimestamp(commitTimestamp);
UninterruptibleLockGuard noInterrupt(opCtx->lockState());
// On secondary, we generate a fake empty oplog slot, since it's not used by opObserver.
@@ -1153,37 +1107,26 @@ void TransactionParticipant::commitPreparedTransaction(
invariant(commitOplogEntryOpTime);
}
- // We need to unlock the session to run the opObserver onTransactionCommit, which calls back
- // into the session. We also do not want to write to storage with the mutex locked.
- lk.unlock();
_commitStorageTransaction(opCtx);
auto opObserver = opCtx->getServiceContext()->getOpObserver();
invariant(opObserver);
- {
- // Once the transaction is committed, the oplog entry must be written.
- UninterruptibleLockGuard lockGuard(opCtx->lockState());
- opObserver->onTransactionCommit(opCtx,
- commitOplogSlot,
- commitTimestamp,
- retrieveCompletedTransactionOperations(opCtx));
- }
+ // Once the transaction is committed, the oplog entry must be written.
+ opObserver->onTransactionCommit(
+ opCtx, commitOplogSlot, commitTimestamp, retrieveCompletedTransactionOperations(opCtx));
clearOperationsInMemory(opCtx);
- lk.lock();
- _checkIsActiveTransaction(lk, *opCtx->getTxnNumber(), true);
-
// If we are committing a prepared transaction, then we must have already recorded this
// transaction's oldest oplog entry optime.
- invariant(_oldestOplogEntryOpTime);
+ invariant(p().oldestOplogEntryOpTime);
// If commitOplogEntryOpTime is a nullopt, then we grab the OpTime from the commitOplogSlot
// which will only be set if we are primary. Otherwise, the commitOplogEntryOpTime must have
// been passed in during secondary oplog application.
- _finishOpTime = commitOplogEntryOpTime.value_or(commitOplogSlot.opTime);
+ p().finishOpTime = commitOplogEntryOpTime.value_or(commitOplogSlot.opTime);
- _finishCommitTransaction(lk, opCtx);
+ _finishCommitTransaction(opCtx);
} catch (...) {
// It is illegal for committing a prepared transaction to fail for any reason, other than an
// invalid command, so we crash instead.
@@ -1194,7 +1137,7 @@ void TransactionParticipant::commitPreparedTransaction(
}
}
-void TransactionParticipant::_commitStorageTransaction(OperationContext* opCtx) try {
+void TransactionParticipant::Participant::_commitStorageTransaction(OperationContext* opCtx) try {
invariant(opCtx->getWriteUnitOfWork());
invariant(opCtx->lockState()->isRSTLLocked());
opCtx->getWriteUnitOfWork()->commit();
@@ -1214,127 +1157,112 @@ void TransactionParticipant::_commitStorageTransaction(OperationContext* opCtx)
std::terminate();
}
-void TransactionParticipant::_finishCommitTransaction(WithLock lk, OperationContext* opCtx) {
+void TransactionParticipant::Participant::_finishCommitTransaction(OperationContext* opCtx) {
// If no writes have been done, set the client optime forward to the read timestamp so waiting
// for write concern will ensure all read data was committed.
//
// TODO(SERVER-34881): Once the default read concern is speculative majority, only set the
// client optime forward if the original read concern level is "majority" or "snapshot".
auto& clientInfo = repl::ReplClientInfo::forClient(opCtx->getClient());
- if (_speculativeTransactionReadOpTime > clientInfo.getLastOp()) {
- clientInfo.setLastOp(_speculativeTransactionReadOpTime);
+ if (p().speculativeTransactionReadOpTime > clientInfo.getLastOp()) {
+ clientInfo.setLastOp(p().speculativeTransactionReadOpTime);
}
- const bool isCommittingWithPrepare = _txnState.isCommittingWithPrepare(lk);
- _txnState.transitionTo(lk, TransactionState::kCommitted);
{
- stdx::lock_guard<stdx::mutex> lm(_metricsMutex);
auto tickSource = opCtx->getServiceContext()->getTickSource();
- _transactionMetricsObserver.onCommit(ServerTransactionsMetrics::get(opCtx),
- tickSource,
- _oldestOplogEntryOpTime,
- _finishOpTime,
- &Top::get(getGlobalServiceContext()),
- isCommittingWithPrepare);
- _transactionMetricsObserver.onTransactionOperation(
+ const bool isCommittingWithPrepare = o().txnState.isCommittingWithPrepare();
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).txnState.transitionTo(TransactionState::kCommitted);
+
+ o(lk).transactionMetricsObserver.onCommit(ServerTransactionsMetrics::get(opCtx),
+ tickSource,
+ p().oldestOplogEntryOpTime,
+ p().finishOpTime,
+ &Top::get(getGlobalServiceContext()),
+ isCommittingWithPrepare);
+ o(lk).transactionMetricsObserver.onTransactionOperation(
opCtx->getClient(),
CurOp::get(opCtx)->debug().additiveMetrics,
CurOp::get(opCtx)->debug().storageStats);
}
-
// We must clear the recovery unit and locker so any post-transaction writes can run without
// transactional settings such as a read timestamp.
- _cleanUpTxnResourceOnOpCtx(lk, opCtx, TerminationCause::kCommitted);
+ _cleanUpTxnResourceOnOpCtx(opCtx, TerminationCause::kCommitted);
}
-void TransactionParticipant::shutdown() {
- stdx::lock_guard<stdx::mutex> lock(_mutex);
+void TransactionParticipant::Participant::shutdown(OperationContext* opCtx) {
+ stdx::lock_guard<Client> lock(*opCtx->getClient());
- _inShutdown = true;
- _txnResourceStash = boost::none;
+ p().inShutdown = true;
+ o(lock).txnResourceStash = boost::none;
}
-void TransactionParticipant::abortArbitraryTransaction() {
- stdx::lock_guard<stdx::mutex> lock(_mutex);
-
- if (!_txnState.isInProgress(lock)) {
+void TransactionParticipant::Participant::abortTransactionIfNotPrepared(OperationContext* opCtx) {
+ if (!o().txnState.isInProgress()) {
// We do not want to abort transactions that are prepared unless we get an
// 'abortTransaction' command.
return;
}
- _abortTransactionOnSession(lock);
+ _abortTransactionOnSession(opCtx);
}
-bool TransactionParticipant::expired() const {
- stdx::lock_guard<stdx::mutex> lock(_mutex);
-
- return _txnState.isInProgress(lock) && _transactionExpireDate &&
- _transactionExpireDate < getGlobalServiceContext()->getPreciseClockSource()->now();
+bool TransactionParticipant::Observer::expiredAsOf(Date_t when) const {
+ return o().txnState.isInProgress() && o().transactionExpireDate &&
+ o().transactionExpireDate < when;
}
-void TransactionParticipant::abortActiveTransaction(OperationContext* opCtx) {
- stdx::unique_lock<stdx::mutex> lock(_mutex);
-
+void TransactionParticipant::Participant::abortActiveTransaction(OperationContext* opCtx) {
// Re-acquire the RSTL to prevent state transitions while aborting the transaction. If the
// transaction was prepared then we dropped it on preparing the transaction. We do not need to
// reacquire the PBWM because if we're not the primary we will uassert anyways.
Lock::ResourceLock rstl(opCtx->lockState(), resourceIdReplicationStateTransitionLock, MODE_IX);
- if (_txnState.isPrepared(lock) && opCtx->writesAreReplicated()) {
+ if (o().txnState.isPrepared() && opCtx->writesAreReplicated()) {
auto replCoord = repl::ReplicationCoordinator::get(opCtx);
uassert(ErrorCodes::NotMaster,
"Not primary so we cannot abort a prepared transaction",
replCoord->canAcceptWritesForDatabase(opCtx, "admin"));
}
- // This function shouldn't throw if the transaction is already aborted.
- _checkIsActiveTransaction(lock, *opCtx->getTxnNumber(), false);
- _abortActiveTransaction(
- std::move(lock), opCtx, TransactionState::kInProgress | TransactionState::kPrepared);
+ _abortActiveTransaction(opCtx, TransactionState::kInProgress | TransactionState::kPrepared);
}
-void TransactionParticipant::abortActiveUnpreparedOrStashPreparedTransaction(
+void TransactionParticipant::Participant::abortActiveUnpreparedOrStashPreparedTransaction(
OperationContext* opCtx) try {
- stdx::unique_lock<stdx::mutex> lock(_mutex);
- if (_txnState.isInSet(lock, TransactionState::kNone | TransactionState::kCommitted)) {
+ if (o().txnState.isInSet(TransactionState::kNone | TransactionState::kCommitted)) {
// If there is no active transaction, do nothing.
return;
}
- // We do this check to follow convention and maintain safety. If this were to throw we should
- // have returned in the check above. As a result, throwing here is fatal.
- _checkIsActiveTransaction(lock, *opCtx->getTxnNumber(), false);
-
// Stash the transaction if it's in prepared state.
- if (_txnState.isInSet(lock, TransactionState::kPrepared)) {
- _stashActiveTransaction(lock, opCtx);
+ if (o().txnState.isInSet(TransactionState::kPrepared)) {
+ _stashActiveTransaction(opCtx);
return;
}
// TODO SERVER-37129: Remove this invariant once we allow transactions larger than 16MB.
- invariant(!_oldestOplogEntryOpTime,
+ invariant(!p().oldestOplogEntryOpTime,
str::stream() << "The oldest oplog entry Timestamp should not have been set because "
<< "this transaction is not prepared. But, it is currently "
- << _oldestOplogEntryOpTime->toString());
+ << p().oldestOplogEntryOpTime->toString());
- _abortActiveTransaction(std::move(lock), opCtx, TransactionState::kInProgress);
+ _abortActiveTransaction(opCtx, TransactionState::kInProgress);
} catch (...) {
// It is illegal for this to throw so we catch and log this here for diagnosability.
severe() << "Caught exception during transaction " << opCtx->getTxnNumber()
- << " abort or stash on " << _sessionId().toBSON() << " in state " << _txnState << ": "
- << exceptionToStatus();
+ << " abort or stash on " << _sessionId().toBSON() << " in state " << o().txnState
+ << ": " << exceptionToStatus();
std::terminate();
}
-void TransactionParticipant::_abortActiveTransaction(stdx::unique_lock<stdx::mutex> lock,
- OperationContext* opCtx,
- TransactionState::StateSet expectedStates) {
- invariant(!_txnResourceStash);
- invariant(!_txnState.isCommittingWithPrepare(lock));
+void TransactionParticipant::Participant::_abortActiveTransaction(
+ OperationContext* opCtx, TransactionState::StateSet expectedStates) {
+ invariant(!o().txnResourceStash);
+ invariant(!o().txnState.isCommittingWithPrepare());
- if (!_txnState.isNone(lock)) {
- stdx::lock_guard<stdx::mutex> lm(_metricsMutex);
- _transactionMetricsObserver.onTransactionOperation(
+ if (!o().txnState.isNone()) {
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).transactionMetricsObserver.onTransactionOperation(
opCtx->getClient(),
CurOp::get(opCtx)->debug().additiveMetrics,
CurOp::get(opCtx)->debug().storageStats);
@@ -1346,42 +1274,35 @@ void TransactionParticipant::_abortActiveTransaction(stdx::unique_lock<stdx::mut
// OpObserver.
boost::optional<OplogSlotReserver> oplogSlotReserver;
boost::optional<OplogSlot> abortOplogSlot;
- if (_txnState.isPrepared(lock) && opCtx->writesAreReplicated()) {
+ if (o().txnState.isPrepared() && opCtx->writesAreReplicated()) {
oplogSlotReserver.emplace(opCtx);
abortOplogSlot = oplogSlotReserver->getReservedOplogSlot();
}
// Clean up the transaction resources on the opCtx even if the transaction resources on the
// session were not aborted. This actually aborts the storage-transaction.
- _cleanUpTxnResourceOnOpCtx(lock, opCtx, TerminationCause::kAborted);
+ _cleanUpTxnResourceOnOpCtx(opCtx, TerminationCause::kAborted);
// Write the abort oplog entry. This must be done after aborting the storage transaction, so
- // that the lock state is reset, and there is no max lock timeout on the locker. We need to
- // unlock the session to run the opObserver onTransactionAbort, which calls back into the
- // session.
- lock.unlock();
-
+ // that the lock state is reset, and there is no max lock timeout on the locker.
auto opObserver = opCtx->getServiceContext()->getOpObserver();
invariant(opObserver);
opObserver->onTransactionAbort(opCtx, abortOplogSlot);
- lock.lock();
- // We do not check if the active transaction number is correct here because we handle it below.
-
// Set the finishOpTime of this transaction if we have recorded this transaction's oldest oplog
// entry optime.
- if (_oldestOplogEntryOpTime) {
- _finishOpTime = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp();
+ if (p().oldestOplogEntryOpTime) {
+ p().finishOpTime = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp();
}
// Only abort the transaction in session if it's in expected states.
// When the state of active transaction on session is not expected, it means another
// thread has already aborted the transaction on session.
- if (_txnState.isInSet(lock, expectedStates)) {
- invariant(opCtx->getTxnNumber() == _activeTxnNumber);
- _abortTransactionOnSession(lock);
- } else if (opCtx->getTxnNumber() == _activeTxnNumber) {
- if (_txnState.isNone(lock)) {
+ if (o().txnState.isInSet(expectedStates)) {
+ invariant(opCtx->getTxnNumber() == o().activeTxnNumber);
+ _abortTransactionOnSession(opCtx);
+ } else if (opCtx->getTxnNumber() == o().activeTxnNumber) {
+ if (o().txnState.isNone()) {
// The active transaction is not a multi-document transaction.
invariant(opCtx->getWriteUnitOfWork() == nullptr);
return;
@@ -1392,56 +1313,57 @@ void TransactionParticipant::_abortActiveTransaction(stdx::unique_lock<stdx::mut
| TransactionState::kCommittingWithPrepare //
| TransactionState::kCommittingWithoutPrepare //
| TransactionState::kCommitted; //
- invariant(!_txnState.isInSet(lock, unabortableStates),
- str::stream() << "Cannot abort transaction in " << _txnState.toString());
+ invariant(!o().txnState.isInSet(unabortableStates),
+ str::stream() << "Cannot abort transaction in " << o().txnState.toString());
} else {
// If _activeTxnNumber is higher than ours, it means the transaction is already aborted.
- invariant(_txnState.isInSet(lock,
- TransactionState::kNone |
- TransactionState::kAbortedWithoutPrepare |
- TransactionState::kAbortedWithPrepare));
+ invariant(o().txnState.isInSet(TransactionState::kNone |
+ TransactionState::kAbortedWithoutPrepare |
+ TransactionState::kAbortedWithPrepare),
+ str::stream() << "actual state: " << o().txnState.toString());
}
}
-void TransactionParticipant::_abortTransactionOnSession(WithLock wl) {
- const auto tickSource = getGlobalServiceContext()->getTickSource();
+void TransactionParticipant::Participant::_abortTransactionOnSession(OperationContext* opCtx) {
+ const auto tickSource = opCtx->getServiceContext()->getTickSource();
// If the transaction is stashed, then we have aborted an inactive transaction.
- if (_txnResourceStash) {
- // The transaction is stashed, so we abort the inactive transaction on session.
+ if (o().txnResourceStash) {
{
- stdx::lock_guard<stdx::mutex> lm(_metricsMutex);
- _transactionMetricsObserver.onAbortInactive(
- ServerTransactionsMetrics::get(getGlobalServiceContext()),
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ // The transaction is stashed, so we abort the inactive transaction on session.
+ o(lk).transactionMetricsObserver.onAbortInactive(
+ ServerTransactionsMetrics::get(opCtx->getServiceContext()),
tickSource,
- _oldestOplogEntryOpTime,
- &Top::get(getGlobalServiceContext()));
+ p().oldestOplogEntryOpTime,
+ &Top::get(opCtx->getServiceContext()));
}
- _logSlowTransaction(wl,
- &(_txnResourceStash->locker()->getLockerInfo(boost::none))->stats,
+ _logSlowTransaction(opCtx,
+ &(o().txnResourceStash->locker()->getLockerInfo(boost::none))->stats,
TerminationCause::kAborted,
- _txnResourceStash->getReadConcernArgs());
+ o().txnResourceStash->getReadConcernArgs());
} else {
- stdx::lock_guard<stdx::mutex> lm(_metricsMutex);
- _transactionMetricsObserver.onAbortActive(
- ServerTransactionsMetrics::get(getGlobalServiceContext()),
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).transactionMetricsObserver.onAbortActive(
+ ServerTransactionsMetrics::get(opCtx->getServiceContext()),
tickSource,
- _oldestOplogEntryOpTime,
- _finishOpTime,
- &Top::get(getGlobalServiceContext()),
- _txnState.isPrepared(lm));
+ p().oldestOplogEntryOpTime,
+ p().finishOpTime,
+ &Top::get(opCtx->getServiceContext()),
+ o().txnState.isPrepared());
}
- const auto nextState = _txnState.isPrepared(wl) ? TransactionState::kAbortedWithPrepare
- : TransactionState::kAbortedWithoutPrepare;
- _resetTransactionState(wl, nextState);
+ const auto nextState = o().txnState.isPrepared() ? TransactionState::kAbortedWithPrepare
+ : TransactionState::kAbortedWithoutPrepare;
+
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ _resetTransactionState(lk, nextState);
}
-void TransactionParticipant::_cleanUpTxnResourceOnOpCtx(WithLock wl,
- OperationContext* opCtx,
- TerminationCause terminationCause) {
+void TransactionParticipant::Participant::_cleanUpTxnResourceOnOpCtx(
+ OperationContext* opCtx, TerminationCause terminationCause) {
// Log the transaction if its duration is longer than the slowMS command threshold.
_logSlowTransaction(
- wl,
+ opCtx,
&(opCtx->lockState()->getLockerInfo(CurOp::get(*opCtx)->getLockStatsBase()))->stats,
terminationCause,
repl::ReadConcernArgs::get(opCtx));
@@ -1461,64 +1383,42 @@ void TransactionParticipant::_cleanUpTxnResourceOnOpCtx(WithLock wl,
opCtx->lockState()->unsetMaxLockTimeout();
}
-void TransactionParticipant::_checkIsActiveTransaction(WithLock wl,
- const TxnNumber& requestTxnNumber,
- bool checkAbort) const {
- uassert(ErrorCodes::ConflictingOperationInProgress,
- str::stream() << "Cannot perform operations on requested transaction "
- << requestTxnNumber
- << " on session "
- << _sessionId()
- << " because a different transaction "
- << _activeTxnNumber
- << " is now active.",
- requestTxnNumber == _activeTxnNumber);
-
- uassert(ErrorCodes::NoSuchTransaction,
- str::stream() << "Transaction " << _activeTxnNumber << " has been aborted.",
- !checkAbort || !_txnState.isAborted(wl));
-}
-
-void TransactionParticipant::_checkIsCommandValidWithTxnState(WithLock wl,
- const TxnNumber& requestTxnNumber,
- const std::string& cmdName) {
- // Throw NoSuchTransaction error instead of TransactionAborted error since this is the entry
- // point of transaction execution.
+void TransactionParticipant::Participant::_checkIsCommandValidWithTxnState(
+ const TxnNumber& requestTxnNumber, const std::string& cmdName) const {
uassert(ErrorCodes::NoSuchTransaction,
str::stream() << "Transaction " << requestTxnNumber << " has been aborted.",
- !_txnState.isAborted(wl));
+ !o().txnState.isAborted());
// Cannot change committed transaction but allow retrying commitTransaction command.
uassert(ErrorCodes::TransactionCommitted,
str::stream() << "Transaction " << requestTxnNumber << " has been committed.",
- cmdName == "commitTransaction" || !_txnState.isCommitted(wl));
+ cmdName == "commitTransaction" || !o().txnState.isCommitted());
// Disallow operations other than abort, prepare or commit on a prepared transaction
uassert(ErrorCodes::PreparedTransactionInProgress,
str::stream() << "Cannot call any operation other than abort, prepare or commit on"
<< " a prepared transaction",
- !_txnState.isPrepared(wl) ||
+ !o().txnState.isPrepared() ||
preparedTxnCmdWhitelist.find(cmdName) != preparedTxnCmdWhitelist.cend());
}
-BSONObj TransactionParticipant::reportStashedState() const {
+BSONObj TransactionParticipant::Observer::reportStashedState(OperationContext* opCtx) const {
BSONObjBuilder builder;
- reportStashedState(&builder);
+ reportStashedState(opCtx, &builder);
return builder.obj();
}
-void TransactionParticipant::reportStashedState(BSONObjBuilder* builder) const {
- stdx::lock_guard<stdx::mutex> lm(_mutex);
-
- if (_txnResourceStash && _txnResourceStash->locker()) {
- if (auto lockerInfo = _txnResourceStash->locker()->getLockerInfo(boost::none)) {
- invariant(_activeTxnNumber != kUninitializedTxnNumber);
+void TransactionParticipant::Observer::reportStashedState(OperationContext* opCtx,
+ BSONObjBuilder* builder) const {
+ if (o().txnResourceStash && o().txnResourceStash->locker()) {
+ if (auto lockerInfo = o().txnResourceStash->locker()->getLockerInfo(boost::none)) {
+ invariant(o().activeTxnNumber != kUninitializedTxnNumber);
builder->append("type", "idleSession");
builder->append("host", getHostNameCachedAndPort());
builder->append("desc", "inactive transaction");
const auto& lastClientInfo =
- _transactionMetricsObserver.getSingleTransactionStats().getLastClientInfo();
+ o().transactionMetricsObserver.getSingleTransactionStats().getLastClientInfo();
builder->append("client", lastClientInfo.clientHostAndPort);
builder->append("connectionId", lastClientInfo.connectionId);
builder->append("appName", lastClientInfo.appName);
@@ -1531,7 +1431,7 @@ void TransactionParticipant::reportStashedState(BSONObjBuilder* builder) const {
BSONObjBuilder transactionBuilder;
_reportTransactionStats(
- lm, &transactionBuilder, _txnResourceStash->getReadConcernArgs());
+ opCtx, &transactionBuilder, o().txnResourceStash->getReadConcernArgs());
builder->append("transaction", transactionBuilder.obj());
builder->append("waitingForLock", false);
@@ -1542,19 +1442,18 @@ void TransactionParticipant::reportStashedState(BSONObjBuilder* builder) const {
}
}
-void TransactionParticipant::reportUnstashedState(OperationContext* opCtx,
- BSONObjBuilder* builder) const {
+void TransactionParticipant::Observer::reportUnstashedState(OperationContext* opCtx,
+ BSONObjBuilder* builder) const {
// This method may only take the metrics mutex, as it is called with the Client mutex held. So
// we cannot check the stashed state directly. Instead, a transaction is considered unstashed
// if it is not actually a transaction (retryable write, no stash used), or is active (not
// stashed), or has ended (any stash would be cleared).
- stdx::lock_guard<stdx::mutex> lm(_metricsMutex);
- const auto& singleTransactionStats = _transactionMetricsObserver.getSingleTransactionStats();
+ const auto& singleTransactionStats = o().transactionMetricsObserver.getSingleTransactionStats();
if (!singleTransactionStats.isForMultiDocumentTransaction() ||
singleTransactionStats.isActive() || singleTransactionStats.isEnded()) {
BSONObjBuilder transactionBuilder;
- _reportTransactionStats(lm, &transactionBuilder, repl::ReadConcernArgs::get(opCtx));
+ _reportTransactionStats(opCtx, &transactionBuilder, repl::ReadConcernArgs::get(opCtx));
builder->append("transaction", transactionBuilder.obj());
}
}
@@ -1660,8 +1559,7 @@ bool TransactionParticipant::TransactionState::_isLegalTransition(StateFlag oldS
MONGO_UNREACHABLE;
}
-void TransactionParticipant::TransactionState::transitionTo(WithLock,
- StateFlag newState,
+void TransactionParticipant::TransactionState::transitionTo(StateFlag newState,
TransitionValidation shouldValidate) {
if (shouldValidate == TransitionValidation::kValidateTransition) {
invariant(TransactionState::_isLegalTransition(_state, newState),
@@ -1673,15 +1571,15 @@ void TransactionParticipant::TransactionState::transitionTo(WithLock,
_state = newState;
}
-void TransactionParticipant::_reportTransactionStats(WithLock wl,
- BSONObjBuilder* builder,
- repl::ReadConcernArgs readConcernArgs) const {
- const auto tickSource = getGlobalServiceContext()->getTickSource();
- _transactionMetricsObserver.getSingleTransactionStats().report(
+void TransactionParticipant::Observer::_reportTransactionStats(
+ OperationContext* opCtx, BSONObjBuilder* builder, repl::ReadConcernArgs readConcernArgs) const {
+ const auto tickSource = opCtx->getServiceContext()->getTickSource();
+ o().transactionMetricsObserver.getSingleTransactionStats().report(
builder, readConcernArgs, tickSource, tickSource->getTicks());
}
-std::string TransactionParticipant::_transactionInfoForLog(
+std::string TransactionParticipant::Participant::_transactionInfoForLog(
+ OperationContext* opCtx,
const SingleThreadedLockStats* lockStats,
TerminationCause terminationCause,
repl::ReadConcernArgs readConcernArgs) const {
@@ -1696,15 +1594,15 @@ std::string TransactionParticipant::_transactionInfoForLog(
_sessionId().serialize(&lsidBuilder);
lsidBuilder.doneFast();
- parametersBuilder.append("txnNumber", _activeTxnNumber);
- parametersBuilder.append("autocommit", _autoCommit ? *_autoCommit : true);
+ parametersBuilder.append("txnNumber", o().activeTxnNumber);
+ parametersBuilder.append("autocommit", p().autoCommit ? *p().autoCommit : true);
readConcernArgs.appendInfo(&parametersBuilder);
s << "parameters:" << parametersBuilder.obj().toString() << ",";
- s << " readTimestamp:" << _speculativeTransactionReadOpTime.getTimestamp().toString() << ",";
+ s << " readTimestamp:" << p().speculativeTransactionReadOpTime.getTimestamp().toString() << ",";
- const auto& singleTransactionStats = _transactionMetricsObserver.getSingleTransactionStats();
+ const auto& singleTransactionStats = o().transactionMetricsObserver.getSingleTransactionStats();
s << singleTransactionStats.getOpDebug()->additiveMetrics.report();
@@ -1712,7 +1610,7 @@ std::string TransactionParticipant::_transactionInfoForLog(
terminationCause == TerminationCause::kCommitted ? "committed" : "aborted";
s << " terminationCause:" << terminationCauseString;
- auto tickSource = getGlobalServiceContext()->getTickSource();
+ auto tickSource = opCtx->getServiceContext()->getTickSource();
auto curTick = tickSource->getTicks();
s << " timeActiveMicros:"
@@ -1742,15 +1640,15 @@ std::string TransactionParticipant::_transactionInfoForLog(
s << " wasPrepared:" << txnWasPrepared;
if (txnWasPrepared) {
s << " totalPreparedDurationMicros:" << totalPreparedDuration;
- s << " prepareOpTime:" << _prepareOpTime.toString();
+ s << " prepareOpTime:" << o().prepareOpTime.toString();
}
- if (_oldestOplogEntryOpTime) {
- s << " oldestOplogEntryOpTime:" << _oldestOplogEntryOpTime->toString();
+ if (p().oldestOplogEntryOpTime) {
+ s << " oldestOplogEntryOpTime:" << p().oldestOplogEntryOpTime->toString();
}
- if (_finishOpTime) {
- s << " finishOpTime:" << _finishOpTime->toString();
+ if (p().finishOpTime) {
+ s << " finishOpTime:" << p().finishOpTime->toString();
}
// Total duration of the transaction.
@@ -1760,83 +1658,80 @@ std::string TransactionParticipant::_transactionInfoForLog(
return s.str();
}
-void TransactionParticipant::_logSlowTransaction(WithLock wl,
- const SingleThreadedLockStats* lockStats,
- TerminationCause terminationCause,
- repl::ReadConcernArgs readConcernArgs) {
+void TransactionParticipant::Participant::_logSlowTransaction(
+ OperationContext* opCtx,
+ const SingleThreadedLockStats* lockStats,
+ TerminationCause terminationCause,
+ repl::ReadConcernArgs readConcernArgs) {
// Only log multi-document transactions.
- if (!_txnState.isNone(wl)) {
- const auto tickSource = getGlobalServiceContext()->getTickSource();
+ if (!o().txnState.isNone()) {
+ const auto tickSource = opCtx->getServiceContext()->getTickSource();
// Log the transaction if its duration is longer than the slowMS command threshold.
- if (_transactionMetricsObserver.getSingleTransactionStats().getDuration(
+ if (o().transactionMetricsObserver.getSingleTransactionStats().getDuration(
tickSource, tickSource->getTicks()) > Milliseconds(serverGlobalParams.slowMS)) {
log(logger::LogComponent::kTransaction)
<< "transaction "
- << _transactionInfoForLog(lockStats, terminationCause, readConcernArgs);
+ << _transactionInfoForLog(opCtx, lockStats, terminationCause, readConcernArgs);
}
}
}
-void TransactionParticipant::_setNewTxnNumber(WithLock wl, const TxnNumber& txnNumber) {
+void TransactionParticipant::Participant::_setNewTxnNumber(OperationContext* opCtx,
+ const TxnNumber& txnNumber) {
uassert(ErrorCodes::PreparedTransactionInProgress,
"Cannot change transaction number while the session has a prepared transaction",
- !_txnState.isInSet(
- wl, TransactionState::kPrepared | TransactionState::kCommittingWithPrepare));
+ !o().txnState.isInSet(TransactionState::kPrepared |
+ TransactionState::kCommittingWithPrepare));
LOG_FOR_TRANSACTION(4) << "New transaction started with txnNumber: " << txnNumber
<< " on session with lsid " << _sessionId().getId();
// Abort the existing transaction if it's not prepared, committed, or aborted.
- if (_txnState.isInProgress(wl)) {
- _abortTransactionOnSession(wl);
+ if (o().txnState.isInProgress()) {
+ _abortTransactionOnSession(opCtx);
}
- _activeTxnNumber = txnNumber;
- _lastWriteOpTime = repl::OpTime();
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).activeTxnNumber = txnNumber;
+ o(lk).lastWriteOpTime = repl::OpTime();
// Reset the retryable writes state
- _resetRetryableWriteState(wl);
+ _resetRetryableWriteState();
// Reset the transactional state
- _resetTransactionState(wl, TransactionState::kNone);
+ _resetTransactionState(lk, TransactionState::kNone);
// Reset the transactions metrics
- stdx::lock_guard<stdx::mutex> lm(_metricsMutex);
- _transactionMetricsObserver.resetSingleTransactionStats(txnNumber);
+ o(lk).transactionMetricsObserver.resetSingleTransactionStats(txnNumber);
}
-void TransactionParticipant::refreshFromStorageIfNeeded() {
- const auto opCtx = _opCtx();
+void TransactionParticipant::Participant::refreshFromStorageIfNeeded(OperationContext* opCtx) {
invariant(!opCtx->getClient()->isInDirectClient());
invariant(!opCtx->lockState()->isLocked());
- if (_isValid)
+ if (p().isValid)
return;
auto activeTxnHistory = fetchActiveTransactionHistory(opCtx, _sessionId());
-
- stdx::lock_guard<stdx::mutex> lg(_mutex);
-
const auto& lastTxnRecord = activeTxnHistory.lastTxnRecord;
-
if (lastTxnRecord) {
- _activeTxnNumber = lastTxnRecord->getTxnNum();
- _lastWriteOpTime = lastTxnRecord->getLastWriteOpTime();
- _activeTxnCommittedStatements = std::move(activeTxnHistory.committedStatements);
- _hasIncompleteHistory = activeTxnHistory.hasIncompleteHistory;
+ stdx::lock_guard<Client> lg(*opCtx->getClient());
+ o(lg).activeTxnNumber = lastTxnRecord->getTxnNum();
+ o(lg).lastWriteOpTime = lastTxnRecord->getLastWriteOpTime();
+ p().activeTxnCommittedStatements = std::move(activeTxnHistory.committedStatements);
+ p().hasIncompleteHistory = activeTxnHistory.hasIncompleteHistory;
if (activeTxnHistory.transactionCommitted) {
- _txnState.transitionTo(
- lg,
+ o(lg).txnState.transitionTo(
TransactionState::kCommitted,
TransactionState::TransitionValidation::kRelaxTransitionValidation);
}
}
- _isValid = true;
+ p().isValid = true;
}
-void TransactionParticipant::onWriteOpCompletedOnPrimary(
+void TransactionParticipant::Participant::onWriteOpCompletedOnPrimary(
OperationContext* opCtx,
TxnNumber txnNumber,
std::vector<StmtId> stmtIdsWritten,
@@ -1844,9 +1739,7 @@ void TransactionParticipant::onWriteOpCompletedOnPrimary(
Date_t lastStmtIdWriteDate,
boost::optional<DurableTxnStateEnum> txnState) {
invariant(opCtx->lockState()->inAWriteUnitOfWork());
- invariant(txnNumber == _activeTxnNumber);
-
- stdx::unique_lock<stdx::mutex> ul(_mutex);
+ invariant(txnNumber == o().activeTxnNumber);
// Sanity check that we don't double-execute statements
for (const auto stmtId : stmtIdsWritten) {
@@ -1860,97 +1753,89 @@ void TransactionParticipant::onWriteOpCompletedOnPrimary(
const auto updateRequest =
_makeUpdateRequest(lastStmtIdWriteOpTime, lastStmtIdWriteDate, txnState);
- ul.unlock();
-
repl::UnreplicatedWritesBlock doNotReplicateWrites(opCtx);
updateSessionEntry(opCtx, updateRequest);
- _registerUpdateCacheOnCommit(std::move(stmtIdsWritten), lastStmtIdWriteOpTime);
+ _registerUpdateCacheOnCommit(opCtx, std::move(stmtIdsWritten), lastStmtIdWriteOpTime);
}
-void TransactionParticipant::onMigrateCompletedOnPrimary(OperationContext* opCtx,
- TxnNumber txnNumber,
- std::vector<StmtId> stmtIdsWritten,
- const repl::OpTime& lastStmtIdWriteOpTime,
- Date_t oplogLastStmtIdWriteDate) {
+void TransactionParticipant::Participant::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);
-
- _checkValid(ul);
- _checkIsActiveTransaction(ul, txnNumber);
+ invariant(txnNumber == o().activeTxnNumber);
// We do not migrate transaction oplog entries so don't set the txn state
const auto txnState = boost::none;
const auto updateRequest =
_makeUpdateRequest(lastStmtIdWriteOpTime, oplogLastStmtIdWriteDate, txnState);
- ul.unlock();
-
repl::UnreplicatedWritesBlock doNotReplicateWrites(opCtx);
updateSessionEntry(opCtx, updateRequest);
- _registerUpdateCacheOnCommit(std::move(stmtIdsWritten), lastStmtIdWriteOpTime);
+ _registerUpdateCacheOnCommit(opCtx, std::move(stmtIdsWritten), lastStmtIdWriteOpTime);
}
-void TransactionParticipant::_invalidate(WithLock) {
- _isValid = false;
- _activeTxnNumber = kUninitializedTxnNumber;
- _lastWriteOpTime = repl::OpTime();
+void TransactionParticipant::Participant::_invalidate(WithLock wl) {
+ p().isValid = false;
+ o(wl).activeTxnNumber = kUninitializedTxnNumber;
+ o(wl).lastWriteOpTime = repl::OpTime();
// Reset the transactions metrics.
- stdx::lock_guard<stdx::mutex> lm(_metricsMutex);
- _transactionMetricsObserver.resetSingleTransactionStats(_activeTxnNumber);
+ o(wl).transactionMetricsObserver.resetSingleTransactionStats(o().activeTxnNumber);
}
-void TransactionParticipant::_resetRetryableWriteState(WithLock) {
- _activeTxnCommittedStatements.clear();
- _hasIncompleteHistory = false;
+void TransactionParticipant::Participant::_resetRetryableWriteState() {
+ p().activeTxnCommittedStatements.clear();
+ p().hasIncompleteHistory = false;
}
-void TransactionParticipant::_resetTransactionState(WithLock wl,
- TransactionState::StateFlag state) {
+void TransactionParticipant::Participant::_resetTransactionState(
+ WithLock wl, TransactionState::StateFlag state) {
// If we are transitioning to kNone, we are either starting a new transaction or aborting a
// prepared transaction for rollback. In the latter case, we will need to relax the invariant
// that prevents transitioning from kPrepared to kNone.
- if (_txnState.isPrepared(wl) && state == TransactionState::kNone) {
- _txnState.transitionTo(
- wl, state, TransactionState::TransitionValidation::kRelaxTransitionValidation);
+ if (o().txnState.isPrepared() && state == TransactionState::kNone) {
+ o(wl).txnState.transitionTo(
+ state, TransactionState::TransitionValidation::kRelaxTransitionValidation);
} else {
- _txnState.transitionTo(wl, state);
+ o(wl).txnState.transitionTo(state);
}
- _transactionOperationBytes = 0;
- _transactionOperations.clear();
- _prepareOpTime = repl::OpTime();
- _oldestOplogEntryOpTime = boost::none;
- _finishOpTime = boost::none;
- _speculativeTransactionReadOpTime = repl::OpTime();
- _multikeyPathInfo.clear();
- _autoCommit = boost::none;
+ p().transactionOperationBytes = 0;
+ p().transactionOperations.clear();
+ o(wl).prepareOpTime = repl::OpTime();
+ p().oldestOplogEntryOpTime = boost::none;
+ p().finishOpTime = boost::none;
+ p().speculativeTransactionReadOpTime = repl::OpTime();
+ p().multikeyPathInfo.clear();
+ p().autoCommit = boost::none;
// Release any locks held by this participant and abort the storage transaction.
- _txnResourceStash = boost::none;
+ o(wl).txnResourceStash = boost::none;
}
-void TransactionParticipant::invalidate() {
- stdx::lock_guard<stdx::mutex> lg(_mutex);
+void TransactionParticipant::Participant::invalidate(OperationContext* opCtx) {
+ stdx::lock_guard<Client> lg(*opCtx->getClient());
uassert(ErrorCodes::PreparedTransactionInProgress,
"Cannot invalidate prepared transaction",
- !_txnState.isInSet(
- lg, TransactionState::kPrepared | TransactionState::kCommittingWithPrepare));
+ !o().txnState.isInSet(TransactionState::kPrepared |
+ TransactionState::kCommittingWithPrepare));
// Invalidate the session and clear both the retryable writes and transactional states on
// this participant.
_invalidate(lg);
- _resetRetryableWriteState(lg);
+ _resetRetryableWriteState();
_resetTransactionState(lg, TransactionState::kNone);
}
-void TransactionParticipant::abortPreparedTransactionForRollback() {
- stdx::lock_guard<stdx::mutex> lg(_mutex);
+void TransactionParticipant::Participant::abortPreparedTransactionForRollback(
+ OperationContext* opCtx) {
+ stdx::lock_guard<Client> lg(*opCtx->getClient());
// Invalidate the session.
_invalidate(lg);
@@ -1958,7 +1843,7 @@ void TransactionParticipant::abortPreparedTransactionForRollback() {
uassert(51030,
str::stream() << "Cannot call abortPreparedTransactionForRollback on unprepared "
<< "transaction.",
- _txnState.isPrepared(lg));
+ o().txnState.isPrepared());
// It should be safe to clear transactionOperationBytes and transactionOperations because
// we only modify these variables when adding an operation to a transaction. Since this
@@ -1969,13 +1854,8 @@ void TransactionParticipant::abortPreparedTransactionForRollback() {
_resetTransactionState(lg, TransactionState::kNone);
}
-repl::OpTime TransactionParticipant::getLastWriteOpTime() const {
- stdx::lock_guard<stdx::mutex> lg(_mutex);
- return _lastWriteOpTime;
-}
-
-boost::optional<repl::OplogEntry> TransactionParticipant::checkStatementExecuted(
- StmtId stmtId) const {
+boost::optional<repl::OplogEntry> TransactionParticipant::Participant::checkStatementExecuted(
+ OperationContext* opCtx, StmtId stmtId) const {
const auto stmtTimestamp = _checkStatementExecuted(stmtId);
if (!stmtTimestamp)
@@ -1983,7 +1863,7 @@ boost::optional<repl::OplogEntry> TransactionParticipant::checkStatementExecuted
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;
@@ -1992,38 +1872,23 @@ boost::optional<repl::OplogEntry> TransactionParticipant::checkStatementExecuted
MONGO_UNREACHABLE;
}
-bool TransactionParticipant::checkStatementExecutedNoOplogEntryFetch(StmtId stmtId) const {
+bool TransactionParticipant::Participant::checkStatementExecutedNoOplogEntryFetch(
+ StmtId stmtId) const {
return bool(_checkStatementExecuted(stmtId));
}
-void TransactionParticipant::_checkValid(WithLock) const {
- uassert(ErrorCodes::ConflictingOperationInProgress,
- str::stream() << "Session " << _sessionId()
- << " was concurrently modified and the operation must be retried.",
- _isValid);
-}
-
-void TransactionParticipant::_checkIsActiveTransaction(WithLock, TxnNumber txnNumber) const {
- uassert(ErrorCodes::ConflictingOperationInProgress,
- str::stream() << "Cannot perform operations on transaction " << txnNumber
- << " on session "
- << _sessionId()
- << " because a different transaction "
- << _activeTxnNumber
- << " is now active.",
- txnNumber == _activeTxnNumber);
-}
-
-boost::optional<repl::OpTime> TransactionParticipant::_checkStatementExecuted(StmtId stmtId) const {
- invariant(_isValid);
+boost::optional<repl::OpTime> TransactionParticipant::Participant::_checkStatementExecuted(
+ StmtId stmtId) const {
+ invariant(p().isValid);
- const auto it = _activeTxnCommittedStatements.find(stmtId);
- if (it == _activeTxnCommittedStatements.end()) {
+ const auto it = p().activeTxnCommittedStatements.find(stmtId);
+ if (it == p().activeTxnCommittedStatements.end()) {
uassert(ErrorCodes::IncompleteTransactionHistory,
- str::stream() << "Incomplete history detected for transaction " << _activeTxnNumber
+ str::stream() << "Incomplete history detected for transaction "
+ << o().activeTxnNumber
<< " on session "
<< _sessionId(),
- !_hasIncompleteHistory);
+ !p().hasIncompleteHistory);
return boost::none;
}
@@ -2031,7 +1896,7 @@ boost::optional<repl::OpTime> TransactionParticipant::_checkStatementExecuted(St
return it->second;
}
-UpdateRequest TransactionParticipant::_makeUpdateRequest(
+UpdateRequest TransactionParticipant::Participant::_makeUpdateRequest(
const repl::OpTime& newLastWriteOpTime,
Date_t newLastWriteDate,
boost::optional<DurableTxnStateEnum> newState) const {
@@ -2040,7 +1905,7 @@ UpdateRequest TransactionParticipant::_makeUpdateRequest(
const auto updateBSON = [&] {
SessionTxnRecord newTxnRecord;
newTxnRecord.setSessionId(_sessionId());
- newTxnRecord.setTxnNum(_activeTxnNumber);
+ newTxnRecord.setTxnNum(o().activeTxnNumber);
newTxnRecord.setLastWriteOpTime(newLastWriteOpTime);
newTxnRecord.setLastWriteDate(newLastWriteDate);
newTxnRecord.setState(newState);
@@ -2053,34 +1918,37 @@ UpdateRequest TransactionParticipant::_makeUpdateRequest(
return updateRequest;
}
-void TransactionParticipant::_registerUpdateCacheOnCommit(
- std::vector<StmtId> stmtIdsWritten, const repl::OpTime& lastStmtIdWriteOpTime) {
- _opCtx()->recoveryUnit()->onCommit(
- [ this, stmtIdsWritten = std::move(stmtIdsWritten), lastStmtIdWriteOpTime ](
+void TransactionParticipant::Participant::_registerUpdateCacheOnCommit(
+ OperationContext* opCtx,
+ std::vector<StmtId> stmtIdsWritten,
+ const repl::OpTime& lastStmtIdWriteOpTime) {
+ opCtx->recoveryUnit()->onCommit(
+ [ opCtx, stmtIdsWritten = std::move(stmtIdsWritten), lastStmtIdWriteOpTime ](
boost::optional<Timestamp>) {
- invariant(_isValid);
+ TransactionParticipant::Participant participant(opCtx);
+ invariant(participant.p().isValid);
- RetryableWritesStats::get(getGlobalServiceContext())
+ RetryableWritesStats::get(opCtx->getServiceContext())
->incrementTransactionsCollectionWriteCount();
- stdx::lock_guard<stdx::mutex> lg(_mutex);
+ stdx::lock_guard<Client> lg(*opCtx->getClient());
// The cache of the last written record must always be advanced after a write so that
// subsequent writes have the correct point to start from.
- _lastWriteOpTime = lastStmtIdWriteOpTime;
+ participant.o(lg).lastWriteOpTime = lastStmtIdWriteOpTime;
for (const auto stmtId : stmtIdsWritten) {
if (stmtId == kIncompleteHistoryStmtId) {
- _hasIncompleteHistory = true;
+ participant.p().hasIncompleteHistory = true;
continue;
}
- const auto insertRes =
- _activeTxnCommittedStatements.emplace(stmtId, lastStmtIdWriteOpTime);
+ const auto insertRes = participant.p().activeTxnCommittedStatements.emplace(
+ stmtId, lastStmtIdWriteOpTime);
if (!insertRes.second) {
const auto& existingOpTime = insertRes.first->second;
- fassertOnRepeatedExecution(_sessionId(),
- _activeTxnNumber,
+ fassertOnRepeatedExecution(participant._sessionId(),
+ participant.o().activeTxnNumber,
stmtId,
existingOpTime,
lastStmtIdWriteOpTime);
@@ -2093,7 +1961,7 @@ void TransactionParticipant::_registerUpdateCacheOnCommit(
const auto closeConnectionElem = data["closeConnection"];
if (closeConnectionElem.eoo() || closeConnectionElem.Bool()) {
- _opCtx()->getClient()->session()->end();
+ opCtx->getClient()->session()->end();
}
const auto failBeforeCommitExceptionElem = data["failBeforeCommitExceptionCode"];
@@ -2101,7 +1969,7 @@ void TransactionParticipant::_registerUpdateCacheOnCommit(
const auto failureCode = ErrorCodes::Error(int(failBeforeCommitExceptionElem.Number()));
uasserted(failureCode,
str::stream() << "Failing write for " << _sessionId() << ":"
- << _activeTxnNumber
+ << o().activeTxnNumber
<< " due to failpoint. The write must not be reflected.");
}
}
diff --git a/src/mongo/db/transaction_participant.h b/src/mongo/db/transaction_participant.h
index 650e1296f65..f90a12ed77b 100644
--- a/src/mongo/db/transaction_participant.h
+++ b/src/mongo/db/transaction_participant.h
@@ -42,6 +42,7 @@
#include "mongo/db/repl/oplog_entry.h"
#include "mongo/db/repl/read_concern_args.h"
#include "mongo/db/session.h"
+#include "mongo/db/session_catalog.h"
#include "mongo/db/session_txn_record_gen.h"
#include "mongo/db/single_transaction_stats.h"
#include "mongo/db/storage/recovery_unit.h"
@@ -81,12 +82,96 @@ enum class TerminationCause {
};
/**
- * A state machine that coordinates a distributed transaction commit with the transaction
- * coordinator.
+ * This class maintains the state of a transaction running on a server session. It can only exist as
+ * a decoration on the Session object and its state can only be modified by the thread which has the
+ * session checked-out.
+ *
+ * Its methods are split in two groups with distinct read/write and concurrency control rules. See
+ * the comments below for more information.
*/
class TransactionParticipant {
MONGO_DISALLOW_COPYING(TransactionParticipant);
+ struct PrivateState;
+ struct ObservableState;
+
+ /**
+ * Indicates the state of the current multi-document transaction, if any. If the transaction is
+ * in any state but kInProgress, no more operations can be collected. Once the transaction is in
+ * kPrepared, the transaction is not allowed to abort outside of an 'abortTransaction' command.
+ * At this point, aborting the transaction must log an 'abortTransaction' oplog entry.
+ */
+ class TransactionState {
+ public:
+ enum StateFlag {
+ kNone = 1 << 0,
+ kInProgress = 1 << 1,
+ kPrepared = 1 << 2,
+ kCommittingWithoutPrepare = 1 << 3,
+ kCommittingWithPrepare = 1 << 4,
+ kCommitted = 1 << 5,
+ kAbortedWithoutPrepare = 1 << 6,
+ kAbortedWithPrepare = 1 << 7
+ };
+
+ using StateSet = int;
+ bool isInSet(StateSet stateSet) const {
+ return _state & stateSet;
+ }
+
+ /**
+ * Transitions the session from the current state to the new state. If transition validation
+ * is not relaxed, invariants if the transition is illegal.
+ */
+ enum class TransitionValidation { kValidateTransition, kRelaxTransitionValidation };
+ void transitionTo(
+ StateFlag newState,
+ TransitionValidation shouldValidate = TransitionValidation::kValidateTransition);
+
+ bool inMultiDocumentTransaction() const {
+ return _state == kInProgress || _state == kPrepared;
+ }
+
+ bool isNone() const {
+ return _state == kNone;
+ }
+
+ bool isInProgress() const {
+ return _state == kInProgress;
+ }
+
+ bool isPrepared() const {
+ return _state == kPrepared;
+ }
+
+ bool isCommittingWithoutPrepare() const {
+ return _state == kCommittingWithoutPrepare;
+ }
+
+ bool isCommittingWithPrepare() const {
+ return _state == kCommittingWithPrepare;
+ }
+
+ bool isCommitted() const {
+ return _state == kCommitted;
+ }
+
+ bool isAborted() const {
+ return _state == kAbortedWithPrepare || _state == kAbortedWithoutPrepare;
+ }
+
+ std::string toString() const {
+ return toString(_state);
+ }
+
+ static std::string toString(StateFlag state);
+
+ private:
+ static bool _isLegalTransition(StateFlag oldState, StateFlag newState);
+
+ StateFlag _state = kNone;
+ };
+
public:
/**
* Holds state for a snapshot read or multi-statement transaction in between network
@@ -98,9 +183,9 @@ public:
/**
* Stashes transaction state from 'opCtx' in the newly constructed TxnResources.
- * Ephemerally holds the Client lock associated with opCtx.
+ * Caller must hold the Client lock associated with opCtx, attested by WithLock.
*/
- TxnResources(OperationContext* opCtx, StashStyle stashStyle);
+ TxnResources(WithLock, OperationContext* opCtx, StashStyle stashStyle) noexcept;
~TxnResources();
// Rule of 5: because we have a class-defined destructor, we need to explictly specify
@@ -146,11 +231,6 @@ public:
SideTransactionBlock(OperationContext* opCtx);
~SideTransactionBlock();
- // Rule of 5: because we have a class-defined destructor, we need to explictly specify
- // the move operator and move assignment operator.
- SideTransactionBlock(SideTransactionBlock&&) = default;
- SideTransactionBlock& operator=(SideTransactionBlock&&) = default;
-
private:
boost::optional<TxnResources> _txnResources;
OperationContext* _opCtx;
@@ -160,413 +240,566 @@ public:
static const BSONObj kDeadEndSentinel;
- TransactionParticipant();
- ~TransactionParticipant();
-
/**
- * Obtains the transaction participant from a session and a syntactic sugar variant, which
- * obtains it from an operation context on which the session has been checked-out.
+ * Class used by observers to examine the state of a TransactionParticipant.
*/
- static TransactionParticipant* get(OperationContext* opCtx);
- static TransactionParticipant* get(Session* session);
+ class Observer {
+ public:
+ explicit Observer(const ObservableSession& session);
- /**
- * When the server returns a NoSuchTransaction error for a command, it performs a noop write if
- * there is a writeConcern on the command. The TransientTransactionError label is only appended
- * to a NoSuchTransaction response for 'commitTransaction' and 'coordinateCommitTransaction' if
- * there is no writeConcern error. This ensures that if 'commitTransaction' or
- * 'coordinateCommitTransaction' is run with w:majority, then the TransientTransactionError
- * label is only returned if the transaction is not committed on any valid branch of history,
- * so the driver or application can safely retry the entire transaction.
- */
- static void performNoopWriteForNoSuchTransaction(OperationContext* opCtx);
+ /**
+ * Returns the currently active transaction number on this participant.
+ */
+ TxnNumber getActiveTxnNumber() const {
+ return o().activeTxnNumber;
+ }
- /**
- * Blocking method, which loads the transaction state from storage if it has been marked as
- * needing refresh.
- *
- * In order to avoid the possibility of deadlock, this method must not be called while holding a
- * lock.
- */
- void refreshFromStorageIfNeeded();
+ /**
+ * Returns the op time of the last committed write for this session and transaction. If no
+ * write has completed yet, returns an empty timestamp.
+ */
+ repl::OpTime getLastWriteOpTime() const {
+ return o().lastWriteOpTime;
+ }
- /**
- * Starts a new transaction (and if the txnNumber is newer aborts any in-progress transaction on
- * the session), or continues an already active transaction.
- *
- * 'autocommit' comes from the 'autocommit' field in the original client request. The only valid
- * values are boost::none (meaning no autocommit was specified) and false (meaning that this is
- * the beginning of a multi-statement transaction).
- *
- * 'startTransaction' comes from the 'startTransaction' field in the original client request.
- * See below for the acceptable values and the meaning of the combinations of autocommit and
- * startTransaction.
- *
- * autocommit = boost::none, startTransaction = boost::none: Means retryable write
- * autocommit = false, startTransaction = boost::none: Means continuation of a multi-statement
- * transaction
- * autocommit = false, startTransaction = true: Means abort whatever transaction is in progress
- * on the session and start a new transaction
- *
- * Any combination other than the ones listed above will invariant since it is expected that the
- * caller has performed the necessary customer input validations.
- *
- * Exceptions of note, which can be thrown are:
- * - TransactionTooOld - if attempt is made to start a transaction older than the currently
- * active one or the last one which committed
- * - PreparedTransactionInProgress - if the transaction is in the prepared state and a new
- * transaction or retryable write is attempted
- */
- void beginOrContinue(TxnNumber txnNumber,
- boost::optional<bool> autocommit,
- boost::optional<bool> startTransaction);
+ /**
+ * Returns the prepare op time that was selected for the transaction, which can be Null if
+ * the transaction is not prepared.
+ */
+ repl::OpTime getPrepareOpTime() const {
+ return o().prepareOpTime;
+ }
- /**
- * Used only by the secondary oplog application logic. Equivalent to 'beginOrContinue(txnNumber,
- * false, true)' without performing any checks for whether the new txnNumber will start a
- * transaction number in the past.
- *
- * NOTE: This method assumes that there are no concurrent users of the transaction since it
- * unconditionally changes the active transaction on the session.
- */
- void beginOrContinueTransactionUnconditionally(TxnNumber txnNumber);
+ /**
+ * Returns whether the transaction has exceeded its expiration time.
+ */
+ bool expiredAsOf(Date_t when) const;
- /**
- * Transfers management of transaction resources from the OperationContext to the Session.
- */
- void stashTransactionResources(OperationContext* opCtx);
+ /**
+ * Returns whether we are in a multi-document transaction, which means we have an active
+ * transaction which has autocommit:false and has not been committed or aborted. It is
+ * possible that the current transaction is stashed onto the stack via a
+ * `SideTransactionBlock`.
+ */
+ bool inMultiDocumentTransaction() const {
+ return o().txnState.inMultiDocumentTransaction();
+ };
- /**
- * Transfers management of transaction resources from the Session to the OperationContext.
- */
- void unstashTransactionResources(OperationContext* opCtx, const std::string& cmdName);
+ bool transactionIsCommitted() const {
+ return o().txnState.isCommitted();
+ }
- /**
- * Puts a transaction into a prepared state and returns the prepareTimestamp.
- *
- * On secondary, the "prepareTimestamp" will be given in the oplog.
- */
- Timestamp prepareTransaction(OperationContext* opCtx,
- boost::optional<repl::OpTime> prepareOptime);
+ bool transactionIsAborted() const {
+ return o().txnState.isAborted();
+ }
- /**
- * Commits the transaction, including committing the write unit of work and updating
- * transaction state.
- *
- * Throws an exception if the transaction is prepared.
- */
- void commitUnpreparedTransaction(OperationContext* opCtx);
+ bool transactionIsPrepared() const {
+ return o().txnState.isPrepared();
+ }
- /**
- * Commits the transaction, including committing the write unit of work and updating
- * transaction state.
- *
- * On a secondary, the "commitOplogEntryOpTime" will be the OpTime of the commitTransaction oplog
- * entry.
- *
- * Throws an exception if the transaction is not prepared or if the 'commitTimestamp' is null.
- */
- void commitPreparedTransaction(OperationContext* opCtx,
- Timestamp commitTimestamp,
- boost::optional<repl::OpTime> commitOplogEntryOpTime);
+ /**
+ * Returns true if we are in an active multi-document transaction or if the transaction has
+ * been aborted. This is used to cover the case where a transaction has been aborted, but
+ * the
+ * OperationContext state has not been cleared yet.
+ */
+ bool inActiveOrKilledMultiDocumentTransaction() const {
+ return o().txnState.inMultiDocumentTransaction() || o().txnState.isAborted();
+ }
- /**
- * Aborts the transaction outside the transaction, releasing transaction resources.
- *
- * Not called with session checked out.
- */
- void abortArbitraryTransaction();
-
- /*
- * Aborts the transaction inside the transaction, releasing transaction resources.
- * We're inside the transaction when we have the Session checked out and 'opCtx' owns the
- * transaction resources.
- * Aborts the transaction and releases transaction resources when we have the Session checked
- * out and 'opCtx' owns the transaction resources.
- */
- void abortActiveTransaction(OperationContext* opCtx);
+ /**
+ * If this session is holding stashed locks in txnResourceStash, reports the current state
+ * of the session using the provided builder.
+ */
+ BSONObj reportStashedState(OperationContext* opCtx) const;
+ void reportStashedState(OperationContext* opCtx, BSONObjBuilder* builder) const;
- /*
- * If the transaction is prepared, stash its resources. If not, it's the same as
- * abortActiveTransaction.
- */
- void abortActiveUnpreparedOrStashPreparedTransaction(OperationContext* opCtx);
+ /**
+ * If this session is not holding stashed locks in txnResourceStash (transaction is active),
+ * reports the current state of the session using the provided builder.
+ */
+ void reportUnstashedState(OperationContext* opCtx, BSONObjBuilder* builder) const;
- /**
- * Aborts the storage transaction of the prepared transaction on this participant by releasing
- * its resources. Also invalidates the session and the current transaction state.
- * Avoids writing any oplog entries or making any changes to the transaction table since the
- * state for prepared transactions will be re-constituted during replication recovery.
- */
- void abortPreparedTransactionForRollback();
+ protected:
+ explicit Observer(TransactionParticipant* tp) : _tp(tp) {}
- /**
- * Adds a stored operation to the list of stored operations for the current multi-document
- * (non-autocommit) transaction. It is illegal to add operations when no multi-document
- * transaction is in progress.
- */
- void addTransactionOperation(OperationContext* opCtx, const repl::ReplOperation& operation);
+ const TransactionParticipant::ObservableState& o() const {
+ return _tp->_o;
+ }
- /**
- * Returns a reference to the stored operations for a completed multi-document (non-autocommit)
- * transaction. "Completed" implies that no more operations will be added to the transaction.
- * It is legal to call this method only when the transaction state is in progress or committed.
- */
- std::vector<repl::ReplOperation>& retrieveCompletedTransactionOperations(
- OperationContext* opCtx);
+ const LogicalSessionId& _sessionId() const;
- /**
- * Clears the stored operations for an multi-document (non-autocommit) transaction, marking
- * the transaction as closed. It is illegal to attempt to add operations to the transaction
- * after this is called.
- */
- void clearOperationsInMemory(OperationContext* opCtx);
+ // Reports transaction stats for both active and inactive transactions using the provided
+ // builder.
+ void _reportTransactionStats(OperationContext* opCtx,
+ BSONObjBuilder* builder,
+ repl::ReadConcernArgs readConcernArgs) const;
- /**
- * Yield or reacquire locks for prepared transacitons, used on replication state transition.
- */
- void refreshLocksForPreparedTransaction(OperationContext* opCtx, bool yieldLocks);
+ TransactionParticipant* _tp;
+ }; // class Observer
- /**
- * May only be called while a multi-document transaction is not committed and adds the multi-key
- * path info to the set of path infos to be updated at commit time.
- */
- void addUncommittedMultikeyPathInfo(MultikeyPathInfo info) {
- invariant(inMultiDocumentTransaction());
- _multikeyPathInfo.emplace_back(std::move(info));
- }
/**
- * May only be called while a mutil-document transaction is not committed and returns the path
- * infos which have been added so far.
+ * Class used by a thread that has checked out the TransactionParticipant's session to
+ * observe and modify the transaction participant.
*/
- const std::vector<MultikeyPathInfo>& getUncommittedMultikeyPathInfos() const {
- invariant(inMultiDocumentTransaction());
- return _multikeyPathInfo;
- }
+ class Participant : public Observer {
+ public:
+ explicit Participant(OperationContext* opCtx);
+ explicit Participant(const SessionToKill& session);
- /**
- * Called after a write under the specified transaction completes while the node is a primary
- * and specifies the statement ids which were written. Must be called while the caller is still
- * in the write's WUOW. Updates the on-disk state of the session to match the specified
- * transaction/opTime and keeps the cached state in sync.
- *
- * 'txnState' is 'none' for retryable writes.
- *
- * Must only be called with the session checked-out.
- *
- * Throws if the session has been invalidated or the active transaction number doesn't match.
- */
- void onWriteOpCompletedOnPrimary(OperationContext* opCtx,
- TxnNumber txnNumber,
- std::vector<StmtId> stmtIdsWritten,
- const repl::OpTime& lastStmtIdWriteOpTime,
- Date_t lastStmtIdWriteDate,
- boost::optional<DurableTxnStateEnum> txnState);
+ explicit operator bool() const {
+ return _tp;
+ }
- /**
- * 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
- * specified transaction/opTime and keeps the cached state in sync.
- *
- * May be called concurrently with onWriteOpCompletedOnPrimary or onMigrateCompletedOnPrimary
- * and doesn't require the session to be checked-out.
- *
- * Throws if the session has been invalidated or the active transaction number is newer than the
- * one specified.
- */
- void onMigrateCompletedOnPrimary(OperationContext* opCtx,
- TxnNumber txnNumber,
- std::vector<StmtId> stmtIdsWritten,
- const repl::OpTime& lastStmtIdWriteOpTime,
- Date_t oplogLastStmtIdWriteDate);
+ /**
+ * Blocking method, which loads the transaction state from storage if it has been marked as
+ * needing refresh.
+ *
+ * In order to avoid the possibility of deadlock, this method must not be called while
+ * holding a
+ * lock.
+ */
+ void refreshFromStorageIfNeeded(OperationContext* opCtx);
- /**
- * Checks whether the given statementId for the specified transaction has already executed and
- * if so, returns the oplog entry which was generated by that write. If the statementId hasn't
- * executed, returns boost::none.
- *
- * Must only be called with the session checked-out.
- *
- * Throws if the session has been invalidated or the active transaction number doesn't match.
- */
- boost::optional<repl::OplogEntry> checkStatementExecuted(StmtId stmtId) const;
+ /**
+ * Starts a new transaction (and if the txnNumber is newer aborts any in-progress
+ * transaction on
+ * the session), or continues an already active transaction.
+ *
+ * 'autocommit' comes from the 'autocommit' field in the original client request. The only
+ * valid
+ * values are boost::none (meaning no autocommit was specified) and false (meaning that this
+ * is
+ * the beginning of a multi-statement transaction).
+ *
+ * 'startTransaction' comes from the 'startTransaction' field in the original client
+ * request.
+ * See below for the acceptable values and the meaning of the combinations of autocommit and
+ * startTransaction.
+ *
+ * autocommit = boost::none, startTransaction = boost::none: Means retryable write
+ * autocommit = false, startTransaction = boost::none: Means continuation of a
+ * multi-statement
+ * transaction
+ * autocommit = false, startTransaction = true: Means abort whatever transaction is in
+ * progress
+ * on the session and start a new transaction
+ *
+ * Any combination other than the ones listed above will invariant since it is expected that
+ * the
+ * caller has performed the necessary customer input validations.
+ *
+ * Exceptions of note, which can be thrown are:
+ * - TransactionTooOld - if attempt is made to start a transaction older than the
+ * currently
+ * active one or the last one which committed
+ * - PreparedTransactionInProgress - if the transaction is in the prepared state and a new
+ * transaction or retryable write is attempted
+ */
+ void beginOrContinue(OperationContext* opCtx,
+ TxnNumber txnNumber,
+ boost::optional<bool> autocommit,
+ boost::optional<bool> startTransaction);
- /**
- * Checks whether the given statementId for the specified transaction has already executed
- * without fetching the oplog entry which was generated by that write.
- *
- * Must only be called with the session checked-out.
- *
- * Throws if the session has been invalidated or the active transaction number doesn't match.
- */
- bool checkStatementExecutedNoOplogEntryFetch(StmtId stmtId) const;
+ /**
+ * Used only by the secondary oplog application logic. Equivalent to
+ * 'beginOrContinue(txnNumber,
+ * false, true)' without performing any checks for whether the new txnNumber will start a
+ * transaction number in the past.
+ */
+ void beginOrContinueTransactionUnconditionally(OperationContext* opCtx,
+ TxnNumber txnNumber);
- /**
- * Marks the session as requiring refresh. Used when the session state has been modified
- * externally, such as through a direct write to the transactions table.
- */
- void invalidate();
+ /**
+ * Transfers management of transaction resources from the currently checked-out
+ * OperationContext
+ * to the Session.
+ */
+ void stashTransactionResources(OperationContext* opCtx);
- /**
- * Kills the transaction if it is running, ensuring that it releases all resources, even if the
- * transaction is in prepare(). Avoids writing any oplog entries or making any changes to the
- * transaction table. State for prepared transactions will be re-constituted at startup.
- * Note that we don't take any active steps to prevent continued use of this
- * TransactionParticipant after shutdown() is called, but we rely on callers to not
- * continue using the TransactionParticipant once we are in shutdown.
- */
- void shutdown();
+ /**
+ * Transfers management of transaction resources from the Session to the currently
+ * checked-out
+ * OperationContext.
+ */
+ void unstashTransactionResources(OperationContext* opCtx, const std::string& cmdName);
- /**
- * Returns the currently active transaction number on this participant.
- */
- TxnNumber getActiveTxnNumber() const {
- stdx::lock_guard<stdx::mutex> lg(_mutex);
- return _activeTxnNumber;
- }
+ /**
+ * Puts a transaction into a prepared state and returns the prepareTimestamp.
+ *
+ * On secondary, the "prepareTimestamp" will be given in the oplog.
+ */
+ Timestamp prepareTransaction(OperationContext* opCtx,
+ boost::optional<repl::OpTime> prepareOptime);
- /**
- * Returns the op time of the last committed write for this session and transaction. If no write
- * has completed yet, returns an empty timestamp.
- *
- * Throws if the session has been invalidated or the active transaction number doesn't match.
- */
- repl::OpTime getLastWriteOpTime() const;
+ /**
+ * Commits the transaction, including committing the write unit of work and updating
+ * transaction state.
+ *
+ * Throws an exception if the transaction is prepared.
+ */
+ void commitUnpreparedTransaction(OperationContext* opCtx);
- /**
- * Returns the prepare op time that was selected for the transaction, which can be Null if the
- * transaction is not prepared.
- */
- repl::OpTime getPrepareOpTime() const {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- return _prepareOpTime;
- }
+ /**
+ * Commits the transaction, including committing the write unit of work and updating
+ * transaction state.
+ *
+ * On a secondary, the "commitOplogEntryOpTime" will be the OpTime of the commitTransaction
+ * oplog entry.
+ *
+ * Throws an exception if the transaction is not prepared or if the 'commitTimestamp' is
+ * null.
+ */
+ void commitPreparedTransaction(OperationContext* opCtx,
+ Timestamp commitTimestamp,
+ boost::optional<repl::OpTime> commitOplogEntryOpTime);
- /**
- * Returns whether the transaction has exceeded its expiration time.
- */
- bool expired() const;
+ /**
+ * Aborts the transaction, if it is not in the "prepared" state.
+ */
+ void abortTransactionIfNotPrepared(OperationContext* opCtx);
- /**
- * Returns whether we are in a multi-document transaction, which means we have an active
- * transaction which has autoCommit:false and has not been committed or aborted. It is possible
- * that the current transaction is stashed onto the stack via a `SideTransactionBlock`.
- */
- bool inMultiDocumentTransaction() const {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- return _txnState.inMultiDocumentTransaction(lk);
- };
+ /*
+ * Aborts the transaction, releasing transaction resources.
+ */
+ void abortActiveTransaction(OperationContext* opCtx);
- bool transactionIsCommitted() const {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- return _txnState.isCommitted(lk);
- }
+ /*
+ * If the transaction is prepared, stash its resources. If not, it's the same as
+ * abortActiveTransaction.
+ */
+ void abortActiveUnpreparedOrStashPreparedTransaction(OperationContext* opCtx);
- bool transactionIsAborted() const {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- return _txnState.isAborted(lk);
- }
+ /**
+ * Aborts the storage transaction of the prepared transaction on this participant by
+ * releasing its resources. Also invalidates the session and the current transaction state.
+ * Avoids writing any oplog entries or making any changes to the transaction table since the
+ * state for prepared transactions will be re-constituted during replication recovery.
+ */
+ void abortPreparedTransactionForRollback(OperationContext* opCtx);
- bool transactionIsPrepared() const {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- return _txnState.isPrepared(lk);
- }
+ /**
+ * Adds a stored operation to the list of stored operations for the current multi-document
+ * (non-autocommit) transaction. It is illegal to add operations when no multi-document
+ * transaction is in progress.
+ */
+ void addTransactionOperation(OperationContext* opCtx, const repl::ReplOperation& operation);
- /**
- * Returns true if we are in an active multi-document transaction or if the transaction has
- * been aborted. This is used to cover the case where a transaction has been aborted, but the
- * OperationContext state has not been cleared yet.
- */
- bool inActiveOrKilledMultiDocumentTransaction() const {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- return (_txnState.inMultiDocumentTransaction(lk) || _txnState.isAborted(lk));
- }
+ /**
+ * Returns a reference to the stored operations for a completed multi-document
+ * (non-autocommit) transaction. "Completed" implies that no more operations will be added
+ * to the transaction. It is legal to call this method only when the transaction state is
+ * in progress or committed.
+ */
+ std::vector<repl::ReplOperation>& retrieveCompletedTransactionOperations(
+ OperationContext* opCtx);
- /**
- * If this session is holding stashed locks in _txnResourceStash, reports the current state of
- * the session using the provided builder. Locks the session object's mutex while running.
- */
- BSONObj reportStashedState() const;
- void reportStashedState(BSONObjBuilder* builder) const;
+ /**
+ * Clears the stored operations for an multi-document (non-autocommit) transaction, marking
+ * the transaction as closed. It is illegal to attempt to add operations to the transaction
+ * after this is called.
+ */
+ void clearOperationsInMemory(OperationContext* opCtx);
- /**
- * If this session is not holding stashed locks in _txnResourceStash (transaction is active),
- * reports the current state of the session using the provided builder. Locks the session
- * object's mutex while running.
- *
- * If this is called from a thread other than the owner of the opCtx, that thread must be
- * holding the client lock.
- */
- void reportUnstashedState(OperationContext* opCtx, BSONObjBuilder* builder) const;
-
- //
- // Methods used for unit-testing only
- //
-
- std::string getTransactionInfoForLogForTest(
- const SingleThreadedLockStats* lockStats,
- bool committed,
- const repl::ReadConcernArgs& readConcernArgs) const {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- TerminationCause terminationCause =
- committed ? TerminationCause::kCommitted : TerminationCause::kAborted;
- return _transactionInfoForLog(lockStats, terminationCause, readConcernArgs);
- }
+ /**
+ * Yield or reacquire locks for prepared transactions, used on replication state transition.
+ */
+ void refreshLocksForPreparedTransaction(OperationContext* opCtx, bool yieldLocks);
- SingleTransactionStats getSingleTransactionStatsForTest() const {
- stdx::lock_guard<stdx::mutex> lk(_metricsMutex);
- return _transactionMetricsObserver.getSingleTransactionStats();
- }
+ /**
+ * May only be called while a multi-document transaction is not committed and adds the
+ * multi-key
+ * path info to the set of path infos to be updated at commit time.
+ */
+ void addUncommittedMultikeyPathInfo(MultikeyPathInfo info) {
+ invariant(inMultiDocumentTransaction());
+ p().multikeyPathInfo.emplace_back(std::move(info));
+ }
- std::vector<repl::ReplOperation> getTransactionOperationsForTest() const {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- return _transactionOperations;
- }
+ /**
+ * May only be called while a mutil-document transaction is not committed and returns the
+ * path
+ * infos which have been added so far.
+ */
+ const std::vector<MultikeyPathInfo>& getUncommittedMultikeyPathInfos() const {
+ invariant(inMultiDocumentTransaction());
+ return p().multikeyPathInfo;
+ }
- repl::OpTime getSpeculativeTransactionReadOpTimeForTest() const {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- return _speculativeTransactionReadOpTime;
- }
+ /**
+ * Called after a write under the specified transaction completes while the node is a
+ * primary
+ * and specifies the statement ids which were written. Must be called while the caller is
+ * still
+ * in the write's WUOW. Updates the on-disk state of the session to match the specified
+ * transaction/opTime and keeps the cached state in sync.
+ *
+ * 'txnState' is 'none' for retryable writes.
+ *
+ * Throws if the session has been invalidated or the active transaction number doesn't
+ * match.
+ */
+ void onWriteOpCompletedOnPrimary(OperationContext* opCtx,
+ TxnNumber txnNumber,
+ std::vector<StmtId> stmtIdsWritten,
+ const repl::OpTime& lastStmtIdWriteOpTime,
+ Date_t lastStmtIdWriteDate,
+ boost::optional<DurableTxnStateEnum> txnState);
- boost::optional<repl::OpTime> getFinishOpTimeForTest() const {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- return _finishOpTime;
- }
+ /**
+ * 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 specified transaction/opTime and keeps the cached state in sync.
+ *
+ * Throws if the session has been invalidated or the active transaction number is newer than
+ * the one specified.
+ */
+ void onMigrateCompletedOnPrimary(OperationContext* opCtx,
+ TxnNumber txnNumber,
+ std::vector<StmtId> stmtIdsWritten,
+ const repl::OpTime& lastStmtIdWriteOpTime,
+ Date_t oplogLastStmtIdWriteDate);
- boost::optional<repl::OpTime> getOldestOplogEntryOpTimeForTest() const {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- return _oldestOplogEntryOpTime;
- }
+ /**
+ * Checks whether the given statementId for the specified transaction has already executed
+ * and if so, returns the oplog entry which was generated by that write. If the statementId
+ * hasn't executed, returns boost::none.
+ *
+ * Throws if the session has been invalidated or the active transaction number doesn't
+ * match.
+ */
+ boost::optional<repl::OplogEntry> checkStatementExecuted(OperationContext* opCtx,
+ StmtId stmtId) const;
- const Locker* getTxnResourceStashLockerForTest() const {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- invariant(_txnResourceStash);
- return _txnResourceStash->locker();
- }
+ /**
+ * Checks whether the given statementId for the specified transaction has already executed
+ * without fetching the oplog entry which was generated by that write.
+ *
+ * Throws if the session has been invalidated or the active transaction number doesn't
+ * match.
+ */
+ bool checkStatementExecutedNoOplogEntryFetch(StmtId stmtId) const;
- void transitionToPreparedforTest() {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- _txnState.transitionTo(lk, TransactionState::kPrepared);
- }
+ /**
+ * Marks the session as requiring refresh. Used when the session state has been modified
+ * externally, such as through a direct write to the transactions table.
+ */
+ void invalidate(OperationContext* opCtx);
- void transitionToCommittingWithPrepareforTest() {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- _txnState.transitionTo(lk, TransactionState::kCommittingWithPrepare);
- }
+ /**
+ * Kills the transaction if it is running, ensuring that it releases all resources, even if
+ * the transaction is in prepare(). Avoids writing any oplog entries or making any changes
+ * to the transaction table. State for prepared transactions will be re-constituted at
+ * startup. Note that we don't take any active steps to prevent continued use of this
+ * TransactionParticipant after shutdown() is called, but we rely on callers to not continue
+ * using the TransactionParticipant once we are in shutdown.
+ */
+ void shutdown(OperationContext* opCtx);
+
+ //
+ // Methods for use in C++ unit tests, only. Beware: these methods may not adhere to the
+ // concurrency control rules.
+ //
+
+ std::string getTransactionInfoForLogForTest(
+ OperationContext* opCtx,
+ const SingleThreadedLockStats* lockStats,
+ bool committed,
+ const repl::ReadConcernArgs& readConcernArgs) const {
+
+ TerminationCause terminationCause =
+ committed ? TerminationCause::kCommitted : TerminationCause::kAborted;
+ return _transactionInfoForLog(opCtx, lockStats, terminationCause, readConcernArgs);
+ }
+
+ SingleTransactionStats getSingleTransactionStatsForTest() const {
+ return o().transactionMetricsObserver.getSingleTransactionStats();
+ }
+
+ std::vector<repl::ReplOperation> getTransactionOperationsForTest() const {
+ return p().transactionOperations;
+ }
+
+ repl::OpTime getSpeculativeTransactionReadOpTimeForTest() const {
+ return p().speculativeTransactionReadOpTime;
+ }
+
+ boost::optional<repl::OpTime> getOldestOplogEntryOpTimeForTest() const {
+ return p().oldestOplogEntryOpTime;
+ }
+
+ boost::optional<repl::OpTime> getFinishOpTimeForTest() const {
+ return p().finishOpTime;
+ }
+
+ const Locker* getTxnResourceStashLockerForTest() const {
+ invariant(o().txnResourceStash);
+ return o().txnResourceStash->locker();
+ }
+
+ void transitionToPreparedforTest(OperationContext* opCtx) {
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).txnState.transitionTo(TransactionState::kPrepared);
+ }
+
+ void transitionToCommittingWithPrepareforTest(OperationContext* opCtx) {
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).txnState.transitionTo(TransactionState::kCommittingWithPrepare);
+ }
+ void transitionToAbortedWithoutPrepareforTest(OperationContext* opCtx) {
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).txnState.transitionTo(TransactionState::kAbortedWithoutPrepare);
+ }
+
+ void transitionToAbortedWithPrepareforTest(OperationContext* opCtx) {
+ stdx::lock_guard<Client> lk(*opCtx->getClient());
+ o(lk).txnState.transitionTo(TransactionState::kAbortedWithPrepare);
+ }
- void transitionToAbortedWithoutPrepareforTest() {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- _txnState.transitionTo(lk, TransactionState::kAbortedWithoutPrepare);
+ private:
+ boost::optional<repl::OpTime> _checkStatementExecuted(StmtId stmtId) const;
+
+ UpdateRequest _makeUpdateRequest(const repl::OpTime& newLastWriteOpTime,
+ Date_t newLastWriteDate,
+ boost::optional<DurableTxnStateEnum> newState) const;
+
+ void _registerUpdateCacheOnCommit(OperationContext* opCtx,
+ std::vector<StmtId> stmtIdsWritten,
+ const repl::OpTime& lastStmtIdWriteTs);
+
+ // Called for speculative transactions to fix the optime of the snapshot to read from.
+ void _setSpeculativeTransactionOpTime(OperationContext* opCtx,
+ SpeculativeTransactionOpTime opTimeChoice);
+
+
+ // Like _setSpeculativeTransactionOpTime, but caller chooses timestamp of snapshot
+ // explicitly.
+ // It is up to the caller to ensure that Timestamp is greater than or equal to the
+ // all-committed
+ // optime before calling this method (e.g. by calling
+ // ReplCoordinator::waitForOpTimeForRead).
+ void _setSpeculativeTransactionReadTimestamp(OperationContext* opCtx, Timestamp timestamp);
+
+ // Finishes committing the multi-document transaction after the storage-transaction has been
+ // committed, the oplog entry has been inserted into the oplog, and the transactions table
+ // has
+ // been updated.
+ void _finishCommitTransaction(OperationContext* opCtx);
+
+ // Commits the storage-transaction on the OperationContext.
+ //
+ // This should be called *without* the Client being locked.
+ void _commitStorageTransaction(OperationContext* opCtx);
+
+ // Stash transaction resources.
+ void _stashActiveTransaction(OperationContext* opCtx);
+
+ // Abort the transaction if it's in one of the expected states and clean up the transaction
+ // states associated with the opCtx.
+ void _abortActiveTransaction(OperationContext* opCtx,
+ TransactionState::StateSet expectedStates);
+
+ // Releases stashed transaction resources to abort the transaction on the session.
+ void _abortTransactionOnSession(OperationContext* opCtx);
+
+ // Clean up the transaction resources unstashed on operation context.
+ void _cleanUpTxnResourceOnOpCtx(OperationContext* opCtx, TerminationCause terminationCause);
+
+ // Checks if the command can be run on this transaction based on the state of the
+ // transaction.
+ void _checkIsCommandValidWithTxnState(const TxnNumber& requestTxnNumber,
+ const std::string& cmdName) const;
+
+ // Logs the transaction information if it has run slower than the global parameter slowMS.
+ // The
+ // transaction must be committed or aborted when this function is called.
+ void _logSlowTransaction(OperationContext* opCtx,
+ const SingleThreadedLockStats* lockStats,
+ TerminationCause terminationCause,
+ repl::ReadConcernArgs readConcernArgs);
+
+ // This method returns a string with information about a slow transaction. The format of the
+ // logging string produced should match the format used for slow operation logging. A
+ // transaction must be completed (committed or aborted) and a valid LockStats reference must
+ // be
+ // passed in order for this method to be called.
+ std::string _transactionInfoForLog(OperationContext* opCtx,
+ const SingleThreadedLockStats* lockStats,
+ TerminationCause terminationCause,
+ repl::ReadConcernArgs readConcernArgs) const;
+
+ // Bumps up the transaction number of this transaction and perform the necessary cleanup.
+ void _setNewTxnNumber(OperationContext* opCtx, const TxnNumber& txnNumber);
+
+ // Attempt to begin or retry a retryable write at the given transaction number.
+ void _beginOrContinueRetryableWrite(OperationContext* opCtx, TxnNumber txnNumber);
+
+ // Attempt to begin a new multi document transaction at the given transaction number.
+ void _beginMultiDocumentTransaction(OperationContext* opCtx, TxnNumber txnNumber);
+
+ // Attempt to continue an in-progress multi document transaction at the given transaction
+ // number.
+ void _continueMultiDocumentTransaction(OperationContext* opCtx, TxnNumber txnNumber);
+
+ // Helper that invalidates the session state and activeTxnNumber. Also resets the single
+ // transaction stats because the session is no longer valid.
+ void _invalidate(WithLock);
+
+ // Helper that resets the retryable writes state.
+ void _resetRetryableWriteState();
+
+ // Helper that resets the transactional state. This is used when aborting a transaction,
+ // invalidating a transaction, or starting a new transaction.
+ void _resetTransactionState(WithLock wl, TransactionState::StateFlag state);
+
+ // Helper that updates ServerTransactionsMetrics once a transaction commits.
+ void _updateTxnMetricsOnCommit(OperationContext* opCtx, bool isCommittingWithPrepare);
+
+ // Releases the resources held in *o().txnResources to the operation context.
+ // o().txnResources must be engaged prior to calling this.
+ void _releaseTransactionResourcesToOpCtx(OperationContext* opCtx);
+
+ TransactionParticipant::PrivateState& p() {
+ return _tp->_p;
+ }
+ const TransactionParticipant::PrivateState& p() const {
+ return _tp->_p;
+ }
+ TransactionParticipant::ObservableState& o(WithLock) {
+ return _tp->_o;
+ }
+ using Observer::o;
+ }; // class Participant
+
+ static Participant get(OperationContext* opCtx) {
+ return Participant(opCtx);
}
- void transitionToAbortedWithPrepareforTest() {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- _txnState.transitionTo(lk, TransactionState::kAbortedWithPrepare);
+ static Participant get(const SessionToKill& session) {
+ return Participant(session);
}
+ static Observer get(const ObservableSession& osession) {
+ return Observer(osession);
+ }
+
+ /**
+ * When the server returns a NoSuchTransaction error for a command, it performs a noop write if
+ * there is a writeConcern on the command. The TransientTransactionError label is only appended
+ * to a NoSuchTransaction response for 'commitTransaction' and 'coordinateCommitTransaction' if
+ * there is no writeConcern error. This ensures that if 'commitTransaction' or
+ * 'coordinateCommitTransaction' is run with w:majority, then the TransientTransactionError
+ * label is only returned if the transaction is not committed on any valid branch of history,
+ * so the driver or application can safely retry the entire transaction.
+ */
+ static void performNoopWriteForNoSuchTransaction(OperationContext* opCtx);
+
+ TransactionParticipant() = default;
+ ~TransactionParticipant() = default;
+
private:
/**
* Reserves a slot in the oplog with an open storage-transaction while it is alive. Reserves the
@@ -578,11 +811,6 @@ private:
OplogSlotReserver(OperationContext* opCtx);
~OplogSlotReserver();
- // Rule of 5: because we have a class-defined destructor, we need to explictly specify
- // the move operator and move assignment operator.
- OplogSlotReserver(OplogSlotReserver&&) = default;
- OplogSlotReserver& operator=(OplogSlotReserver&&) = default;
-
/**
* Returns the oplog slot reserved at construction.
*/
@@ -598,299 +826,103 @@ private:
OplogSlot _oplogSlot;
};
- /**
- * Indicates the state of the current multi-document transaction, if any. If the transaction is
- * in any state but kInProgress, no more operations can be collected. Once the transaction is in
- * kPrepared, the transaction is not allowed to abort outside of an 'abortTransaction' command.
- * At this point, aborting the transaction must log an 'abortTransaction' oplog entry.
- */
- class TransactionState {
- public:
- enum StateFlag {
- kNone = 1 << 0,
- kInProgress = 1 << 1,
- kPrepared = 1 << 2,
- kCommittingWithoutPrepare = 1 << 3,
- kCommittingWithPrepare = 1 << 4,
- kCommitted = 1 << 5,
- kAbortedWithoutPrepare = 1 << 6,
- kAbortedWithPrepare = 1 << 7
- };
-
- using StateSet = int;
- bool isInSet(WithLock, StateSet stateSet) const {
- return _state & stateSet;
- }
-
- /**
- * Transitions the session from the current state to the new state. If transition validation
- * is not relaxed, invariants if the transition is illegal.
- */
- enum class TransitionValidation { kValidateTransition, kRelaxTransitionValidation };
- void transitionTo(
- WithLock,
- StateFlag newState,
- TransitionValidation shouldValidate = TransitionValidation::kValidateTransition);
-
- bool inMultiDocumentTransaction(WithLock) const {
- return _state == kInProgress || _state == kPrepared;
- }
-
- bool isNone(WithLock) const {
- return _state == kNone;
- }
+ friend std::ostream& operator<<(std::ostream& s, TransactionState txnState) {
+ return (s << txnState.toString());
+ }
- bool isInProgress(WithLock) const {
- return _state == kInProgress;
- }
+ friend StringBuilder& operator<<(StringBuilder& s, TransactionState txnState) {
+ return (s << txnState.toString());
+ }
- bool isPrepared(WithLock) const {
- return _state == kPrepared;
- }
+ /**
+ * State in this struct may be read by methods of Observer or Participant, and may be written by
+ * methods of Participant when they acquire the lock on the opCtx's Client. Access this inside
+ * Observer and Participant using the private o() method for reading and (Participant only) the
+ * o(WithLock) method for writing.
+ */
+ struct ObservableState {
+ // Holds transaction resources between network operations.
+ boost::optional<TxnResources> txnResourceStash;
- bool isCommittingWithoutPrepare(WithLock) const {
- return _state == kCommittingWithoutPrepare;
- }
+ // Maintains the transaction state and the transition table for legal state transitions.
+ TransactionState txnState;
- bool isCommittingWithPrepare(WithLock) const {
- return _state == kCommittingWithPrepare;
- }
+ // Tracks the last seen txn number for the session and is always >= to the transaction
+ // number in the last written txn record. When it is > than that in the last written txn
+ // record, this means a new transaction has begun on the session, but it hasn't yet
+ // performed any writes.
+ TxnNumber activeTxnNumber{kUninitializedTxnNumber};
- bool isCommitted(WithLock) const {
- return _state == kCommitted;
- }
+ // Caches what is known to be the last optime written for the active transaction.
+ repl::OpTime lastWriteOpTime;
- bool isAborted(WithLock) const {
- return _state == kAbortedWithoutPrepare || _state == kAbortedWithPrepare;
- }
+ // Set when a snapshot read / transaction begins. Alleviates cache pressure by limiting how
+ // long a snapshot will remain open and available. Checked in combination with _txnState to
+ // determine whether the transaction should be aborted. This is unset until a transaction
+ // begins on the session, and then reset only when new transactions begin.
+ boost::optional<Date_t> transactionExpireDate;
- std::string toString() const {
- return toString(_state);
- }
+ // Track the prepareOpTime, the OpTime of the 'prepare' oplog entry for a transaction.
+ repl::OpTime prepareOpTime;
- static std::string toString(StateFlag state);
+ // Tracks and updates transaction metrics upon the appropriate transaction event.
+ TransactionMetricsObserver transactionMetricsObserver;
+ } _o;
- private:
- static bool _isLegalTransition(StateFlag oldState, StateFlag newState);
+ /**
+ * State in this struct may be read and written by methods of the Participant, only. It may
+ * access the struct via the private p() accessor. No further locking is required in methods
+ * of the Participant.
+ */
+ struct PrivateState {
+ // Only set if the server is shutting down and it has been ensured that no new requests will
+ // be accepted. Ensures that any transaction resources will not be stashed from the
+ // operation context onto the transaction participant when the session is checked-in so that
+ // locks can automatically get freed.
+ bool inShutdown = false;
- StateFlag _state = kNone;
- };
+ // Holds oplog data for operations which have been applied in the current multi-document
+ // transaction.
+ std::vector<repl::ReplOperation> transactionOperations;
- friend std::ostream& operator<<(std::ostream& s, TransactionState txnState) {
- return (s << txnState.toString());
- }
+ // Total size in bytes of all operations within the _transactionOperations vector.
+ size_t transactionOperationBytes = 0;
- friend StringBuilder& operator<<(StringBuilder& s, TransactionState txnState) {
- return (s << txnState.toString());
- }
+ // The autocommit setting of this transaction. Should always be false for multi-statement
+ // transaction. Currently only needed for diagnostics reporting.
+ boost::optional<bool> autoCommit;
- // Shortcut to obtain the id of the session under which this participant runs
- const LogicalSessionId& _sessionId() const;
+ // The OpTime a speculative transaction is reading from and also the earliest opTime it
+ // should wait for write concern for on commit.
+ repl::OpTime speculativeTransactionReadOpTime;
- // Shortcut to obtain the currently checked-out operation context under this participant runs
- OperationContext* _opCtx() const;
+ // Contains uncommitted multi-key path info entries which were modified under this
+ // transaction so they can be applied to subsequent opreations before the transaction
+ // commits
+ std::vector<MultikeyPathInfo> multikeyPathInfo;
- /**
- * Performing any checks based on the in-memory state of the TransactionParticipant requires
- * that the object is fully in sync with its on-disk representation in the transactions table.
- * This method checks that. The object can be out of sync with the on-disk representation either
- * when it was just created, or after invalidate() was called (which typically happens after a
- * direct write to the transactions table).
- */
- void _checkValid(WithLock) const;
-
- // Checks that the specified transaction number is the same as the activeTxnNumber. Effectively
- // 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(StmtId stmtId) const;
-
- UpdateRequest _makeUpdateRequest(const repl::OpTime& newLastWriteOpTime,
- Date_t newLastWriteDate,
- boost::optional<DurableTxnStateEnum> newState) const;
-
- void _registerUpdateCacheOnCommit(std::vector<StmtId> stmtIdsWritten,
- const repl::OpTime& lastStmtIdWriteTs);
-
- // Called for speculative transactions to fix the optime of the snapshot to read from.
- void _setSpeculativeTransactionOpTime(WithLock,
- OperationContext* opCtx,
- SpeculativeTransactionOpTime opTimeChoice);
-
-
- // Like _setSpeculativeTransactionOpTime, but caller chooses timestamp of snapshot explicitly.
- // It is up to the caller to ensure that Timestamp is greater than or equal to the all-committed
- // optime before calling this method (e.g. by calling ReplCoordinator::waitForOpTimeForRead).
- void _setSpeculativeTransactionReadTimestamp(WithLock,
- OperationContext* opCtx,
- Timestamp timestamp);
-
- // Finishes committing the multi-document transaction after the storage-transaction has been
- // committed, the oplog entry has been inserted into the oplog, and the transactions table has
- // been updated.
- void _finishCommitTransaction(WithLock lk, OperationContext* opCtx);
-
- // Commits the storage-transaction on the OperationContext.
- //
- // This should be called *without* the mutex being locked.
- void _commitStorageTransaction(OperationContext* opCtx);
-
- // Stash transaction resources.
- void _stashActiveTransaction(WithLock, OperationContext* opCtx);
-
- // Abort the transaction if it's in one of the expected states and clean up the transaction
- // states associated with the opCtx.
- void _abortActiveTransaction(stdx::unique_lock<stdx::mutex> lock,
- OperationContext* opCtx,
- TransactionState::StateSet expectedStates);
-
- // Releases stashed transaction resources to abort the transaction on the session.
- void _abortTransactionOnSession(WithLock);
-
- // Clean up the transaction resources unstashed on operation context.
- void _cleanUpTxnResourceOnOpCtx(WithLock wl,
- OperationContext* opCtx,
- TerminationCause terminationCause);
-
- // Checks if the current transaction number of this transaction still matches with the
- // parent session as well as the transaction number of the current operation context.
- void _checkIsActiveTransaction(WithLock,
- const TxnNumber& requestTxnNumber,
- bool checkAbort) const;
-
- // Checks if the command can be run on this transaction based on the state of the transaction.
- void _checkIsCommandValidWithTxnState(WithLock,
- const TxnNumber& requestTxnNumber,
- const std::string& cmdName);
-
- // Logs the transaction information if it has run slower than the global parameter slowMS. The
- // transaction must be committed or aborted when this function is called.
- void _logSlowTransaction(WithLock wl,
- const SingleThreadedLockStats* lockStats,
- TerminationCause terminationCause,
- repl::ReadConcernArgs readConcernArgs);
-
- // This method returns a string with information about a slow transaction. The format of the
- // logging string produced should match the format used for slow operation logging. A
- // transaction must be completed (committed or aborted) and a valid LockStats reference must be
- // passed in order for this method to be called.
- std::string _transactionInfoForLog(const SingleThreadedLockStats* lockStats,
- TerminationCause terminationCause,
- repl::ReadConcernArgs readConcernArgs) const;
-
- // Reports transaction stats for both active and inactive transactions using the provided
- // builder. The lock may be either a lock on _mutex or a lock on _metricsMutex.
- void _reportTransactionStats(WithLock wl,
- BSONObjBuilder* builder,
- repl::ReadConcernArgs readConcernArgs) const;
-
- // Bumps up the transaction number of this transaction and perform the necessary cleanup.
- void _setNewTxnNumber(WithLock wl, const TxnNumber& txnNumber);
-
- // Attempt to begin or retry a retryable write at the given transaction number.
- void _beginOrContinueRetryableWrite(WithLock wl, TxnNumber txnNumber);
-
- // Attempt to begin a new multi document transaction at the given transaction number.
- void _beginMultiDocumentTransaction(WithLock wl, TxnNumber txnNumber);
-
- // Attempt to continue an in-progress multi document transaction at the given transaction
- // number.
- void _continueMultiDocumentTransaction(WithLock wl, TxnNumber txnNumber);
-
- // Helper that invalidates the session state and activeTxnNumber. Also resets the single
- // transaction stats because the session is no longer valid.
- void _invalidate(WithLock);
-
- // Helper that resets the retryable writes state.
- void _resetRetryableWriteState(WithLock);
-
- // Helper that resets the transactional state. This is used when aborting a transaction,
- // invalidating a transaction, or starting a new transaction.
- void _resetTransactionState(WithLock wl, TransactionState::StateFlag state);
-
- // Protects the member variables below.
- mutable stdx::mutex _mutex;
-
- // Holds transaction resources between network operations.
- boost::optional<TxnResources> _txnResourceStash;
-
- // Maintains the transaction state and the transition table for legal state transitions.
- TransactionState _txnState;
-
- // Holds oplog data for operations which have been applied in the current multi-document
- // transaction.
- std::vector<repl::ReplOperation> _transactionOperations;
-
- // Total size in bytes of all operations within the _transactionOperations vector.
- size_t _transactionOperationBytes = 0;
-
- // Tracks the last seen txn number for the session and is always >= to the transaction number in
- // the last written txn record. When it is > than that in the last written txn record, this
- // means a new transaction has begun on the session, but it hasn't yet performed any writes.
- TxnNumber _activeTxnNumber{kUninitializedTxnNumber};
-
- // Caches what is known to be the last optime written for the active transaction.
- repl::OpTime _lastWriteOpTime;
-
- // Set when a snapshot read / transaction begins. Alleviates cache pressure by limiting how long
- // a snapshot will remain open and available. Checked in combination with _txnState to determine
- // whether the transaction should be aborted.
- // This is unset until a transaction begins on the session, and then reset only when new
- // transactions begin.
- boost::optional<Date_t> _transactionExpireDate;
-
- // The autoCommit setting of this transaction. Should always be false for multi-statement
- // transaction. Currently only needed for diagnostics reporting.
- boost::optional<bool> _autoCommit;
-
- // Track the prepareOpTime, the OpTime of the 'prepare' oplog entry for a transaction.
- repl::OpTime _prepareOpTime;
-
- // The OpTime a speculative transaction is reading from and also the earliest opTime it
- // should wait for write concern for on commit.
- repl::OpTime _speculativeTransactionReadOpTime;
-
- // Contains uncommitted multi-key path info entries which were modified under this transaction
- // so they can be applied to subsequent opreations before the transaction commits
- std::vector<MultikeyPathInfo> _multikeyPathInfo;
-
- // Tracks the OpTime of the first oplog entry written by this TransactionParticipant.
- boost::optional<repl::OpTime> _oldestOplogEntryOpTime;
-
- // Tracks the OpTime of the abort/commit oplog entry associated with this transaction.
- boost::optional<repl::OpTime> _finishOpTime;
-
- // Protects _transactionMetricsObserver. The concurrency rules are that const methods on
- // _transactionMetricsObserver may be called under either _mutex or _metricsMutex, but for
- // non-const methods, both mutexes must be held, with _mutex being taken before _metricsMutex.
- // No other locks, particularly including the Client lock, may be taken while holding
- // _metricsMutex.
- mutable stdx::mutex _metricsMutex;
-
- // Tracks and updates transaction metrics upon the appropriate transaction event.
- TransactionMetricsObserver _transactionMetricsObserver;
+ // Tracks the OpTime of the first oplog entry written by this TransactionParticipant.
+ boost::optional<repl::OpTime> oldestOplogEntryOpTime;
- // Only set if the server is shutting down and it has been ensured that no new requests will be
- // accepted. Ensures that any transaction resources will not be stashed from the operation
- // context onto the transaction participant when the session is checked-in so that locks can
- // automatically get freed.
- bool _inShutdown{false};
+ // Tracks the OpTime of the abort/commit oplog entry associated with this transaction.
+ boost::optional<repl::OpTime> finishOpTime;
- //
- // Retryable writes state
- //
+ //
+ // Retryable writes state
+ //
- // Specifies whether the session information needs to be refreshed from storage
- bool _isValid{false};
+ // Specifies whether the session information needs to be refreshed from storage
+ bool isValid{false};
- // Set to true if incomplete history is detected. For example, when the oplog to a write was
- // truncated because it was too old.
- bool _hasIncompleteHistory{false};
+ // Set to true if incomplete history is detected. For example, when the oplog to a write was
+ // truncated because it was too old.
+ bool hasIncompleteHistory{false};
- // For the active txn, tracks which statement ids have been committed and at which oplog
- // opTime. Used for fast retryability check and retrieving the previous write's data without
- // having to scan through the oplog.
- CommittedStatementTimestampMap _activeTxnCommittedStatements;
+ // For the active txn, tracks which statement ids have been committed and at which oplog
+ // opTime. Used for fast retryability check and retrieving the previous write's data without
+ // having to scan through the oplog.
+ CommittedStatementTimestampMap activeTxnCommittedStatements;
+ } _p;
};
} // namespace mongo
diff --git a/src/mongo/db/transaction_participant_retryable_writes_test.cpp b/src/mongo/db/transaction_participant_retryable_writes_test.cpp
index 92ea9a26393..5271df46282 100644
--- a/src/mongo/db/transaction_participant_retryable_writes_test.cpp
+++ b/src/mongo/db/transaction_participant_retryable_writes_test.cpp
@@ -206,8 +206,8 @@ protected:
repl::OpTime prevOpTime,
boost::optional<DurableTxnStateEnum> txnState) {
const auto session = OperationContextSession::get(opCtx());
- const auto txnParticipant = TransactionParticipant::get(session);
- txnParticipant->beginOrContinue(txnNum, boost::none, boost::none);
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.beginOrContinue(opCtx(), txnNum, boost::none, boost::none);
const auto uuid = UUID::gen();
@@ -215,7 +215,7 @@ protected:
WriteUnitOfWork wuow(opCtx());
const auto opTime =
logOp(opCtx(), kNss, uuid, session->getSessionId(), txnNum, stmtId, prevOpTime);
- txnParticipant->onWriteOpCompletedOnPrimary(
+ txnParticipant.onWriteOpCompletedOnPrimary(
opCtx(), txnNum, {stmtId}, opTime, Date_t::now(), txnState);
wuow.commit();
@@ -245,12 +245,12 @@ protected:
ASSERT_EQ(txnState != boost::none,
txnRecordObj.hasField(SessionTxnRecord::kStateFieldName));
- const auto txnParticipant = TransactionParticipant::get(session);
- ASSERT_EQ(opTime, txnParticipant->getLastWriteOpTime());
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ ASSERT_EQ(opTime, txnParticipant.getLastWriteOpTime());
- txnParticipant->invalidate();
- txnParticipant->refreshFromStorageIfNeeded();
- ASSERT_EQ(opTime, txnParticipant->getLastWriteOpTime());
+ txnParticipant.invalidate(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
+ ASSERT_EQ(opTime, txnParticipant.getLastWriteOpTime());
}
private:
@@ -259,12 +259,12 @@ private:
TEST_F(TransactionParticipantRetryableWritesTest, SessionEntryNotWrittenOnBegin) {
const auto& sessionId = *opCtx()->getLogicalSessionId();
- const auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->refreshFromStorageIfNeeded();
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
const TxnNumber txnNum = 20;
- txnParticipant->beginOrContinue(txnNum, boost::none, boost::none);
- ASSERT(txnParticipant->getLastWriteOpTime().isNull());
+ txnParticipant.beginOrContinue(opCtx(), txnNum, boost::none, boost::none);
+ ASSERT(txnParticipant.getLastWriteOpTime().isNull());
DBDirectClient client(opCtx());
auto cursor = client.query(NamespaceString::kSessionTransactionsTableNamespace,
@@ -274,12 +274,12 @@ TEST_F(TransactionParticipantRetryableWritesTest, SessionEntryNotWrittenOnBegin)
}
TEST_F(TransactionParticipantRetryableWritesTest, SessionEntryWrittenAtFirstWrite) {
- const auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->refreshFromStorageIfNeeded();
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
const auto& sessionId = *opCtx()->getLogicalSessionId();
const TxnNumber txnNum = 21;
- txnParticipant->beginOrContinue(txnNum, boost::none, boost::none);
+ txnParticipant.beginOrContinue(opCtx(), txnNum, boost::none, boost::none);
const auto opTime = writeTxnRecord(txnNum, 0, {}, boost::none);
@@ -296,13 +296,13 @@ TEST_F(TransactionParticipantRetryableWritesTest, SessionEntryWrittenAtFirstWrit
ASSERT_EQ(txnNum, txnRecord.getTxnNum());
ASSERT_EQ(opTime, txnRecord.getLastWriteOpTime());
ASSERT(!txnRecord.getState());
- ASSERT_EQ(opTime, txnParticipant->getLastWriteOpTime());
+ ASSERT_EQ(opTime, txnParticipant.getLastWriteOpTime());
}
TEST_F(TransactionParticipantRetryableWritesTest,
StartingNewerTransactionUpdatesThePersistedSession) {
- const auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->refreshFromStorageIfNeeded();
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
const auto& sessionId = *opCtx()->getLogicalSessionId();
@@ -322,16 +322,16 @@ TEST_F(TransactionParticipantRetryableWritesTest,
ASSERT_EQ(200, txnRecord.getTxnNum());
ASSERT_EQ(secondOpTime, txnRecord.getLastWriteOpTime());
ASSERT(!txnRecord.getState());
- ASSERT_EQ(secondOpTime, txnParticipant->getLastWriteOpTime());
+ ASSERT_EQ(secondOpTime, txnParticipant.getLastWriteOpTime());
- txnParticipant->invalidate();
- txnParticipant->refreshFromStorageIfNeeded();
- ASSERT_EQ(secondOpTime, txnParticipant->getLastWriteOpTime());
+ txnParticipant.invalidate(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
+ ASSERT_EQ(secondOpTime, txnParticipant.getLastWriteOpTime());
}
TEST_F(TransactionParticipantRetryableWritesTest, TransactionTableUpdatesReplaceEntireDocument) {
- const auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->refreshFromStorageIfNeeded();
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
const auto firstOpTime = writeTxnRecord(100, 0, {}, boost::none);
assertTxnRecord(100, 0, firstOpTime, boost::none);
@@ -344,21 +344,22 @@ TEST_F(TransactionParticipantRetryableWritesTest, TransactionTableUpdatesReplace
}
TEST_F(TransactionParticipantRetryableWritesTest, StartingOldTxnShouldAssert) {
- const auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->refreshFromStorageIfNeeded();
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
const TxnNumber txnNum = 20;
- txnParticipant->beginOrContinue(txnNum, boost::none, boost::none);
+ txnParticipant.beginOrContinue(opCtx(), txnNum, boost::none, boost::none);
- ASSERT_THROWS_CODE(txnParticipant->beginOrContinue(txnNum - 1, boost::none, boost::none),
- AssertionException,
- ErrorCodes::TransactionTooOld);
- ASSERT(txnParticipant->getLastWriteOpTime().isNull());
+ ASSERT_THROWS_CODE(
+ txnParticipant.beginOrContinue(opCtx(), txnNum - 1, boost::none, boost::none),
+ AssertionException,
+ ErrorCodes::TransactionTooOld);
+ ASSERT(txnParticipant.getLastWriteOpTime().isNull());
}
TEST_F(TransactionParticipantRetryableWritesTest, SessionTransactionsCollectionNotDefaultCreated) {
- const auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->refreshFromStorageIfNeeded();
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
const auto& sessionId = *opCtx()->getLogicalSessionId();
@@ -369,65 +370,65 @@ TEST_F(TransactionParticipantRetryableWritesTest, SessionTransactionsCollectionN
ASSERT(client.runCommand(nss.db().toString(), BSON("drop" << nss.coll()), dropResult));
const TxnNumber txnNum = 21;
- txnParticipant->beginOrContinue(txnNum, boost::none, boost::none);
+ txnParticipant.beginOrContinue(opCtx(), txnNum, boost::none, boost::none);
AutoGetCollection autoColl(opCtx(), kNss, MODE_IX);
WriteUnitOfWork wuow(opCtx());
const auto uuid = UUID::gen();
const auto opTime = logOp(opCtx(), kNss, uuid, sessionId, txnNum, 0);
- ASSERT_THROWS(txnParticipant->onWriteOpCompletedOnPrimary(
+ ASSERT_THROWS(txnParticipant.onWriteOpCompletedOnPrimary(
opCtx(), txnNum, {0}, opTime, Date_t::now(), boost::none),
AssertionException);
}
TEST_F(TransactionParticipantRetryableWritesTest, CheckStatementExecuted) {
- const auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->refreshFromStorageIfNeeded();
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
const TxnNumber txnNum = 100;
- txnParticipant->beginOrContinue(txnNum, boost::none, boost::none);
+ txnParticipant.beginOrContinue(opCtx(), txnNum, boost::none, boost::none);
- ASSERT(!txnParticipant->checkStatementExecuted(1000));
- ASSERT(!txnParticipant->checkStatementExecutedNoOplogEntryFetch(1000));
+ ASSERT(!txnParticipant.checkStatementExecuted(opCtx(), 1000));
+ ASSERT(!txnParticipant.checkStatementExecutedNoOplogEntryFetch(1000));
const auto firstOpTime = writeTxnRecord(txnNum, 1000, {}, boost::none);
- ASSERT(txnParticipant->checkStatementExecuted(1000));
- ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(1000));
+ ASSERT(txnParticipant.checkStatementExecuted(opCtx(), 1000));
+ ASSERT(txnParticipant.checkStatementExecutedNoOplogEntryFetch(1000));
- ASSERT(!txnParticipant->checkStatementExecuted(2000));
- ASSERT(!txnParticipant->checkStatementExecutedNoOplogEntryFetch(2000));
+ ASSERT(!txnParticipant.checkStatementExecuted(opCtx(), 2000));
+ ASSERT(!txnParticipant.checkStatementExecutedNoOplogEntryFetch(2000));
writeTxnRecord(txnNum, 2000, firstOpTime, boost::none);
- ASSERT(txnParticipant->checkStatementExecuted(2000));
- ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(2000));
+ ASSERT(txnParticipant.checkStatementExecuted(opCtx(), 2000));
+ ASSERT(txnParticipant.checkStatementExecutedNoOplogEntryFetch(2000));
// Invalidate the session and ensure the statements still check out
- txnParticipant->invalidate();
- txnParticipant->refreshFromStorageIfNeeded();
+ txnParticipant.invalidate(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
- ASSERT(txnParticipant->checkStatementExecuted(1000));
- ASSERT(txnParticipant->checkStatementExecuted(2000));
+ ASSERT(txnParticipant.checkStatementExecuted(opCtx(), 1000));
+ ASSERT(txnParticipant.checkStatementExecuted(opCtx(), 2000));
- ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(1000));
- ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(2000));
+ ASSERT(txnParticipant.checkStatementExecutedNoOplogEntryFetch(1000));
+ ASSERT(txnParticipant.checkStatementExecutedNoOplogEntryFetch(2000));
}
DEATH_TEST_F(TransactionParticipantRetryableWritesTest,
CheckStatementExecutedForInvalidatedTransactionInvariants,
- "Invariant failure _isValid") {
- const auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->invalidate();
- txnParticipant->checkStatementExecuted(0);
+ "Invariant failure p().isValid") {
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.invalidate(opCtx());
+ txnParticipant.checkStatementExecuted(opCtx(), 0);
}
DEATH_TEST_F(TransactionParticipantRetryableWritesTest,
WriteOpCompletedOnPrimaryForOldTransactionInvariants,
- "Invariant failure txnNumber == _activeTxnNumber") {
- const auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->refreshFromStorageIfNeeded();
+ "Invariant failure txnNumber == o().activeTxnNumber") {
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
const auto& sessionId = *opCtx()->getLogicalSessionId();
const TxnNumber txnNum = 100;
- txnParticipant->beginOrContinue(txnNum, boost::none, boost::none);
+ txnParticipant.beginOrContinue(opCtx(), txnNum, boost::none, boost::none);
const auto uuid = UUID::gen();
@@ -435,7 +436,7 @@ DEATH_TEST_F(TransactionParticipantRetryableWritesTest,
AutoGetCollection autoColl(opCtx(), kNss, MODE_IX);
WriteUnitOfWork wuow(opCtx());
const auto opTime = logOp(opCtx(), kNss, uuid, sessionId, txnNum, 0);
- txnParticipant->onWriteOpCompletedOnPrimary(
+ txnParticipant.onWriteOpCompletedOnPrimary(
opCtx(), txnNum, {0}, opTime, Date_t::now(), boost::none);
wuow.commit();
}
@@ -444,27 +445,27 @@ DEATH_TEST_F(TransactionParticipantRetryableWritesTest,
AutoGetCollection autoColl(opCtx(), kNss, MODE_IX);
WriteUnitOfWork wuow(opCtx());
const auto opTime = logOp(opCtx(), kNss, uuid, sessionId, txnNum - 1, 0);
- txnParticipant->onWriteOpCompletedOnPrimary(
+ txnParticipant.onWriteOpCompletedOnPrimary(
opCtx(), txnNum - 1, {0}, opTime, Date_t::now(), boost::none);
}
}
DEATH_TEST_F(TransactionParticipantRetryableWritesTest,
WriteOpCompletedOnPrimaryForInvalidatedTransactionInvariants,
- "Invariant failure txnNumber == _activeTxnNumber") {
- const auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->refreshFromStorageIfNeeded();
+ "Invariant failure txnNumber == o().activeTxnNumber") {
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
const TxnNumber txnNum = 100;
- txnParticipant->beginOrContinue(txnNum, boost::none, boost::none);
+ txnParticipant.beginOrContinue(opCtx(), txnNum, boost::none, boost::none);
AutoGetCollection autoColl(opCtx(), kNss, MODE_IX);
WriteUnitOfWork wuow(opCtx());
const auto uuid = UUID::gen();
const auto opTime = logOp(opCtx(), kNss, uuid, *opCtx()->getLogicalSessionId(), txnNum, 0);
- txnParticipant->invalidate();
- txnParticipant->onWriteOpCompletedOnPrimary(
+ txnParticipant.invalidate(opCtx());
+ txnParticipant.onWriteOpCompletedOnPrimary(
opCtx(), txnNum, {0}, opTime, Date_t::now(), boost::none);
}
@@ -520,20 +521,20 @@ TEST_F(TransactionParticipantRetryableWritesTest, IncompleteHistoryDueToOpLogTru
}());
}
- const auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->refreshFromStorageIfNeeded();
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
- ASSERT_THROWS_CODE(txnParticipant->checkStatementExecuted(0),
+ ASSERT_THROWS_CODE(txnParticipant.checkStatementExecuted(opCtx(), 0),
AssertionException,
ErrorCodes::IncompleteTransactionHistory);
- ASSERT(txnParticipant->checkStatementExecuted(1));
- ASSERT(txnParticipant->checkStatementExecuted(2));
+ ASSERT(txnParticipant.checkStatementExecuted(opCtx(), 1));
+ ASSERT(txnParticipant.checkStatementExecuted(opCtx(), 2));
- ASSERT_THROWS_CODE(txnParticipant->checkStatementExecutedNoOplogEntryFetch(0),
+ ASSERT_THROWS_CODE(txnParticipant.checkStatementExecutedNoOplogEntryFetch(0),
AssertionException,
ErrorCodes::IncompleteTransactionHistory);
- ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(1));
- ASSERT(txnParticipant->checkStatementExecutedNoOplogEntryFetch(2));
+ ASSERT(txnParticipant.checkStatementExecutedNoOplogEntryFetch(1));
+ ASSERT(txnParticipant.checkStatementExecutedNoOplogEntryFetch(2));
}
TEST_F(TransactionParticipantRetryableWritesTest, ErrorOnlyWhenStmtIdBeingCheckedIsNotInCache) {
@@ -541,9 +542,9 @@ TEST_F(TransactionParticipantRetryableWritesTest, ErrorOnlyWhenStmtIdBeingChecke
const auto sessionId = *opCtx()->getLogicalSessionId();
const TxnNumber txnNum = 2;
- const auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->refreshFromStorageIfNeeded();
- txnParticipant->beginOrContinue(txnNum, boost::none, boost::none);
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
+ txnParticipant.beginOrContinue(opCtx(), txnNum, boost::none, boost::none);
OperationSessionInfo osi;
osi.setSessionId(sessionId);
@@ -568,7 +569,7 @@ TEST_F(TransactionParticipantRetryableWritesTest, ErrorOnlyWhenStmtIdBeingChecke
{},
false /* prepare */,
OplogSlot());
- txnParticipant->onWriteOpCompletedOnPrimary(
+ txnParticipant.onWriteOpCompletedOnPrimary(
opCtx(), txnNum, {1}, opTime, wallClockTime, boost::none);
wuow.commit();
@@ -598,30 +599,30 @@ TEST_F(TransactionParticipantRetryableWritesTest, ErrorOnlyWhenStmtIdBeingChecke
false /* prepare */,
OplogSlot());
- txnParticipant->onWriteOpCompletedOnPrimary(
+ txnParticipant.onWriteOpCompletedOnPrimary(
opCtx(), txnNum, {kIncompleteHistoryStmtId}, opTime, wallClockTime, boost::none);
wuow.commit();
}
{
- auto oplog = txnParticipant->checkStatementExecuted(1);
+ auto oplog = txnParticipant.checkStatementExecuted(opCtx(), 1);
ASSERT_TRUE(oplog);
ASSERT_EQ(firstOpTime, oplog->getOpTime());
}
- ASSERT_THROWS(txnParticipant->checkStatementExecuted(2), AssertionException);
+ ASSERT_THROWS(txnParticipant.checkStatementExecuted(opCtx(), 2), AssertionException);
// Should have the same behavior after loading state from storage.
- txnParticipant->invalidate();
- txnParticipant->refreshFromStorageIfNeeded();
+ txnParticipant.invalidate(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
{
- auto oplog = txnParticipant->checkStatementExecuted(1);
+ auto oplog = txnParticipant.checkStatementExecuted(opCtx(), 1);
ASSERT_TRUE(oplog);
ASSERT_EQ(firstOpTime, oplog->getOpTime());
}
- ASSERT_THROWS(txnParticipant->checkStatementExecuted(2), AssertionException);
+ ASSERT_THROWS(txnParticipant.checkStatementExecuted(opCtx(), 2), AssertionException);
}
} // namespace
diff --git a/src/mongo/db/transaction_participant_test.cpp b/src/mongo/db/transaction_participant_test.cpp
index d13a172d746..75e6ddafbad 100644
--- a/src/mongo/db/transaction_participant_test.cpp
+++ b/src/mongo/db/transaction_participant_test.cpp
@@ -234,24 +234,10 @@ protected:
}
void runFunctionFromDifferentOpCtx(std::function<void(OperationContext*)> func) {
- // Stash the original client.
- auto originalClient = Client::releaseCurrent();
-
// Create a new client (e.g. for migration) and opCtx.
- auto service = opCtx()->getServiceContext();
- auto newClientOwned = service->makeClient("newClient");
- auto newClient = newClientOwned.get();
- Client::setCurrent(std::move(newClientOwned));
- auto newOpCtx = newClient->makeOperationContext();
-
- ON_BLOCK_EXIT([&] {
- // Restore the original client.
- newOpCtx.reset();
- Client::releaseCurrent();
- Client::setCurrent(std::move(originalClient));
- });
-
- // Run the function on bahalf of another operation context.
+ auto newClientOwned = getServiceContext()->makeClient("newClient");
+ AlternativeClientRegion acr(newClientOwned);
+ auto newOpCtx = cc().makeOperationContext();
func(newOpCtx.get());
}
@@ -260,7 +246,7 @@ protected:
opCtx()->lockState()->setShouldConflictWithSecondaryBatchApplication(false);
auto opCtxSession = std::make_unique<MongoDOperationContextSession>(opCtx());
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), false, startNewTxn);
+ txnParticipant.beginOrContinue(opCtx(), *opCtx()->getTxnNumber(), false, startNewTxn);
return opCtxSession;
}
@@ -276,10 +262,10 @@ TEST_F(TxnParticipantTest, TransactionThrowsLockTimeoutIfLockIsUnavailable) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
{ Lock::DBLock dbXLock(opCtx(), dbName, MODE_X); }
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
auto clientWithDatabaseXLock = Client::releaseCurrent();
@@ -305,8 +291,8 @@ TEST_F(TxnParticipantTest, TransactionThrowsLockTimeoutIfLockIsUnavailable) {
MongoDOperationContextSession newOpCtxSession(newOpCtx.get());
auto newTxnParticipant = TransactionParticipant::get(newOpCtx.get());
- newTxnParticipant->beginOrContinue(newTxnNum, false, true);
- newTxnParticipant->unstashTransactionResources(newOpCtx.get(), "insert");
+ newTxnParticipant.beginOrContinue(newOpCtx.get(), newTxnNum, false, true);
+ newTxnParticipant.unstashTransactionResources(newOpCtx.get(), "insert");
Date_t t1 = Date_t::now();
ASSERT_THROWS_CODE(Lock::DBLock(newOpCtx.get(), dbName, MODE_X),
@@ -342,14 +328,14 @@ TEST_F(TxnParticipantTest, StashAndUnstashResources) {
// Perform initial unstash which sets up a WriteUnitOfWork.
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "find");
+ txnParticipant.unstashTransactionResources(opCtx(), "find");
ASSERT_EQUALS(originalLocker, opCtx()->lockState());
ASSERT_EQUALS(originalRecoveryUnit, opCtx()->recoveryUnit());
ASSERT(opCtx()->getWriteUnitOfWork());
ASSERT(opCtx()->lockState()->isLocked());
// Stash resources. The original Locker and RecoveryUnit now belong to the stash.
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
ASSERT_NOT_EQUALS(originalLocker, opCtx()->lockState());
ASSERT_NOT_EQUALS(originalRecoveryUnit, opCtx()->recoveryUnit());
ASSERT(!opCtx()->getWriteUnitOfWork());
@@ -359,25 +345,26 @@ TEST_F(TxnParticipantTest, StashAndUnstashResources) {
// Unstash the stashed resources. This restores the original Locker and RecoveryUnit to the
// OperationContext.
- txnParticipant->unstashTransactionResources(opCtx(), "find");
+ txnParticipant.unstashTransactionResources(opCtx(), "find");
ASSERT_EQUALS(originalLocker, opCtx()->lockState());
ASSERT_EQUALS(originalRecoveryUnit, opCtx()->recoveryUnit());
ASSERT(opCtx()->getWriteUnitOfWork());
// Commit the transaction. This allows us to release locks.
- txnParticipant->commitUnpreparedTransaction(opCtx());
+ txnParticipant.commitUnpreparedTransaction(opCtx());
}
TEST_F(TxnParticipantTest, CannotSpecifyStartTransactionOnInProgressTxn) {
// Must specify startTransaction=true and autocommit=false to start a transaction.
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- ASSERT_TRUE(txnParticipant->inMultiDocumentTransaction());
+ ASSERT_TRUE(txnParticipant.inMultiDocumentTransaction());
// Cannot try to start a transaction that already started.
- ASSERT_THROWS_CODE(txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), false, true),
- AssertionException,
- ErrorCodes::ConflictingOperationInProgress);
+ ASSERT_THROWS_CODE(
+ txnParticipant.beginOrContinue(opCtx(), *opCtx()->getTxnNumber(), false, true),
+ AssertionException,
+ ErrorCodes::ConflictingOperationInProgress);
}
TEST_F(TxnParticipantTest, AutocommitRequiredOnEveryTxnOp) {
@@ -385,19 +372,19 @@ TEST_F(TxnParticipantTest, AutocommitRequiredOnEveryTxnOp) {
auto txnParticipant = TransactionParticipant::get(opCtx());
// We must have stashed transaction resources to do a second operation on the transaction.
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
auto txnNum = *opCtx()->getTxnNumber();
// Omitting 'autocommit' after the first statement of a transaction should throw an error.
- ASSERT_THROWS_CODE(txnParticipant->beginOrContinue(txnNum, boost::none, boost::none),
+ ASSERT_THROWS_CODE(txnParticipant.beginOrContinue(opCtx(), txnNum, boost::none, boost::none),
AssertionException,
ErrorCodes::InvalidOptions);
// Including autocommit=false should succeed.
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), false, boost::none);
+ txnParticipant.beginOrContinue(opCtx(), *opCtx()->getTxnNumber(), false, boost::none);
}
DEATH_TEST_F(TxnParticipantTest, AutocommitCannotBeTrue, "invariant") {
@@ -405,7 +392,7 @@ DEATH_TEST_F(TxnParticipantTest, AutocommitCannotBeTrue, "invariant") {
auto txnParticipant = TransactionParticipant::get(opCtx());
// Passing 'autocommit=true' is not allowed and should crash.
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), true, boost::none);
+ txnParticipant.beginOrContinue(opCtx(), *opCtx()->getTxnNumber(), true, boost::none);
}
DEATH_TEST_F(TxnParticipantTest, StartTransactionCannotBeFalse, "invariant") {
@@ -413,7 +400,7 @@ DEATH_TEST_F(TxnParticipantTest, StartTransactionCannotBeFalse, "invariant") {
auto txnParticipant = TransactionParticipant::get(opCtx());
// Passing 'startTransaction=false' is not allowed and should crash.
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), false, false);
+ txnParticipant.beginOrContinue(opCtx(), *opCtx()->getTxnNumber(), false, false);
}
TEST_F(TxnParticipantTest, SameTransactionPreservesStoredStatements) {
@@ -421,40 +408,40 @@ TEST_F(TxnParticipantTest, SameTransactionPreservesStoredStatements) {
auto txnParticipant = TransactionParticipant::get(opCtx());
// We must have stashed transaction resources to re-open the transaction.
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
auto operation = repl::OplogEntry::makeInsertOperation(kNss, kUUID, BSON("TestValue" << 0));
- txnParticipant->addTransactionOperation(opCtx(), operation);
+ txnParticipant.addTransactionOperation(opCtx(), operation);
ASSERT_BSONOBJ_EQ(operation.toBSON(),
- txnParticipant->getTransactionOperationsForTest()[0].toBSON());
+ txnParticipant.getTransactionOperationsForTest()[0].toBSON());
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
// Check the transaction operations before re-opening the transaction.
ASSERT_BSONOBJ_EQ(operation.toBSON(),
- txnParticipant->getTransactionOperationsForTest()[0].toBSON());
+ txnParticipant.getTransactionOperationsForTest()[0].toBSON());
// Re-opening the same transaction should have no effect.
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), false, boost::none);
+ txnParticipant.beginOrContinue(opCtx(), *opCtx()->getTxnNumber(), false, boost::none);
ASSERT_BSONOBJ_EQ(operation.toBSON(),
- txnParticipant->getTransactionOperationsForTest()[0].toBSON());
+ txnParticipant.getTransactionOperationsForTest()[0].toBSON());
}
TEST_F(TxnParticipantTest, AbortClearsStoredStatements) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
auto operation = repl::OplogEntry::makeInsertOperation(kNss, kUUID, BSON("TestValue" << 0));
- txnParticipant->addTransactionOperation(opCtx(), operation);
+ txnParticipant.addTransactionOperation(opCtx(), operation);
ASSERT_BSONOBJ_EQ(operation.toBSON(),
- txnParticipant->getTransactionOperationsForTest()[0].toBSON());
+ txnParticipant.getTransactionOperationsForTest()[0].toBSON());
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->stashTransactionResources(opCtx());
- txnParticipant->abortArbitraryTransaction();
- ASSERT_TRUE(txnParticipant->getTransactionOperationsForTest().empty());
- ASSERT_TRUE(txnParticipant->transactionIsAborted());
+ txnParticipant.stashTransactionResources(opCtx());
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
+ ASSERT_TRUE(txnParticipant.getTransactionOperationsForTest().empty());
+ ASSERT_TRUE(txnParticipant.transactionIsAborted());
}
// This test makes sure the commit machinery works even when no operations are done on the
@@ -462,14 +449,14 @@ TEST_F(TxnParticipantTest, AbortClearsStoredStatements) {
TEST_F(TxnParticipantTest, EmptyTransactionCommit) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->commitUnpreparedTransaction(opCtx());
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.commitUnpreparedTransaction(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
- ASSERT_TRUE(txnParticipant->transactionIsCommitted());
+ ASSERT_TRUE(txnParticipant.transactionIsCommitted());
}
// This test makes sure the commit machinery works even when no operations are done on the
@@ -477,45 +464,45 @@ TEST_F(TxnParticipantTest, EmptyTransactionCommit) {
TEST_F(TxnParticipantTest, EmptyPreparedTransactionCommit) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
const auto commitTS = Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
- txnParticipant->commitPreparedTransaction(opCtx(), commitTS, {});
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTS, {});
+ txnParticipant.stashTransactionResources(opCtx());
- ASSERT_TRUE(txnParticipant->transactionIsCommitted());
+ ASSERT_TRUE(txnParticipant.transactionIsCommitted());
}
TEST_F(TxnParticipantTest, PrepareSucceedsWithNestedLocks) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
{
Lock::GlobalLock lk1(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
Lock::GlobalLock lk2(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
}
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
const auto commitTS = Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
- txnParticipant->commitPreparedTransaction(opCtx(), commitTS, {});
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTS, {});
+ txnParticipant.stashTransactionResources(opCtx());
- ASSERT_TRUE(txnParticipant->transactionIsCommitted());
+ ASSERT_TRUE(txnParticipant.transactionIsCommitted());
}
TEST_F(TxnParticipantTest, CommitTransactionSetsCommitTimestampOnPreparedTransaction) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
// The transaction machinery cannot store an empty locker.
Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
const auto commitTS = Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
auto originalFn = _opObserver->onTransactionCommitFn;
@@ -531,13 +518,13 @@ TEST_F(TxnParticipantTest, CommitTransactionSetsCommitTimestampOnPreparedTransac
ASSERT(statements.empty());
};
- txnParticipant->commitPreparedTransaction(opCtx(), commitTS, {});
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTS, {});
// The recovery unit is reset on commit.
ASSERT(opCtx()->recoveryUnit()->getCommitTimestamp().isNull());
- txnParticipant->stashTransactionResources(opCtx());
- ASSERT_TRUE(txnParticipant->transactionIsCommitted());
+ txnParticipant.stashTransactionResources(opCtx());
+ ASSERT_TRUE(txnParticipant.transactionIsCommitted());
ASSERT(opCtx()->recoveryUnit()->getCommitTimestamp().isNull());
}
@@ -546,11 +533,11 @@ TEST_F(TxnParticipantTest, CommitTransactionWithCommitTimestampFailsOnUnprepared
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
// The transaction machinery cannot store an empty locker.
Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
- ASSERT_THROWS_CODE(txnParticipant->commitPreparedTransaction(opCtx(), commitTimestamp, {}),
+ ASSERT_THROWS_CODE(txnParticipant.commitPreparedTransaction(opCtx(), commitTimestamp, {}),
AssertionException,
ErrorCodes::InvalidOptions);
}
@@ -570,16 +557,16 @@ TEST_F(TxnParticipantTest, CommitTransactionDoesNotSetCommitTimestampOnUnprepare
};
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
// The transaction machinery cannot store an empty locker.
Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
- txnParticipant->commitUnpreparedTransaction(opCtx());
+ txnParticipant.commitUnpreparedTransaction(opCtx());
ASSERT(opCtx()->recoveryUnit()->getCommitTimestamp().isNull());
- txnParticipant->stashTransactionResources(opCtx());
- ASSERT_TRUE(txnParticipant->transactionIsCommitted());
+ txnParticipant.stashTransactionResources(opCtx());
+ ASSERT_TRUE(txnParticipant.transactionIsCommitted());
ASSERT(opCtx()->recoveryUnit()->getCommitTimestamp().isNull());
}
@@ -587,12 +574,12 @@ TEST_F(TxnParticipantTest, CommitTransactionWithoutCommitTimestampFailsOnPrepare
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
// The transaction machinery cannot store an empty locker.
Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
- txnParticipant->prepareTransaction(opCtx(), {});
- ASSERT_THROWS_CODE(txnParticipant->commitUnpreparedTransaction(opCtx()),
+ txnParticipant.prepareTransaction(opCtx(), {});
+ ASSERT_THROWS_CODE(txnParticipant.commitUnpreparedTransaction(opCtx()),
AssertionException,
ErrorCodes::InvalidOptions);
}
@@ -601,12 +588,12 @@ TEST_F(TxnParticipantTest, CommitTransactionWithNullCommitTimestampFailsOnPrepar
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
// The transaction machinery cannot store an empty locker.
Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
- txnParticipant->prepareTransaction(opCtx(), {});
- ASSERT_THROWS_CODE(txnParticipant->commitPreparedTransaction(opCtx(), Timestamp(), {}),
+ txnParticipant.prepareTransaction(opCtx(), {});
+ ASSERT_THROWS_CODE(txnParticipant.commitPreparedTransaction(opCtx(), Timestamp(), {}),
AssertionException,
ErrorCodes::InvalidOptions);
}
@@ -616,12 +603,12 @@ TEST_F(TxnParticipantTest,
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
// The transaction machinery cannot store an empty locker.
Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
- auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
- ASSERT_THROWS_CODE(txnParticipant->commitPreparedTransaction(
+ auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
+ ASSERT_THROWS_CODE(txnParticipant.commitPreparedTransaction(
opCtx(), Timestamp(prepareTimestamp.getSecs() - 1, 1), {}),
AssertionException,
ErrorCodes::InvalidOptions);
@@ -632,12 +619,12 @@ TEST_F(TxnParticipantTest,
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
// The transaction machinery cannot store an empty locker.
Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
- auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
- ASSERT_THROWS_CODE(txnParticipant->commitPreparedTransaction(opCtx(), prepareTimestamp, {}),
+ auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
+ ASSERT_THROWS_CODE(txnParticipant.commitPreparedTransaction(opCtx(), prepareTimestamp, {}),
AssertionException,
ErrorCodes::InvalidOptions);
}
@@ -647,13 +634,13 @@ TEST_F(TxnParticipantTest,
TEST_F(TxnParticipantTest, EmptyTransactionAbort) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->stashTransactionResources(opCtx());
- txnParticipant->abortArbitraryTransaction();
- ASSERT_TRUE(txnParticipant->transactionIsAborted());
+ txnParticipant.stashTransactionResources(opCtx());
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
+ ASSERT_TRUE(txnParticipant.transactionIsAborted());
}
// This test makes sure the abort machinery works even when no operations are done on the
@@ -661,174 +648,20 @@ TEST_F(TxnParticipantTest, EmptyTransactionAbort) {
TEST_F(TxnParticipantTest, EmptyPreparedTransactionAbort) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->prepareTransaction(opCtx(), {});
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT_TRUE(txnParticipant->transactionIsAborted());
-}
-
-TEST_F(TxnParticipantTest, ConcurrencyOfUnstashAndAbort) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
-
- // The transaction may be aborted without checking out the txnParticipant.
- txnParticipant->abortArbitraryTransaction();
-
- // An unstash after an abort should uassert.
- ASSERT_THROWS_CODE(txnParticipant->unstashTransactionResources(opCtx(), "find"),
- AssertionException,
- ErrorCodes::NoSuchTransaction);
-}
-
-TEST_F(TxnParticipantTest, ConcurrencyOfStashAndAbort) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "find");
-
- // The transaction may be aborted without checking out the txnParticipant->
- txnParticipant->abortArbitraryTransaction();
-
- // A stash after an abort should be a noop.
- txnParticipant->stashTransactionResources(opCtx());
-}
-
-TEST_F(TxnParticipantTest, ConcurrencyOfAddTransactionOperationAndAbort) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
-
- // The transaction may be aborted without checking out the txnParticipant.
- txnParticipant->abortArbitraryTransaction();
-
- // An addTransactionOperation() after an abort should uassert.
- auto operation = repl::OplogEntry::makeInsertOperation(kNss, kUUID, BSON("TestValue" << 0));
- ASSERT_THROWS_CODE(txnParticipant->addTransactionOperation(opCtx(), operation),
- AssertionException,
- ErrorCodes::NoSuchTransaction);
-}
-
-TEST_F(TxnParticipantTest, ConcurrencyOfRetrieveCompletedTransactionOperationsAndAbort) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
-
- // The transaction may be aborted without checking out the txnParticipant.
- txnParticipant->abortArbitraryTransaction();
-
- // A retrieveCompletedTransactionOperations() after an abort should uassert.
- ASSERT_THROWS_CODE(txnParticipant->retrieveCompletedTransactionOperations(opCtx()),
- AssertionException,
- ErrorCodes::NoSuchTransaction);
-}
-
-TEST_F(TxnParticipantTest, ConcurrencyOfClearOperationsInMemory) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
-
- // The transaction may be aborted without checking out the txnParticipant.
- txnParticipant->abortArbitraryTransaction();
-
- // An clearOperationsInMemory() after an abort should uassert.
- ASSERT_THROWS_CODE(txnParticipant->clearOperationsInMemory(opCtx()),
- AssertionException,
- ErrorCodes::NoSuchTransaction);
-}
-
-TEST_F(TxnParticipantTest, ConcurrencyOfCommitUnpreparedTransactionAndAbort) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
-
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
-
- // The transaction may be aborted without checking out the txnParticipant.
- txnParticipant->abortArbitraryTransaction();
-
- // A commitUnpreparedTransaction() after an abort should uassert.
- ASSERT_THROWS_CODE(txnParticipant->commitUnpreparedTransaction(opCtx()),
- AssertionException,
- ErrorCodes::NoSuchTransaction);
-}
-
-TEST_F(TxnParticipantTest, ConcurrencyOfCommitPreparedTransactionAndAbort) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
-
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
- auto prepareTS = txnParticipant->prepareTransaction(opCtx(), {});
- auto commitTS = Timestamp(prepareTS.getSecs(), prepareTS.getInc() + 1);
-
- txnParticipant->abortArbitraryTransaction();
-
- // A commitPreparedTransaction() after an abort should succeed since the abort should fail.
- txnParticipant->commitPreparedTransaction(opCtx(), commitTS, {});
-
- ASSERT(_opObserver->transactionCommitted);
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
- ASSERT(txnParticipant->transactionIsCommitted());
-}
-
-TEST_F(TxnParticipantTest, ConcurrencyOfActiveUnpreparedAbortAndArbitraryAbort) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
-
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
- ASSERT(txnParticipant->inMultiDocumentTransaction());
-
- // The transaction may be aborted without checking out the txnParticipant.
- txnParticipant->abortArbitraryTransaction();
-
- // The operation throws for some reason and aborts implicitly.
- // Abort active transaction after it's been aborted by KillSession is a no-op.
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT(txnParticipant->transactionIsAborted());
- ASSERT(opCtx()->getWriteUnitOfWork() == nullptr);
-}
-
-TEST_F(TxnParticipantTest, ConcurrencyOfActivePreparedAbortAndArbitraryAbort) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
-
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
- ASSERT(txnParticipant->inMultiDocumentTransaction());
- txnParticipant->prepareTransaction(opCtx(), {});
-
- // The transaction may be aborted without checking out the txnParticipant.
- txnParticipant->abortArbitraryTransaction();
-
- // The operation throws for some reason and aborts implicitly.
- // Abort active transaction after it's been aborted by KillSession is a no-op.
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT(txnParticipant->transactionIsAborted());
- ASSERT(opCtx()->getWriteUnitOfWork() == nullptr);
-}
-
-TEST_F(TxnParticipantTest, ConcurrencyOfPrepareTransactionAndAbort) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
-
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
-
- // The transaction may be aborted without checking out the txnParticipant.
- txnParticipant->abortArbitraryTransaction();
- ASSERT(txnParticipant->transactionIsAborted());
-
- // A prepareTransaction() after an abort should uassert.
- ASSERT_THROWS_CODE(txnParticipant->prepareTransaction(opCtx(), {}),
- AssertionException,
- ErrorCodes::NoSuchTransaction);
- ASSERT_FALSE(_opObserver->transactionPrepared);
- ASSERT(txnParticipant->transactionIsAborted());
+ txnParticipant.prepareTransaction(opCtx(), {});
+ txnParticipant.abortActiveTransaction(opCtx());
+ ASSERT_TRUE(txnParticipant.transactionIsAborted());
}
TEST_F(TxnParticipantTest, KillSessionsDuringPrepareDoesNotAbortTransaction) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
auto ruPrepareTimestamp = Timestamp();
auto originalFn = _opObserver->onTransactionPrepareFn;
@@ -839,93 +672,41 @@ TEST_F(TxnParticipantTest, KillSessionsDuringPrepareDoesNotAbortTransaction) {
ASSERT_FALSE(ruPrepareTimestamp.isNull());
// The transaction may be aborted without checking out the txnParticipant.
- txnParticipant->abortArbitraryTransaction();
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
+ ASSERT_FALSE(txnParticipant.transactionIsAborted());
};
// Check that prepareTimestamp gets set.
- auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
ASSERT_EQ(ruPrepareTimestamp, prepareTimestamp);
// Check that the oldest prepareTimestamp is the one we just set.
auto prepareOpTime = ServerTransactionsMetrics::get(opCtx())->getOldestActiveOpTime();
ASSERT_EQ(prepareOpTime->getTimestamp(), prepareTimestamp);
ASSERT(_opObserver->transactionPrepared);
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
-}
-
-DEATH_TEST_F(TxnParticipantTest, AbortDuringPrepareIsFatal, "Invariant") {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
-
- auto originalFn = _opObserver->onTransactionPrepareFn;
- _opObserver->onTransactionPrepareFn = [&]() {
- originalFn();
-
- // The transaction may be aborted without checking out the txnParticipant.
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT(txnParticipant->transactionIsAborted());
- };
-
- txnParticipant->prepareTransaction(opCtx(), {});
+ ASSERT_FALSE(txnParticipant.transactionIsAborted());
}
TEST_F(TxnParticipantTest, ThrowDuringOnTransactionPrepareAbortsTransaction) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
_opObserver->onTransactionPrepareThrowsException = true;
- ASSERT_THROWS_CODE(txnParticipant->prepareTransaction(opCtx(), {}),
+ ASSERT_THROWS_CODE(txnParticipant.prepareTransaction(opCtx(), {}),
AssertionException,
ErrorCodes::OperationFailed);
ASSERT_FALSE(_opObserver->transactionPrepared);
- ASSERT(txnParticipant->transactionIsAborted());
-}
-
-TEST_F(TxnParticipantTest, KillSessionsDuringPreparedCommitDoesNotAbortTransaction) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
-
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
- const auto commitTS = Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
-
- auto originalFn = _opObserver->onTransactionCommitFn;
- _opObserver->onTransactionCommitFn = [&](boost::optional<OplogSlot> commitOplogEntryOpTime,
- boost::optional<Timestamp> commitTimestamp,
- std::vector<repl::ReplOperation>& statements) {
- originalFn(commitOplogEntryOpTime, commitTimestamp, statements);
- ASSERT(commitOplogEntryOpTime);
- ASSERT(commitTimestamp);
-
- ASSERT_GT(*commitTimestamp, prepareTimestamp);
-
- ASSERT(statements.empty());
-
- // The transaction may be aborted without checking out the txnParticipant.
- txnParticipant->abortArbitraryTransaction();
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
- };
-
- txnParticipant->commitPreparedTransaction(opCtx(), commitTS, {});
-
- // The recovery unit is reset on commit.
- ASSERT(opCtx()->recoveryUnit()->getCommitTimestamp().isNull());
-
- ASSERT(_opObserver->transactionCommitted);
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
- ASSERT(txnParticipant->transactionIsCommitted());
+ ASSERT(txnParticipant.transactionIsAborted());
}
TEST_F(TxnParticipantTest, StepDownAfterPrepareDoesNotBlock) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.prepareTransaction(opCtx(), {});
// Test that we can acquire the RSTL in mode X, and then immediately release it so the test can
// complete successfully.
@@ -937,17 +718,17 @@ TEST_F(TxnParticipantTest, StepDownAfterPrepareDoesNotBlock) {
};
runFunctionFromDifferentOpCtx(func);
- txnParticipant->abortActiveTransaction(opCtx());
+ txnParticipant.abortActiveTransaction(opCtx());
ASSERT(_opObserver->transactionAborted);
- ASSERT(txnParticipant->transactionIsAborted());
+ ASSERT(txnParticipant.transactionIsAborted());
}
TEST_F(TxnParticipantTest, StepDownAfterPrepareDoesNotBlockThenCommit) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
const auto commitTS = Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
// Test that we can acquire the RSTL in mode X, and then immediately release it so the test can
@@ -960,118 +741,82 @@ TEST_F(TxnParticipantTest, StepDownAfterPrepareDoesNotBlockThenCommit) {
};
runFunctionFromDifferentOpCtx(func);
- txnParticipant->commitPreparedTransaction(opCtx(), commitTS, {});
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTS, {});
ASSERT(_opObserver->transactionCommitted);
- ASSERT(txnParticipant->transactionIsCommitted());
+ ASSERT(txnParticipant.transactionIsCommitted());
}
TEST_F(TxnParticipantTest, StepDownDuringAbortSucceeds) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
ASSERT_OK(repl::ReplicationCoordinator::get(opCtx())->setFollowerMode(
repl::MemberState::RS_SECONDARY));
- txnParticipant->abortActiveTransaction(opCtx());
+ txnParticipant.abortActiveTransaction(opCtx());
ASSERT(_opObserver->transactionAborted);
- ASSERT(txnParticipant->transactionIsAborted());
+ ASSERT(txnParticipant.transactionIsAborted());
}
TEST_F(TxnParticipantTest, StepDownDuringPreparedAbortFails) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.prepareTransaction(opCtx(), {});
ASSERT_OK(repl::ReplicationCoordinator::get(opCtx())->setFollowerMode(
repl::MemberState::RS_SECONDARY));
ASSERT_THROWS_CODE(
- txnParticipant->abortActiveTransaction(opCtx()), AssertionException, ErrorCodes::NotMaster);
+ txnParticipant.abortActiveTransaction(opCtx()), AssertionException, ErrorCodes::NotMaster);
}
TEST_F(TxnParticipantTest, StepDownDuringPreparedCommitFails) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
const auto commitTS = Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
ASSERT_OK(repl::ReplicationCoordinator::get(opCtx())->setFollowerMode(
repl::MemberState::RS_SECONDARY));
- ASSERT_THROWS_CODE(txnParticipant->commitPreparedTransaction(opCtx(), commitTS, {}),
+ ASSERT_THROWS_CODE(txnParticipant.commitPreparedTransaction(opCtx(), commitTS, {}),
AssertionException,
ErrorCodes::NotMaster);
}
-TEST_F(TxnParticipantTest, ArbitraryAbortDuringPreparedCommitDoesNotAbortTransaction) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
-
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
- const auto commitTS = Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
-
- auto originalFn = _opObserver->onTransactionCommitFn;
- _opObserver->onTransactionCommitFn = [&](boost::optional<OplogSlot> commitOplogEntryOpTime,
- boost::optional<Timestamp> commitTimestamp,
- std::vector<repl::ReplOperation>& statements) {
- originalFn(commitOplogEntryOpTime, commitTimestamp, statements);
- ASSERT(commitOplogEntryOpTime);
- ASSERT(commitTimestamp);
-
- ASSERT_GT(*commitTimestamp, prepareTimestamp);
-
- ASSERT(statements.empty());
-
- // The transaction may be aborted without checking out the txnParticipant.
- auto func = [&](OperationContext* opCtx) { txnParticipant->abortArbitraryTransaction(); };
- runFunctionFromDifferentOpCtx(func);
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
- };
-
- txnParticipant->commitPreparedTransaction(opCtx(), commitTS, {});
-
- // The recovery unit is reset on commit.
- ASSERT(opCtx()->recoveryUnit()->getCommitTimestamp().isNull());
-
- ASSERT(_opObserver->transactionCommitted);
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
- ASSERT(txnParticipant->transactionIsCommitted());
-}
-
DEATH_TEST_F(TxnParticipantTest,
ThrowDuringPreparedOnTransactionCommitIsFatal,
"Caught exception during commit") {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
_opObserver->onTransactionCommitThrowsException = true;
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
const auto commitTS = Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
- txnParticipant->commitPreparedTransaction(opCtx(), commitTS, {});
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTS, {});
}
TEST_F(TxnParticipantTest, ThrowDuringUnpreparedCommitLetsTheAbortAtEntryPointToCleanUp) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
_opObserver->onTransactionCommitThrowsException = true;
- ASSERT_THROWS_CODE(txnParticipant->commitUnpreparedTransaction(opCtx()),
+ ASSERT_THROWS_CODE(txnParticipant.commitUnpreparedTransaction(opCtx()),
AssertionException,
ErrorCodes::OperationFailed);
ASSERT_FALSE(_opObserver->transactionCommitted);
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
- ASSERT_FALSE(txnParticipant->transactionIsCommitted());
+ ASSERT_FALSE(txnParticipant.transactionIsAborted());
+ ASSERT_FALSE(txnParticipant.transactionIsCommitted());
// Simulate the abort at entry point.
- txnParticipant->abortActiveUnpreparedOrStashPreparedTransaction(opCtx());
- ASSERT_TRUE(txnParticipant->transactionIsAborted());
+ txnParticipant.abortActiveUnpreparedOrStashPreparedTransaction(opCtx());
+ ASSERT_TRUE(txnParticipant.transactionIsAborted());
}
TEST_F(TxnParticipantTest, ContinuingATransactionWithNoResourcesAborts) {
@@ -1083,7 +828,7 @@ TEST_F(TxnParticipantTest, ContinuingATransactionWithNoResourcesAborts) {
auto txnParticipant = TransactionParticipant::get(opCtx());
ASSERT_THROWS_CODE(
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), false, boost::none),
+ txnParticipant.beginOrContinue(opCtx(), *opCtx()->getTxnNumber(), false, boost::none),
AssertionException,
ErrorCodes::NoSuchTransaction);
}
@@ -1092,7 +837,7 @@ TEST_F(TxnParticipantTest, KillSessionsDoesNotAbortPreparedTransactions) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
auto ruPrepareTimestamp = Timestamp();
auto originalFn = _opObserver->onTransactionPrepareFn;
@@ -1103,23 +848,23 @@ TEST_F(TxnParticipantTest, KillSessionsDoesNotAbortPreparedTransactions) {
};
// Check that prepareTimestamp gets set.
- auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
ASSERT_EQ(ruPrepareTimestamp, prepareTimestamp);
// Check that the oldest prepareTimestamp is the one we just set.
auto prepareOpTime = ServerTransactionsMetrics::get(opCtx())->getOldestActiveOpTime();
ASSERT_EQ(prepareOpTime->getTimestamp(), prepareTimestamp);
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
- txnParticipant->abortArbitraryTransaction();
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
+ ASSERT_FALSE(txnParticipant.transactionIsAborted());
ASSERT(_opObserver->transactionPrepared);
}
-TEST_F(TxnParticipantTest, TransactionTimeoutDoesNotAbortPreparedTransactions) {
+TEST_F(TxnParticipantTest, CannotAbortArbitraryPreparedTransactions) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
auto ruPrepareTimestamp = Timestamp();
auto originalFn = _opObserver->onTransactionPrepareFn;
@@ -1130,16 +875,15 @@ TEST_F(TxnParticipantTest, TransactionTimeoutDoesNotAbortPreparedTransactions) {
};
// Check that prepareTimestamp gets set.
- auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
ASSERT_EQ(ruPrepareTimestamp, prepareTimestamp);
// Check that the oldest prepareTimestamp is the one we just set.
auto prepareOpTime = ServerTransactionsMetrics::get(opCtx())->getOldestActiveOpTime();
ASSERT_EQ(prepareOpTime->getTimestamp(), prepareTimestamp);
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
- ASSERT(!txnParticipant->expired());
- txnParticipant->abortArbitraryTransaction();
- ASSERT(!txnParticipant->transactionIsAborted());
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
+ ASSERT(!txnParticipant.transactionIsAborted());
ASSERT(_opObserver->transactionPrepared);
}
@@ -1147,7 +891,7 @@ TEST_F(TxnParticipantTest, CannotStartNewTransactionWhilePreparedTransactionInPr
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
auto ruPrepareTimestamp = Timestamp();
auto originalFn = _opObserver->onTransactionPrepareFn;
@@ -1159,37 +903,36 @@ TEST_F(TxnParticipantTest, CannotStartNewTransactionWhilePreparedTransactionInPr
};
// Check that prepareTimestamp gets set.
- auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
ASSERT_EQ(ruPrepareTimestamp, prepareTimestamp);
// Check that the oldest prepareTimestamp is the one we just set.
auto prepareOpTime = ServerTransactionsMetrics::get(opCtx())->getOldestActiveOpTime();
ASSERT_EQ(prepareOpTime->getTimestamp(), prepareTimestamp);
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
OperationContextSession::checkIn(opCtx());
{
+ auto guard = makeGuard([&]() { OperationContextSession::checkOut(opCtx()); });
// Try to start a new transaction while there is already a prepared transaction on the
// session. This should fail with a PreparedTransactionInProgress error.
- auto func = [
+ runFunctionFromDifferentOpCtx([
lsid = *opCtx()->getLogicalSessionId(),
txnNumberToStart = *opCtx()->getTxnNumber() + 1
](OperationContext * newOpCtx) {
newOpCtx->setLogicalSessionId(lsid);
newOpCtx->setTxnNumber(txnNumberToStart);
+
MongoDOperationContextSession ocs(newOpCtx);
auto txnParticipant = TransactionParticipant::get(newOpCtx);
-
- ASSERT_THROWS_CODE(txnParticipant->beginOrContinue(txnNumberToStart, false, true),
- AssertionException,
- ErrorCodes::PreparedTransactionInProgress);
- };
-
- runFunctionFromDifferentOpCtx(func);
+ ASSERT_THROWS_CODE(
+ txnParticipant.beginOrContinue(newOpCtx, txnNumberToStart, false, true),
+ AssertionException,
+ ErrorCodes::PreparedTransactionInProgress);
+ });
}
- OperationContextSession::checkOut(opCtx());
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
+ ASSERT_FALSE(txnParticipant.transactionIsAborted());
ASSERT(_opObserver->transactionPrepared);
}
@@ -1197,17 +940,17 @@ TEST_F(TxnParticipantTest, CannotInsertInPreparedTransaction) {
auto outerScopedSession = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
auto operation = repl::OplogEntry::makeInsertOperation(kNss, kUUID, BSON("TestValue" << 0));
- txnParticipant->addTransactionOperation(opCtx(), operation);
+ txnParticipant.addTransactionOperation(opCtx(), operation);
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.prepareTransaction(opCtx(), {});
- ASSERT_THROWS_CODE(txnParticipant->unstashTransactionResources(opCtx(), "insert"),
+ ASSERT_THROWS_CODE(txnParticipant.unstashTransactionResources(opCtx(), "insert"),
AssertionException,
ErrorCodes::PreparedTransactionInProgress);
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
+ ASSERT_FALSE(txnParticipant.transactionIsAborted());
ASSERT(_opObserver->transactionPrepared);
}
@@ -1215,50 +958,44 @@ TEST_F(TxnParticipantTest, ImplictAbortDoesNotAbortPreparedTransaction) {
auto outerScopedSession = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
auto operation = repl::OplogEntry::makeInsertOperation(kNss, kUUID, BSON("TestValue" << 0));
- txnParticipant->addTransactionOperation(opCtx(), operation);
+ txnParticipant.addTransactionOperation(opCtx(), operation);
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.prepareTransaction(opCtx(), {});
// The next command throws an exception and wants to abort the transaction.
// This is a no-op.
- txnParticipant->abortActiveUnpreparedOrStashPreparedTransaction(opCtx());
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
+ txnParticipant.abortActiveUnpreparedOrStashPreparedTransaction(opCtx());
+ ASSERT_FALSE(txnParticipant.transactionIsAborted());
ASSERT_TRUE(_opObserver->transactionPrepared);
}
-DEATH_TEST_F(TxnParticipantTest, AbortIsIllegalDuringCommittingPreparedTransaction, "invariant") {
+DEATH_TEST_F(TxnParticipantTest,
+ AbortIsIllegalDuringCommittingPreparedTransaction,
+ "isCommittingWithPrepare") {
auto outerScopedSession = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
auto operation = repl::OplogEntry::makeInsertOperation(kNss, kUUID, BSON("TestValue" << 0));
- txnParticipant->addTransactionOperation(opCtx(), operation);
- auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.addTransactionOperation(opCtx(), operation);
+ auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
auto commitTS = Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
// Check that the oldest prepareTimestamp is the one we just set.
auto prepareOpTime = ServerTransactionsMetrics::get(opCtx())->getOldestActiveOpTime();
ASSERT_EQ(prepareOpTime->getTimestamp(), prepareTimestamp);
- auto sessionId = *opCtx()->getLogicalSessionId();
- auto txnNum = *opCtx()->getTxnNumber();
_opObserver->onTransactionCommitFn = [&](boost::optional<OplogSlot> commitOplogEntryOpTime,
boost::optional<Timestamp> commitTimestamp,
std::vector<repl::ReplOperation>& statements) {
- // This should never happen.
- auto func = [&](OperationContext* opCtx) {
- opCtx->setLogicalSessionId(sessionId);
- opCtx->setTxnNumber(txnNum);
- // Hit an invariant. This should never happen.
- txnParticipant->abortActiveTransaction(opCtx);
- };
- runFunctionFromDifferentOpCtx(func);
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
+ // Hit an invariant. This should never happen.
+ txnParticipant.abortActiveTransaction(opCtx());
+ ASSERT_FALSE(txnParticipant.transactionIsAborted());
};
- txnParticipant->commitPreparedTransaction(opCtx(), commitTS, {});
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTS, {});
// Check that we removed the prepareTimestamp from the set.
ASSERT_FALSE(ServerTransactionsMetrics::get(opCtx())->getOldestActiveOpTime());
}
@@ -1267,7 +1004,7 @@ TEST_F(TxnParticipantTest, CannotContinueNonExistentTransaction) {
MongoDOperationContextSession opCtxSession(opCtx());
auto txnParticipant = TransactionParticipant::get(opCtx());
ASSERT_THROWS_CODE(
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), false, boost::none),
+ txnParticipant.beginOrContinue(opCtx(), *opCtx()->getTxnNumber(), false, boost::none),
AssertionException,
ErrorCodes::NoSuchTransaction);
}
@@ -1277,7 +1014,7 @@ TEST_F(TxnParticipantTest, TransactionTooLargeWhileBuilding) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
// Two 6MB operations should succeed; three 6MB operations should fail.
constexpr size_t kBigDataSize = 6 * 1024 * 1024;
@@ -1286,9 +1023,9 @@ TEST_F(TxnParticipantTest, TransactionTooLargeWhileBuilding) {
kNss,
kUUID,
BSON("_id" << 0 << "data" << BSONBinData(bigData.get(), kBigDataSize, BinDataGeneral)));
- txnParticipant->addTransactionOperation(opCtx(), operation);
- txnParticipant->addTransactionOperation(opCtx(), operation);
- ASSERT_THROWS_CODE(txnParticipant->addTransactionOperation(opCtx(), operation),
+ txnParticipant.addTransactionOperation(opCtx(), operation);
+ txnParticipant.addTransactionOperation(opCtx(), operation);
+ ASSERT_THROWS_CODE(txnParticipant.addTransactionOperation(opCtx(), operation),
AssertionException,
ErrorCodes::TransactionTooLarge);
}
@@ -1311,7 +1048,7 @@ TEST_F(TxnParticipantTest, StashInNestedSessionIsANoop) {
// Perform initial unstash, which sets up a WriteUnitOfWork.
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "find");
+ txnParticipant.unstashTransactionResources(opCtx(), "find");
ASSERT_EQUALS(originalLocker, opCtx()->lockState());
ASSERT_EQUALS(originalRecoveryUnit, opCtx()->recoveryUnit());
ASSERT(opCtx()->getWriteUnitOfWork());
@@ -1319,7 +1056,7 @@ TEST_F(TxnParticipantTest, StashInNestedSessionIsANoop) {
{
// Make it look like we're in a DBDirectClient running a nested operation.
DirectClientSetter inDirectClient(opCtx());
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
// The stash was a noop, so the locker, RecoveryUnit, and WriteUnitOfWork on the
// OperationContext are unaffected.
@@ -1344,12 +1081,12 @@ protected:
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- ASSERT(txnParticipant->inMultiDocumentTransaction());
+ ASSERT(txnParticipant.inMultiDocumentTransaction());
- ASSERT_THROWS_CODE(
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), autocommit, startTransaction),
- AssertionException,
- 50911);
+ ASSERT_THROWS_CODE(txnParticipant.beginOrContinue(
+ opCtx(), *opCtx()->getTxnNumber(), autocommit, startTransaction),
+ AssertionException,
+ 50911);
}
void canSpecifyStartTransactionOnAbortedTxn() {
@@ -1358,13 +1095,14 @@ protected:
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- ASSERT(txnParticipant->inMultiDocumentTransaction());
+ ASSERT(txnParticipant.inMultiDocumentTransaction());
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT(txnParticipant->transactionIsAborted());
+ txnParticipant.abortActiveTransaction(opCtx());
+ ASSERT(txnParticipant.transactionIsAborted());
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), autocommit, startTransaction);
- ASSERT(txnParticipant->inMultiDocumentTransaction());
+ txnParticipant.beginOrContinue(
+ opCtx(), *opCtx()->getTxnNumber(), autocommit, startTransaction);
+ ASSERT(txnParticipant.inMultiDocumentTransaction());
}
void cannotSpecifyStartTransactionOnCommittedTxn() {
@@ -1373,15 +1111,15 @@ protected:
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- ASSERT(txnParticipant->inMultiDocumentTransaction());
+ ASSERT(txnParticipant.inMultiDocumentTransaction());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
- txnParticipant->commitUnpreparedTransaction(opCtx());
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.commitUnpreparedTransaction(opCtx());
- ASSERT_THROWS_CODE(
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), autocommit, startTransaction),
- AssertionException,
- 50911);
+ ASSERT_THROWS_CODE(txnParticipant.beginOrContinue(
+ opCtx(), *opCtx()->getTxnNumber(), autocommit, startTransaction),
+ AssertionException,
+ 50911);
}
void cannotSpecifyStartTransactionOnPreparedTxn() {
@@ -1390,32 +1128,32 @@ protected:
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- ASSERT(txnParticipant->inMultiDocumentTransaction());
+ ASSERT(txnParticipant.inMultiDocumentTransaction());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
auto operation = repl::OplogEntry::makeInsertOperation(kNss, kUUID, BSON("TestValue" << 0));
- txnParticipant->addTransactionOperation(opCtx(), operation);
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.addTransactionOperation(opCtx(), operation);
+ txnParticipant.prepareTransaction(opCtx(), {});
- ASSERT_THROWS_CODE(
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), autocommit, startTransaction),
- AssertionException,
- 50911);
+ ASSERT_THROWS_CODE(txnParticipant.beginOrContinue(
+ opCtx(), *opCtx()->getTxnNumber(), autocommit, startTransaction),
+ AssertionException,
+ 50911);
}
void cannotSpecifyStartTransactionOnStartedRetryableWrite() {
MongoDOperationContextSession opCtxSession(opCtx());
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), boost::none, boost::none);
- ASSERT_FALSE(txnParticipant->inMultiDocumentTransaction());
+ txnParticipant.beginOrContinue(opCtx(), *opCtx()->getTxnNumber(), boost::none, boost::none);
+ ASSERT_FALSE(txnParticipant.inMultiDocumentTransaction());
auto autocommit = false;
auto startTransaction = true;
- ASSERT_THROWS_CODE(
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), autocommit, startTransaction),
- AssertionException,
- 50911);
+ ASSERT_THROWS_CODE(txnParticipant.beginOrContinue(
+ opCtx(), *opCtx()->getTxnNumber(), autocommit, startTransaction),
+ AssertionException,
+ 50911);
}
void cannotSpecifyStartTransactionOnAbortedPreparedTransaction() {
@@ -1424,20 +1162,20 @@ protected:
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- ASSERT(txnParticipant->inMultiDocumentTransaction());
+ ASSERT(txnParticipant.inMultiDocumentTransaction());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
- txnParticipant->prepareTransaction(opCtx(), {});
- ASSERT(txnParticipant->transactionIsPrepared());
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.prepareTransaction(opCtx(), {});
+ ASSERT(txnParticipant.transactionIsPrepared());
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT(txnParticipant->transactionIsAborted());
+ txnParticipant.abortActiveTransaction(opCtx());
+ ASSERT(txnParticipant.transactionIsAborted());
startTransaction = true;
- ASSERT_THROWS_CODE(
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), autocommit, startTransaction),
- AssertionException,
- 50911);
+ ASSERT_THROWS_CODE(txnParticipant.beginOrContinue(
+ opCtx(), *opCtx()->getTxnNumber(), autocommit, startTransaction),
+ AssertionException,
+ 50911);
}
};
@@ -1521,97 +1259,27 @@ TEST_F(ConfigTxnParticipantTest, CannotSpecifyStartTransactionOnAbortedPreparedT
cannotSpecifyStartTransactionOnAbortedPreparedTransaction();
}
-TEST_F(TxnParticipantTest, KillSessionsDuringUnpreparedAbortSucceeds) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
-
- auto originalFn = _opObserver->onTransactionAbortFn;
- _opObserver->onTransactionAbortFn = [&] {
- originalFn();
-
- // The transaction may be aborted without checking out the txnParticipant.
- txnParticipant->abortArbitraryTransaction();
- ASSERT(txnParticipant->transactionIsAborted());
- };
-
- txnParticipant->abortActiveTransaction(opCtx());
-
- ASSERT(_opObserver->transactionAborted);
- ASSERT(txnParticipant->transactionIsAborted());
-}
-
-TEST_F(TxnParticipantTest, ActiveAbortIsLegalDuringUnpreparedAbort) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
-
- auto sessionId = *opCtx()->getLogicalSessionId();
- auto txnNumber = *opCtx()->getTxnNumber();
- auto originalFn = _opObserver->onTransactionAbortFn;
- _opObserver->onTransactionAbortFn = [&] {
- originalFn();
-
- auto func = [&](OperationContext* opCtx) {
- opCtx->setLogicalSessionId(sessionId);
- opCtx->setTxnNumber(txnNumber);
-
- // Prevent recursion.
- _opObserver->onTransactionAbortFn = originalFn;
- txnParticipant->abortActiveTransaction(opCtx);
- ASSERT(txnParticipant->transactionIsAborted());
- };
- runFunctionFromDifferentOpCtx(func);
- };
-
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT(_opObserver->transactionAborted);
- ASSERT(txnParticipant->transactionIsAborted());
-}
-
TEST_F(TxnParticipantTest, ThrowDuringUnpreparedOnTransactionAbort) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
_opObserver->onTransactionAbortThrowsException = true;
- ASSERT_THROWS_CODE(txnParticipant->abortActiveTransaction(opCtx()),
+ ASSERT_THROWS_CODE(txnParticipant.abortActiveTransaction(opCtx()),
AssertionException,
ErrorCodes::OperationFailed);
}
-TEST_F(TxnParticipantTest, KillSessionsDuringPreparedAbortFails) {
- auto sessionCheckout = checkOutSession();
- auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
- txnParticipant->prepareTransaction(opCtx(), {});
-
- auto originalFn = _opObserver->onTransactionAbortFn;
- _opObserver->onTransactionAbortFn = [&] {
- originalFn();
-
- // KillSessions may attempt to abort without checking out the txnParticipant.
- txnParticipant->abortArbitraryTransaction();
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
- ASSERT(txnParticipant->transactionIsPrepared());
- };
-
- txnParticipant->abortActiveTransaction(opCtx());
-
- ASSERT(_opObserver->transactionAborted);
- ASSERT(txnParticipant->transactionIsAborted());
-}
-
TEST_F(TxnParticipantTest, ThrowDuringPreparedOnTransactionAbortIsFatal) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.prepareTransaction(opCtx(), {});
_opObserver->onTransactionAbortThrowsException = true;
- ASSERT_THROWS_CODE(txnParticipant->abortActiveTransaction(opCtx()),
+ ASSERT_THROWS_CODE(txnParticipant.abortActiveTransaction(opCtx()),
AssertionException,
ErrorCodes::OperationFailed);
}
@@ -1628,16 +1296,16 @@ TEST_F(TxnParticipantTest, ReacquireLocksForPreparedTransactionsOnStepUp) {
repl::UnreplicatedWritesBlock uwb(opCtx());
ASSERT(!opCtx()->writesAreReplicated());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
// Simulate the locking of an insert.
{
Lock::DBLock dbLock(opCtx(), "test", MODE_IX);
Lock::CollectionLock collLock(opCtx()->lockState(), "test.foo", MODE_IX);
}
- txnParticipant->prepareTransaction(opCtx(), repl::OpTime({1, 1}, 1));
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.prepareTransaction(opCtx(), repl::OpTime({1, 1}, 1));
+ txnParticipant.stashTransactionResources(opCtx());
// Secondary yields locks for prepared transactions.
- ASSERT_FALSE(txnParticipant->getTxnResourceStashLockerForTest()->isLocked());
+ ASSERT_FALSE(txnParticipant.getTxnResourceStashLockerForTest()->isLocked());
}
// Step-up will restore the locks of prepared transactions.
@@ -1646,9 +1314,9 @@ TEST_F(TxnParticipantTest, ReacquireLocksForPreparedTransactionsOnStepUp) {
{
auto sessionCheckout = checkOutSession({});
auto txnParticipant = TransactionParticipant::get(opCtx());
- ASSERT(txnParticipant->getTxnResourceStashLockerForTest()->isLocked());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
- txnParticipant->abortActiveTransaction(opCtx());
+ ASSERT(txnParticipant.getTxnResourceStashLockerForTest()->isLocked());
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.abortActiveTransaction(opCtx());
}
}
@@ -1697,8 +1365,8 @@ TEST_F(TransactionsMetricsTest, IncrementPreparedTransaction) {
auto txnParticipant = TransactionParticipant::get(opCtx());
unsigned long long beforePrepareCount =
ServerTransactionsMetrics::get(opCtx())->getTotalPrepared();
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.prepareTransaction(opCtx(), {});
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalPrepared(), beforePrepareCount + 1U);
}
@@ -1706,12 +1374,12 @@ TEST_F(TransactionsMetricsTest, IncrementPreparedTransaction) {
TEST_F(TransactionsMetricsTest, IncrementTotalCommittedOnCommit) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
unsigned long long beforeCommitCount =
ServerTransactionsMetrics::get(opCtx())->getTotalCommitted();
- txnParticipant->commitUnpreparedTransaction(opCtx());
+ txnParticipant.commitUnpreparedTransaction(opCtx());
// Assert that the committed counter is incremented by 1.
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalCommitted(), beforeCommitCount + 1U);
@@ -1720,17 +1388,17 @@ TEST_F(TransactionsMetricsTest, IncrementTotalCommittedOnCommit) {
TEST_F(TransactionsMetricsTest, IncrementTotalPreparedThenCommitted) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
const auto commitTimestamp =
Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
unsigned long long beforePreparedThenCommittedCount =
ServerTransactionsMetrics::get(opCtx())->getTotalPreparedThenCommitted();
- txnParticipant->commitPreparedTransaction(opCtx(), commitTimestamp, {});
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTimestamp, {});
- ASSERT_TRUE(txnParticipant->transactionIsCommitted());
+ ASSERT_TRUE(txnParticipant.transactionIsCommitted());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalPreparedThenCommitted(),
beforePreparedThenCommittedCount + 1U);
}
@@ -1739,12 +1407,12 @@ TEST_F(TransactionsMetricsTest, IncrementTotalPreparedThenCommitted) {
TEST_F(TransactionsMetricsTest, IncrementTotalAbortedUponAbort) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
unsigned long long beforeAbortCount =
ServerTransactionsMetrics::get(opCtx())->getTotalAborted();
- txnParticipant->abortArbitraryTransaction();
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
// Assert that the aborted counter is incremented by 1.
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalAborted(), beforeAbortCount + 1U);
@@ -1756,11 +1424,11 @@ TEST_F(TransactionsMetricsTest, IncrementTotalPreparedThenAborted) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.prepareTransaction(opCtx(), {});
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT(txnParticipant->transactionIsAborted());
+ txnParticipant.abortActiveTransaction(opCtx());
+ ASSERT(txnParticipant.transactionIsAborted());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalPreparedThenAborted(),
beforePreparedThenAbortedCount + 1U);
}
@@ -1771,15 +1439,15 @@ TEST_F(TransactionsMetricsTest, IncrementCurrentPreparedWithCommit) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
const auto commitTimestamp =
Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentPrepared(),
beforeCurrentPrepared + 1U);
- txnParticipant->commitPreparedTransaction(opCtx(), commitTimestamp, {});
- ASSERT(txnParticipant->transactionIsCommitted());
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTimestamp, {});
+ ASSERT(txnParticipant.transactionIsCommitted());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentPrepared(), beforeCurrentPrepared);
}
@@ -1789,13 +1457,13 @@ TEST_F(TransactionsMetricsTest, IncrementCurrentPreparedWithAbort) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.prepareTransaction(opCtx(), {});
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentPrepared(),
beforeCurrentPrepared + 1U);
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT(txnParticipant->transactionIsAborted());
+ txnParticipant.abortActiveTransaction(opCtx());
+ ASSERT(txnParticipant.transactionIsAborted());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentPrepared(), beforeCurrentPrepared);
}
@@ -1806,18 +1474,18 @@ TEST_F(TransactionsMetricsTest, TrackTotalOpenTransactionsWithAbort) {
// Tests that starting a transaction increments the open transactions counter by 1.
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentOpen(),
beforeTransactionStart + 1U);
// Tests that stashing the transaction resources does not affect the open transactions counter.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentOpen(),
beforeTransactionStart + 1U);
// Tests that aborting a transaction decrements the open transactions counter by 1.
- txnParticipant->abortArbitraryTransaction();
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentOpen(), beforeTransactionStart);
}
@@ -1828,20 +1496,20 @@ TEST_F(TransactionsMetricsTest, TrackTotalOpenTransactionsWithCommit) {
// Tests that starting a transaction increments the open transactions counter by 1.
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentOpen(),
beforeTransactionStart + 1U);
// Tests that stashing the transaction resources does not affect the open transactions counter.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentOpen(),
beforeTransactionStart + 1U);
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
// Tests that committing a transaction decrements the open transactions counter by 1.
- txnParticipant->commitUnpreparedTransaction(opCtx());
+ txnParticipant.commitUnpreparedTransaction(opCtx());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentOpen(), beforeTransactionStart);
}
@@ -1859,7 +1527,7 @@ TEST_F(TransactionsMetricsTest, TrackTotalActiveAndInactiveTransactionsWithCommi
// Tests that the first unstash increments the active counter and decrements the inactive
// counter.
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(),
beforeActiveCounter + 1U);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(), beforeInactiveCounter);
@@ -1867,20 +1535,20 @@ TEST_F(TransactionsMetricsTest, TrackTotalActiveAndInactiveTransactionsWithCommi
// Tests that stashing the transaction resources decrements active counter and increments
// inactive counter.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(), beforeActiveCounter);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(),
beforeInactiveCounter + 1U);
// Tests that the second unstash increments the active counter and decrements the inactive
// counter.
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(),
beforeActiveCounter + 1U);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(), beforeInactiveCounter);
// Tests that committing a transaction decrements the active counter only.
- txnParticipant->commitUnpreparedTransaction(opCtx());
+ txnParticipant.commitUnpreparedTransaction(opCtx());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(), beforeActiveCounter);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(), beforeInactiveCounter);
}
@@ -1899,7 +1567,7 @@ TEST_F(TransactionsMetricsTest, TrackTotalActiveAndInactiveTransactionsWithStash
// Tests that the first unstash increments the active counter and decrements the inactive
// counter.
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(),
beforeActiveCounter + 1U);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(), beforeInactiveCounter);
@@ -1907,13 +1575,13 @@ TEST_F(TransactionsMetricsTest, TrackTotalActiveAndInactiveTransactionsWithStash
// Tests that stashing the transaction resources decrements active counter and increments
// inactive counter.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(), beforeActiveCounter);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(),
beforeInactiveCounter + 1U);
// Tests that aborting a stashed transaction decrements the inactive counter only.
- txnParticipant->abortArbitraryTransaction();
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(), beforeActiveCounter);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(), beforeInactiveCounter);
}
@@ -1932,13 +1600,13 @@ TEST_F(TransactionsMetricsTest, TrackTotalActiveAndInactiveTransactionsWithUnsta
// Tests that the first unstash increments the active counter and decrements the inactive
// counter.
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(),
beforeActiveCounter + 1U);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(), beforeInactiveCounter);
// Tests that aborting a stashed transaction decrements the active counter only.
- txnParticipant->abortArbitraryTransaction();
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(), beforeActiveCounter);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(), beforeInactiveCounter);
}
@@ -1956,8 +1624,8 @@ TEST_F(TransactionsMetricsTest, TrackCurrentActiveAndInactivePreparedTransaction
// Tests that unstashing a transaction puts it into an active state.
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
const auto commitTimestamp =
Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
@@ -1968,21 +1636,21 @@ TEST_F(TransactionsMetricsTest, TrackCurrentActiveAndInactivePreparedTransaction
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalPrepared(), beforePrepareCount + 1U);
// Tests that the first stash decrements the active counter and increments the inactive counter.
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(),
beforeActivePreparedCounter);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(),
beforeInactivePreparedCounter + 1U);
// Tests that unstashing increments the active counter and decrements the inactive counter.
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(),
beforeActivePreparedCounter + 1U);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(),
beforeInactivePreparedCounter);
// Tests that committing decrements the active counter only.
- txnParticipant->commitPreparedTransaction(opCtx(), commitTimestamp, {});
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTimestamp, {});
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(),
beforeActivePreparedCounter);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(),
@@ -2002,8 +1670,8 @@ TEST_F(TransactionsMetricsTest,
auto txnParticipant = TransactionParticipant::get(opCtx());
// Tests that unstashing a transaction increments the active counter only.
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.prepareTransaction(opCtx(), {});
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(),
beforeActivePreparedCounter + 1U);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(),
@@ -2011,20 +1679,20 @@ TEST_F(TransactionsMetricsTest,
// Tests that stashing a prepared transaction decrements the active counter and increments the
// inactive counter.
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(),
beforeActivePreparedCounter);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(),
beforeInactivePreparedCounter + 1U);
// Tests that aborting a stashed prepared transaction decrements the inactive counter only.
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(),
beforeActivePreparedCounter + 1U);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(),
beforeInactivePreparedCounter);
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT(txnParticipant->transactionIsAborted());
+ txnParticipant.abortActiveTransaction(opCtx());
+ ASSERT(txnParticipant.transactionIsAborted());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(),
beforeActivePreparedCounter);
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(),
@@ -2055,10 +1723,10 @@ TEST_F(TransactionsMetricsTest, TransactionErrorsBeforeUnstash) {
auto txnParticipant = TransactionParticipant::get(opCtx());
const bool autocommit = false;
const boost::optional<bool> startTransaction = boost::none;
- ASSERT_THROWS_CODE(
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), autocommit, startTransaction),
- AssertionException,
- ErrorCodes::NoSuchTransaction);
+ ASSERT_THROWS_CODE(txnParticipant.beginOrContinue(
+ opCtx(), *opCtx()->getTxnNumber(), autocommit, startTransaction),
+ AssertionException,
+ ErrorCodes::NoSuchTransaction);
// The transaction is now aborted.
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(), beforeActiveCounter);
@@ -2072,16 +1740,16 @@ TEST_F(TransactionsMetricsTest, SingleTransactionStatsDurationShouldBeSetUponCom
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
// The transaction machinery cannot store an empty locker.
Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
// Advance the clock.
tickSource->advance(Microseconds(100));
- txnParticipant->commitUnpreparedTransaction(opCtx());
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getDuration(
- tickSource, tickSource->getTicks()),
+ txnParticipant.commitUnpreparedTransaction(opCtx());
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getDuration(tickSource,
+ tickSource->getTicks()),
Microseconds(100));
}
@@ -2090,7 +1758,7 @@ TEST_F(TransactionsMetricsTest, SingleTransactionStatsPreparedDurationShouldBeSe
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
// The transaction machinery cannot store an empty locker.
Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
@@ -2098,14 +1766,14 @@ TEST_F(TransactionsMetricsTest, SingleTransactionStatsPreparedDurationShouldBeSe
tickSource->advance(Microseconds(10));
// Prepare the transaction and extend the duration in the prepared state.
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
const auto commitTimestamp =
Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
tickSource->advance(Microseconds(100));
- txnParticipant->commitPreparedTransaction(opCtx(), commitTimestamp, {});
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getPreparedDuration(
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTimestamp, {});
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getPreparedDuration(
tickSource, tickSource->getTicks()),
Microseconds(100));
}
@@ -2115,14 +1783,14 @@ TEST_F(TransactionsMetricsTest, SingleTransactionStatsDurationShouldBeSetUponAbo
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
// Advance the clock.
tickSource->advance(Microseconds(100));
- txnParticipant->abortArbitraryTransaction();
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getDuration(
- tickSource, tickSource->getTicks()),
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getDuration(tickSource,
+ tickSource->getTicks()),
Microseconds(100));
}
@@ -2131,17 +1799,17 @@ TEST_F(TransactionsMetricsTest, SingleTransactionStatsPreparedDurationShouldBeSe
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
// Advance the clock.
tickSource->advance(Microseconds(10));
// Prepare the transaction and extend the duration in the prepared state.
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.prepareTransaction(opCtx(), {});
tickSource->advance(Microseconds(100));
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getPreparedDuration(
+ txnParticipant.abortActiveTransaction(opCtx());
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getPreparedDuration(
tickSource, tickSource->getTicks()),
Microseconds(100));
}
@@ -2151,29 +1819,29 @@ TEST_F(TransactionsMetricsTest, SingleTransactionStatsDurationShouldKeepIncreasi
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
// The transaction machinery cannot store an empty locker.
Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
tickSource->advance(Microseconds(100));
// The transaction's duration should have increased.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getDuration(
- tickSource, tickSource->getTicks()),
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getDuration(tickSource,
+ tickSource->getTicks()),
Microseconds(100));
tickSource->advance(Microseconds(100));
// Commit the transaction and check duration.
- txnParticipant->commitUnpreparedTransaction(opCtx());
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getDuration(
- tickSource, tickSource->getTicks()),
+ txnParticipant.commitUnpreparedTransaction(opCtx());
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getDuration(tickSource,
+ tickSource->getTicks()),
Microseconds(200));
// The transaction committed, so the duration shouldn't have increased even if more time passed.
tickSource->advance(Microseconds(100));
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getDuration(
- tickSource, tickSource->getTicks()),
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getDuration(tickSource,
+ tickSource->getTicks()),
Microseconds(200));
}
@@ -2183,34 +1851,34 @@ TEST_F(TransactionsMetricsTest,
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
// The transaction machinery cannot store an empty locker.
Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
// Prepare the transaction and extend the duration in the prepared state.
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
const auto commitTimestamp =
Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
tickSource->advance(Microseconds(100));
// The prepared transaction's duration should have increased.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getPreparedDuration(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getPreparedDuration(
tickSource, tickSource->getTicks()),
Microseconds(100));
tickSource->advance(Microseconds(100));
// Commit the prepared transaction and check the prepared duration.
- txnParticipant->commitPreparedTransaction(opCtx(), commitTimestamp, {});
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getPreparedDuration(
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTimestamp, {});
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getPreparedDuration(
tickSource, tickSource->getTicks()),
Microseconds(200));
// The prepared transaction committed, so the prepared duration shouldn't have increased even if
// more time passed.
tickSource->advance(Microseconds(100));
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getPreparedDuration(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getPreparedDuration(
tickSource, tickSource->getTicks()),
Microseconds(200));
}
@@ -2220,29 +1888,29 @@ TEST_F(TransactionsMetricsTest, SingleTransactionStatsDurationShouldKeepIncreasi
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
// The transaction machinery cannot store an empty locker.
Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
tickSource->advance(Microseconds(100));
// The transaction's duration should have increased.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getDuration(
- tickSource, tickSource->getTicks()),
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getDuration(tickSource,
+ tickSource->getTicks()),
Microseconds(100));
tickSource->advance(Microseconds(100));
// Abort the transaction and check duration.
- txnParticipant->abortArbitraryTransaction();
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getDuration(
- tickSource, tickSource->getTicks()),
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getDuration(tickSource,
+ tickSource->getTicks()),
Microseconds(200));
// The transaction aborted, so the duration shouldn't have increased even if more time passed.
tickSource->advance(Microseconds(100));
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getDuration(
- tickSource, tickSource->getTicks()),
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getDuration(tickSource,
+ tickSource->getTicks()),
Microseconds(200));
}
@@ -2252,31 +1920,31 @@ TEST_F(TransactionsMetricsTest,
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
// The transaction machinery cannot store an empty locker.
Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
// Prepare the transaction and extend the duration in the prepared state.
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.prepareTransaction(opCtx(), {});
tickSource->advance(Microseconds(100));
// The prepared transaction's duration should have increased.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getPreparedDuration(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getPreparedDuration(
tickSource, tickSource->getTicks()),
Microseconds(100));
tickSource->advance(Microseconds(100));
// Abort the prepared transaction and check the prepared duration.
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getPreparedDuration(
+ txnParticipant.abortActiveTransaction(opCtx());
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getPreparedDuration(
tickSource, tickSource->getTicks()),
Microseconds(200));
// The prepared transaction aborted, so the prepared duration shouldn't have increased even if
// more time passed.
tickSource->advance(Microseconds(100));
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getPreparedDuration(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getPreparedDuration(
tickSource, tickSource->getTicks()),
Microseconds(200));
}
@@ -2288,42 +1956,42 @@ TEST_F(TransactionsMetricsTest, TimeActiveMicrosShouldBeSetUponUnstashAndStash)
auto txnParticipant = TransactionParticipant::get(opCtx());
// Time active should be zero.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{0});
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
tickSource->advance(Microseconds(100));
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
// Advance clock during inactive period.
tickSource->advance(Microseconds(100));
// Time active should have increased only during active period.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{100});
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
tickSource->advance(Microseconds(100));
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
// Advance clock during inactive period.
tickSource->advance(Microseconds(100));
// Time active should have increased again.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{200});
// Start a new transaction.
const auto higherTxnNum = *opCtx()->getTxnNumber() + 1;
- txnParticipant->beginOrContinue(higherTxnNum, false, true);
+ txnParticipant.beginOrContinue(opCtx(), higherTxnNum, false, true);
// Time active should be zero for a new transaction.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{0});
}
@@ -2335,23 +2003,23 @@ TEST_F(TransactionsMetricsTest, TimeActiveMicrosShouldBeSetUponUnstashAndAbort)
auto txnParticipant = TransactionParticipant::get(opCtx());
// Time active should be zero.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{0});
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
tickSource->advance(Microseconds(100));
- txnParticipant->abortArbitraryTransaction();
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
// Time active should have increased.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{100});
tickSource->advance(Microseconds(100));
// The transaction is not active after abort, so time active should not have increased.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{100});
}
@@ -2363,17 +2031,17 @@ TEST_F(TransactionsMetricsTest, TimeActiveMicrosShouldNotBeSetUponAbortOnly) {
auto txnParticipant = TransactionParticipant::get(opCtx());
// Time active should be zero.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{0});
// Advance clock during inactive period.
tickSource->advance(Microseconds(100));
- txnParticipant->abortArbitraryTransaction();
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
// Time active should still be zero.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{0});
}
@@ -2385,32 +2053,32 @@ TEST_F(TransactionsMetricsTest, TimeActiveMicrosShouldIncreaseUntilStash) {
auto txnParticipant = TransactionParticipant::get(opCtx());
// Time active should be zero.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{0});
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
tickSource->advance(Microseconds(100));
// Time active should have increased.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds(100));
tickSource->advance(Microseconds(100));
// Time active should have increased again.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds(200));
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
tickSource->advance(Microseconds(100));
// The transaction is no longer active, so time active should have stopped increasing.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds(200));
}
@@ -2422,31 +2090,31 @@ TEST_F(TransactionsMetricsTest, TimeActiveMicrosShouldIncreaseUntilCommit) {
auto txnParticipant = TransactionParticipant::get(opCtx());
// Time active should be zero.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{0});
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
tickSource->advance(Microseconds(100));
// Time active should have increased.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{100});
tickSource->advance(Microseconds(100));
// Time active should have increased again.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{200});
- txnParticipant->commitUnpreparedTransaction(opCtx());
+ txnParticipant.commitUnpreparedTransaction(opCtx());
tickSource->advance(Microseconds(100));
// The transaction is no longer active, so time active should have stopped increasing.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(
tickSource, tickSource->getTicks()),
Microseconds(200));
}
@@ -2456,37 +2124,36 @@ TEST_F(TransactionsMetricsTest, AdditiveMetricsObjectsShouldBeAddedTogetherUponS
auto txnParticipant = TransactionParticipant::get(opCtx());
// Initialize field values for both AdditiveMetrics objects.
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysExamined =
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysExamined =
1;
CurOp::get(opCtx())->debug().additiveMetrics.keysExamined = 5;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.docsExamined =
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.docsExamined =
2;
CurOp::get(opCtx())->debug().additiveMetrics.docsExamined = 0;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.nMatched = 3;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.nModified = 1;
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.nMatched = 3;
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.nModified = 1;
CurOp::get(opCtx())->debug().additiveMetrics.nModified = 1;
CurOp::get(opCtx())->debug().additiveMetrics.ninserted = 4;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysInserted =
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysInserted =
1;
CurOp::get(opCtx())->debug().additiveMetrics.keysInserted = 1;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysDeleted =
- 0;
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysDeleted = 0;
CurOp::get(opCtx())->debug().additiveMetrics.keysDeleted = 0;
- txnParticipant->getSingleTransactionStatsForTest()
+ txnParticipant.getSingleTransactionStatsForTest()
.getOpDebug()
->additiveMetrics.prepareReadConflicts = 5;
CurOp::get(opCtx())->debug().additiveMetrics.prepareReadConflicts = 4;
auto additiveMetricsToCompare =
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics;
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics;
additiveMetricsToCompare.add(CurOp::get(opCtx())->debug().additiveMetrics);
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
- ASSERT(txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.equals(
+ ASSERT(txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.equals(
additiveMetricsToCompare));
}
@@ -2495,40 +2162,39 @@ TEST_F(TransactionsMetricsTest, AdditiveMetricsObjectsShouldBeAddedTogetherUponC
auto txnParticipant = TransactionParticipant::get(opCtx());
// Initialize field values for both AdditiveMetrics objects.
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysExamined =
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysExamined =
3;
CurOp::get(opCtx())->debug().additiveMetrics.keysExamined = 2;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.docsExamined =
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.docsExamined =
0;
CurOp::get(opCtx())->debug().additiveMetrics.docsExamined = 2;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.nMatched = 4;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.nModified = 5;
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.nMatched = 4;
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.nModified = 5;
CurOp::get(opCtx())->debug().additiveMetrics.nModified = 1;
CurOp::get(opCtx())->debug().additiveMetrics.ninserted = 1;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.ndeleted = 4;
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.ndeleted = 4;
CurOp::get(opCtx())->debug().additiveMetrics.ndeleted = 0;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysInserted =
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysInserted =
1;
CurOp::get(opCtx())->debug().additiveMetrics.keysInserted = 1;
- txnParticipant->getSingleTransactionStatsForTest()
+ txnParticipant.getSingleTransactionStatsForTest()
.getOpDebug()
->additiveMetrics.prepareReadConflicts = 0;
CurOp::get(opCtx())->debug().additiveMetrics.prepareReadConflicts = 0;
- txnParticipant->getSingleTransactionStatsForTest()
- .getOpDebug()
- ->additiveMetrics.writeConflicts = 6;
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.writeConflicts =
+ 6;
CurOp::get(opCtx())->debug().additiveMetrics.writeConflicts = 3;
auto additiveMetricsToCompare =
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics;
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics;
additiveMetricsToCompare.add(CurOp::get(opCtx())->debug().additiveMetrics);
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->commitUnpreparedTransaction(opCtx());
+ txnParticipant.commitUnpreparedTransaction(opCtx());
- ASSERT(txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.equals(
+ ASSERT(txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.equals(
additiveMetricsToCompare));
}
@@ -2537,37 +2203,35 @@ TEST_F(TransactionsMetricsTest, AdditiveMetricsObjectsShouldBeAddedTogetherUponA
auto txnParticipant = TransactionParticipant::get(opCtx());
// Initialize field values for both AdditiveMetrics objects.
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysExamined =
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysExamined =
2;
CurOp::get(opCtx())->debug().additiveMetrics.keysExamined = 4;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.docsExamined =
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.docsExamined =
1;
CurOp::get(opCtx())->debug().additiveMetrics.docsExamined = 3;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.nMatched = 2;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.nModified = 0;
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.nMatched = 2;
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.nModified = 0;
CurOp::get(opCtx())->debug().additiveMetrics.nModified = 3;
CurOp::get(opCtx())->debug().additiveMetrics.ndeleted = 5;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysInserted =
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysInserted =
1;
CurOp::get(opCtx())->debug().additiveMetrics.keysInserted = 1;
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysDeleted =
- 6;
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.keysDeleted = 6;
CurOp::get(opCtx())->debug().additiveMetrics.keysDeleted = 0;
- txnParticipant->getSingleTransactionStatsForTest()
- .getOpDebug()
- ->additiveMetrics.writeConflicts = 3;
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.writeConflicts =
+ 3;
CurOp::get(opCtx())->debug().additiveMetrics.writeConflicts = 3;
auto additiveMetricsToCompare =
- txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics;
+ txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics;
additiveMetricsToCompare.add(CurOp::get(opCtx())->debug().additiveMetrics);
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->abortActiveTransaction(opCtx());
+ txnParticipant.abortActiveTransaction(opCtx());
- ASSERT(txnParticipant->getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.equals(
+ ASSERT(txnParticipant.getSingleTransactionStatsForTest().getOpDebug()->additiveMetrics.equals(
additiveMetricsToCompare));
}
@@ -2579,33 +2243,33 @@ TEST_F(TransactionsMetricsTest, TimeInactiveMicrosShouldBeSetUponUnstashAndStash
// Time inactive should have increased.
tickSource->advance(Microseconds(100));
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeInactiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeInactiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{100});
// Time inactive should have increased again.
tickSource->advance(Microseconds(100));
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeInactiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeInactiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{200});
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
tickSource->advance(Microseconds(100));
// The transaction is currently active, so time inactive should not have increased.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeInactiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeInactiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{200});
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
tickSource->advance(Microseconds(100));
// The transaction is inactive again, so time inactive should have increased.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeInactiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeInactiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{300});
}
@@ -2617,28 +2281,28 @@ TEST_F(TransactionsMetricsTest, TimeInactiveMicrosShouldBeSetUponUnstashAndAbort
auto txnParticipant = TransactionParticipant::get(opCtx());
// Time inactive should be greater than or equal to zero.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeInactiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeInactiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{0});
tickSource->advance(Microseconds(100));
// Time inactive should have increased.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeInactiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeInactiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{100});
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
- txnParticipant->abortArbitraryTransaction();
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeInactiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeInactiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{100});
tickSource->advance(Microseconds(100));
// The transaction has aborted, so time inactive should not have increased.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeInactiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeInactiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{100});
}
@@ -2650,26 +2314,26 @@ TEST_F(TransactionsMetricsTest, TimeInactiveMicrosShouldIncreaseUntilCommit) {
auto txnParticipant = TransactionParticipant::get(opCtx());
// Time inactive should be greater than or equal to zero.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeInactiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeInactiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{0});
tickSource->advance(Microseconds(100));
// Time inactive should have increased.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeInactiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeInactiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{100});
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->commitUnpreparedTransaction(opCtx());
+ txnParticipant.commitUnpreparedTransaction(opCtx());
tickSource->advance(Microseconds(100));
// The transaction has committed, so time inactive should not have increased.
- ASSERT_EQ(txnParticipant->getSingleTransactionStatsForTest().getTimeInactiveMicros(
+ ASSERT_EQ(txnParticipant.getSingleTransactionStatsForTest().getTimeInactiveMicros(
tickSource, tickSource->getTicks()),
Microseconds{100});
}
@@ -2714,12 +2378,12 @@ TEST_F(TransactionsMetricsTest, ReportStashedResources) {
// Perform initial unstash which sets up a WriteUnitOfWork.
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "find");
+ txnParticipant.unstashTransactionResources(opCtx(), "find");
ASSERT(opCtx()->getWriteUnitOfWork());
ASSERT(opCtx()->lockState()->isLocked());
// Prepare the transaction and extend the duration in the prepared state.
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
const auto commitTimestamp =
Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
@@ -2727,11 +2391,11 @@ TEST_F(TransactionsMetricsTest, ReportStashedResources) {
tickSource->advance(Microseconds(preparedDuration));
// Stash resources. The original Locker and RecoveryUnit now belong to the stash.
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
ASSERT(!opCtx()->getWriteUnitOfWork());
// Verify that the Session's report of its own stashed state aligns with our expectations.
- auto stashedState = txnParticipant->reportStashedState();
+ auto stashedState = txnParticipant.reportStashedState(opCtx());
auto transactionDocument = stashedState.getObjectField("transaction");
auto parametersDocument = transactionDocument.getObjectField("parameters");
@@ -2772,14 +2436,14 @@ TEST_F(TransactionsMetricsTest, ReportStashedResources) {
// Unstash the stashed resources. This restores the original Locker and RecoveryUnit to the
// OperationContext.
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
ASSERT(opCtx()->getWriteUnitOfWork());
// With the resources unstashed, verify that the Session reports an empty stashed state.
- ASSERT(txnParticipant->reportStashedState().isEmpty());
+ ASSERT(txnParticipant.reportStashedState(opCtx()).isEmpty());
// Commit the transaction. This allows us to release locks.
- txnParticipant->commitPreparedTransaction(opCtx(), commitTimestamp, {});
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTimestamp, {});
}
TEST_F(TransactionsMetricsTest, ReportUnstashedResources) {
@@ -2804,18 +2468,18 @@ TEST_F(TransactionsMetricsTest, ReportUnstashedResources) {
// Perform initial unstash which sets up a WriteUnitOfWork.
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "find");
+ txnParticipant.unstashTransactionResources(opCtx(), "find");
ASSERT(opCtx()->getWriteUnitOfWork());
ASSERT(opCtx()->lockState()->isLocked());
// Prepare transaction and extend duration in the prepared state.
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.prepareTransaction(opCtx(), {});
const long prepareDuration = 10;
tickSource->advance(Microseconds(prepareDuration));
// Verify that the Session's report of its own unstashed state aligns with our expectations.
BSONObjBuilder unstashedStateBuilder;
- txnParticipant->reportUnstashedState(opCtx(), &unstashedStateBuilder);
+ txnParticipant.reportUnstashedState(opCtx(), &unstashedStateBuilder);
auto unstashedState = unstashedStateBuilder.obj();
auto transactionDocument = unstashedState.getObjectField("transaction");
auto parametersDocument = transactionDocument.getObjectField("parameters");
@@ -2842,12 +2506,12 @@ TEST_F(TransactionsMetricsTest, ReportUnstashedResources) {
ASSERT_GTE(transactionDocument.getField("timeInactiveMicros").numberLong(), 0);
// Stash resources. The original Locker and RecoveryUnit now belong to the stash.
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
ASSERT(!opCtx()->getWriteUnitOfWork());
// With the resources stashed, verify that the Session reports an empty unstashed state.
BSONObjBuilder builder;
- txnParticipant->reportUnstashedState(opCtx(), &builder);
+ txnParticipant.reportUnstashedState(opCtx(), &builder);
ASSERT(builder.obj().isEmpty());
}
@@ -2857,8 +2521,8 @@ TEST_F(TransactionsMetricsTest, ReportUnstashedResourcesForARetryableWrite) {
MongoDOperationContextSession opCtxSession(opCtx());
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->beginOrContinue(*opCtx()->getTxnNumber(), boost::none, boost::none);
- txnParticipant->unstashTransactionResources(opCtx(), "find");
+ txnParticipant.beginOrContinue(opCtx(), *opCtx()->getTxnNumber(), boost::none, boost::none);
+ txnParticipant.unstashTransactionResources(opCtx(), "find");
// Build a BSONObj containing the details which we expect to see reported when we invoke
// reportUnstashedState. For a retryable write, we should only include the txnNumber.
@@ -2871,7 +2535,7 @@ TEST_F(TransactionsMetricsTest, ReportUnstashedResourcesForARetryableWrite) {
// Verify that the Session's report of its own unstashed state aligns with our expectations.
BSONObjBuilder unstashedStateBuilder;
- txnParticipant->reportUnstashedState(opCtx(), &unstashedStateBuilder);
+ txnParticipant.reportUnstashedState(opCtx(), &unstashedStateBuilder);
ASSERT_BSONOBJ_EQ(unstashedStateBuilder.obj(), reportBuilder.obj());
}
@@ -2904,13 +2568,13 @@ TEST_F(TransactionsMetricsTest, LastClientInfoShouldUpdateUponStash) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
// The transaction machinery cannot store an empty locker.
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
// LastClientInfo should have been set.
- auto lastClientInfo = txnParticipant->getSingleTransactionStatsForTest().getLastClientInfo();
+ auto lastClientInfo = txnParticipant.getSingleTransactionStatsForTest().getLastClientInfo();
ASSERT_EQ(lastClientInfo.clientHostAndPort, "");
ASSERT_EQ(lastClientInfo.connectionId, 0);
ASSERT_EQ(lastClientInfo.appName, "appName");
@@ -2922,11 +2586,11 @@ TEST_F(TransactionsMetricsTest, LastClientInfoShouldUpdateUponStash) {
clientMetadataIsMasterState.setClientMetadata(opCtx()->getClient(),
std::move(newClientMetadata.getValue()));
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.stashTransactionResources(opCtx());
// LastClientInfo's clientMetadata should have been updated to the new ClientMetadata object.
- lastClientInfo = txnParticipant->getSingleTransactionStatsForTest().getLastClientInfo();
+ lastClientInfo = txnParticipant.getSingleTransactionStatsForTest().getLastClientInfo();
ASSERT_EQ(lastClientInfo.appName, "newAppName");
ASSERT_BSONOBJ_EQ(lastClientInfo.clientMetadata, newObj.getField("client").Obj());
}
@@ -2941,13 +2605,13 @@ TEST_F(TransactionsMetricsTest, LastClientInfoShouldUpdateUponCommit) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
// The transaction machinery cannot store an empty locker.
Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
- txnParticipant->commitUnpreparedTransaction(opCtx());
+ txnParticipant.commitUnpreparedTransaction(opCtx());
// LastClientInfo should have been set.
- auto lastClientInfo = txnParticipant->getSingleTransactionStatsForTest().getLastClientInfo();
+ auto lastClientInfo = txnParticipant.getSingleTransactionStatsForTest().getLastClientInfo();
ASSERT_EQ(lastClientInfo.clientHostAndPort, "");
ASSERT_EQ(lastClientInfo.connectionId, 0);
ASSERT_EQ(lastClientInfo.appName, "appName");
@@ -2965,11 +2629,11 @@ TEST_F(TransactionsMetricsTest, LastClientInfoShouldUpdateUponAbort) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
- txnParticipant->abortActiveTransaction(opCtx());
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.abortActiveTransaction(opCtx());
// LastClientInfo should have been set.
- auto lastClientInfo = txnParticipant->getSingleTransactionStatsForTest().getLastClientInfo();
+ auto lastClientInfo = txnParticipant.getSingleTransactionStatsForTest().getLastClientInfo();
ASSERT_EQ(lastClientInfo.clientHostAndPort, "");
ASSERT_EQ(lastClientInfo.connectionId, 0);
ASSERT_EQ(lastClientInfo.appName, "appName");
@@ -3023,33 +2687,33 @@ void buildSingleTransactionStatsString(StringBuilder* sb, const int metricValue)
* Builds the time active and time inactive info string.
*/
void buildTimeActiveInactiveString(StringBuilder* sb,
- TransactionParticipant* txnParticipant,
+ TransactionParticipant::Participant txnParticipant,
TickSource* tickSource,
TickSource::Tick curTick) {
// Add time active micros to string.
(*sb) << " timeActiveMicros:"
<< durationCount<Microseconds>(
- txnParticipant->getSingleTransactionStatsForTest().getTimeActiveMicros(tickSource,
- curTick));
+ txnParticipant.getSingleTransactionStatsForTest().getTimeActiveMicros(tickSource,
+ curTick));
// Add time inactive micros to string.
(*sb) << " timeInactiveMicros:"
<< durationCount<Microseconds>(
- txnParticipant->getSingleTransactionStatsForTest().getTimeInactiveMicros(
- tickSource, curTick));
+ txnParticipant.getSingleTransactionStatsForTest().getTimeInactiveMicros(tickSource,
+ curTick));
}
/*
* Builds the total prepared duration info string.
*/
void buildPreparedDurationString(StringBuilder* sb,
- TransactionParticipant* txnParticipant,
+ TransactionParticipant::Participant txnParticipant,
TickSource* tickSource,
TickSource::Tick curTick) {
(*sb) << " totalPreparedDurationMicros:"
<< durationCount<Microseconds>(
- txnParticipant->getSingleTransactionStatsForTest().getPreparedDuration(tickSource,
- curTick));
+ txnParticipant.getSingleTransactionStatsForTest().getPreparedDuration(tickSource,
+ curTick));
}
/*
@@ -3057,7 +2721,7 @@ void buildPreparedDurationString(StringBuilder* sb,
*/
std::string buildTransactionInfoString(
OperationContext* opCtx,
- TransactionParticipant* txnParticipant,
+ TransactionParticipant::Participant txnParticipant,
std::string terminationCause,
const LogicalSessionId sessionId,
const TxnNumber txnNum,
@@ -3088,7 +2752,7 @@ std::string buildTransactionInfoString(
StringBuilder readTimestampInfo;
readTimestampInfo
<< " readTimestamp:"
- << txnParticipant->getSpeculativeTransactionReadOpTimeForTest().getTimestamp().toString()
+ << txnParticipant.getSpeculativeTransactionReadOpTimeForTest().getTimestamp().toString()
<< ",";
StringBuilder singleTransactionStatsInfo;
@@ -3129,24 +2793,24 @@ std::string buildTransactionInfoString(
expectedTransactionInfo << totalPreparedDuration.str();
expectedTransactionInfo << " prepareOpTime:"
<< (prepareOpTime ? prepareOpTime->toString()
- : txnParticipant->getPrepareOpTime().toString());
+ : txnParticipant.getPrepareOpTime().toString());
}
- if (txnParticipant->getOldestOplogEntryOpTimeForTest()) {
+ if (txnParticipant.getOldestOplogEntryOpTimeForTest()) {
ASSERT(!oldestOplogEntryOpTime);
expectedTransactionInfo << " oldestOplogEntryOpTime:"
- << txnParticipant->getOldestOplogEntryOpTimeForTest()->toString();
+ << txnParticipant.getOldestOplogEntryOpTimeForTest()->toString();
}
if (oldestOplogEntryOpTime) {
- ASSERT(!txnParticipant->getOldestOplogEntryOpTimeForTest());
+ ASSERT(!txnParticipant.getOldestOplogEntryOpTimeForTest());
expectedTransactionInfo << " oldestOplogEntryOpTime:" << oldestOplogEntryOpTime->toString();
}
- if (txnParticipant->getFinishOpTimeForTest()) {
+ if (txnParticipant.getFinishOpTimeForTest()) {
expectedTransactionInfo << " finishOpTime:"
- << txnParticipant->getFinishOpTimeForTest()->toString();
+ << txnParticipant.getFinishOpTimeForTest()->toString();
}
expectedTransactionInfo << ", "
<< duration_cast<Milliseconds>(
- txnParticipant->getSingleTransactionStatsForTest().getDuration(
+ txnParticipant.getSingleTransactionStatsForTest().getDuration(
tickSource, tickSource->getTicks()));
return expectedTransactionInfo.str();
}
@@ -3169,13 +2833,13 @@ TEST_F(TransactionsMetricsTest, TestTransactionInfoForLogAfterCommit) {
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
- txnParticipant->commitUnpreparedTransaction(opCtx());
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.commitUnpreparedTransaction(opCtx());
const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none);
ASSERT(lockerInfo);
- std::string testTransactionInfo =
- txnParticipant->getTransactionInfoForLogForTest(&lockerInfo->stats, true, readConcernArgs);
+ std::string testTransactionInfo = txnParticipant.getTransactionInfoForLogForTest(
+ opCtx(), &lockerInfo->stats, true, readConcernArgs);
std::string expectedTransactionInfo =
buildTransactionInfoString(opCtx(),
@@ -3209,19 +2873,19 @@ TEST_F(TransactionsMetricsTest, TestPreparedTransactionInfoForLogAfterCommit) {
// Prepare the transaction and extend the duration in the prepared state.
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
const auto commitTimestamp =
Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
tickSource->advance(Microseconds(10));
- txnParticipant->commitPreparedTransaction(opCtx(), commitTimestamp, {});
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTimestamp, {});
const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none);
ASSERT(lockerInfo);
- std::string testTransactionInfo =
- txnParticipant->getTransactionInfoForLogForTest(&lockerInfo->stats, true, readConcernArgs);
+ std::string testTransactionInfo = txnParticipant.getTransactionInfoForLogForTest(
+ opCtx(), &lockerInfo->stats, true, readConcernArgs);
std::string expectedTransactionInfo =
buildTransactionInfoString(opCtx(),
@@ -3252,14 +2916,14 @@ TEST_F(TransactionsMetricsTest, TestTransactionInfoForLogAfterAbort) {
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
- txnParticipant->abortActiveTransaction(opCtx());
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.abortActiveTransaction(opCtx());
const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none);
ASSERT(lockerInfo);
- std::string testTransactionInfo =
- txnParticipant->getTransactionInfoForLogForTest(&lockerInfo->stats, false, readConcernArgs);
+ std::string testTransactionInfo = txnParticipant.getTransactionInfoForLogForTest(
+ opCtx(), &lockerInfo->stats, false, readConcernArgs);
std::string expectedTransactionInfo =
buildTransactionInfoString(opCtx(),
@@ -3293,17 +2957,17 @@ TEST_F(TransactionsMetricsTest, TestPreparedTransactionInfoForLogAfterAbort) {
// Prepare the transaction and extend the duration in the prepared state.
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.prepareTransaction(opCtx(), {});
tickSource->advance(Microseconds(10));
- txnParticipant->abortActiveTransaction(opCtx());
+ txnParticipant.abortActiveTransaction(opCtx());
const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none);
ASSERT(lockerInfo);
- std::string testTransactionInfo =
- txnParticipant->getTransactionInfoForLogForTest(&lockerInfo->stats, false, readConcernArgs);
+ std::string testTransactionInfo = txnParticipant.getTransactionInfoForLogForTest(
+ opCtx(), &lockerInfo->stats, false, readConcernArgs);
std::string expectedTransactionInfo =
buildTransactionInfoString(opCtx(),
@@ -3334,10 +2998,10 @@ DEATH_TEST_F(TransactionsMetricsTest, TestTransactionInfoForLogWithNoLockerInfoS
const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none);
ASSERT(lockerInfo);
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
- txnParticipant->commitUnpreparedTransaction(opCtx());
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.commitUnpreparedTransaction(opCtx());
- txnParticipant->getTransactionInfoForLogForTest(nullptr, true, readConcernArgs);
+ txnParticipant.getTransactionInfoForLogForTest(opCtx(), nullptr, true, readConcernArgs);
}
TEST_F(TransactionsMetricsTest, LogTransactionInfoAfterSlowCommit) {
@@ -3359,19 +3023,20 @@ TEST_F(TransactionsMetricsTest, LogTransactionInfoAfterSlowCommit) {
const int metricValue = 1;
setupAdditiveMetrics(metricValue, opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
serverGlobalParams.slowMS = 10;
tickSource->advance(Microseconds(11 * 1000));
startCapturingLogMessages();
- txnParticipant->commitUnpreparedTransaction(opCtx());
+ txnParticipant.commitUnpreparedTransaction(opCtx());
stopCapturingLogMessages();
const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none);
ASSERT(lockerInfo);
std::string expectedTransactionInfo = "transaction " +
- txnParticipant->getTransactionInfoForLogForTest(&lockerInfo->stats, true, readConcernArgs);
+ txnParticipant.getTransactionInfoForLogForTest(
+ opCtx(), &lockerInfo->stats, true, readConcernArgs);
ASSERT_EQUALS(1, countLogLinesContaining(expectedTransactionInfo));
}
@@ -3397,19 +3062,20 @@ TEST_F(TransactionsMetricsTest, LogPreparedTransactionInfoAfterSlowCommit) {
serverGlobalParams.slowMS = 10;
tickSource->advance(Microseconds(11 * 1000));
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
const auto commitTimestamp =
Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
startCapturingLogMessages();
- txnParticipant->commitPreparedTransaction(opCtx(), commitTimestamp, {});
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTimestamp, {});
stopCapturingLogMessages();
const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none);
ASSERT(lockerInfo);
std::string expectedTransactionInfo = "transaction " +
- txnParticipant->getTransactionInfoForLogForTest(&lockerInfo->stats, true, readConcernArgs);
+ txnParticipant.getTransactionInfoForLogForTest(
+ opCtx(), &lockerInfo->stats, true, readConcernArgs);
ASSERT_EQUALS(1, countLogLinesContaining(expectedTransactionInfo));
}
@@ -3432,13 +3098,13 @@ TEST_F(TransactionsMetricsTest, LogTransactionInfoAfterSlowAbort) {
const int metricValue = 1;
setupAdditiveMetrics(metricValue, opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
serverGlobalParams.slowMS = 10;
tickSource->advance(Microseconds(11 * 1000));
startCapturingLogMessages();
- txnParticipant->abortActiveTransaction(opCtx());
+ txnParticipant.abortActiveTransaction(opCtx());
stopCapturingLogMessages();
const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none);
@@ -3475,15 +3141,15 @@ TEST_F(TransactionsMetricsTest, LogPreparedTransactionInfoAfterSlowAbort) {
const int metricValue = 1;
setupAdditiveMetrics(metricValue, opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.prepareTransaction(opCtx(), {});
serverGlobalParams.slowMS = 10;
tickSource->advance(Microseconds(11 * 1000));
- auto prepareOpTime = txnParticipant->getPrepareOpTime();
+ auto prepareOpTime = txnParticipant.getPrepareOpTime();
startCapturingLogMessages();
- txnParticipant->abortActiveTransaction(opCtx());
+ txnParticipant.abortActiveTransaction(opCtx());
stopCapturingLogMessages();
const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none);
@@ -3522,18 +3188,18 @@ TEST_F(TransactionsMetricsTest, LogTransactionInfoAfterExceptionInPrepare) {
const int metricValue = 1;
setupAdditiveMetrics(metricValue, opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
serverGlobalParams.slowMS = 10;
tickSource->advance(Microseconds(11 * 1000));
_opObserver->onTransactionPrepareThrowsException = true;
startCapturingLogMessages();
- ASSERT_THROWS_CODE(txnParticipant->prepareTransaction(opCtx(), {}),
+ ASSERT_THROWS_CODE(txnParticipant.prepareTransaction(opCtx(), {}),
AssertionException,
ErrorCodes::OperationFailed);
ASSERT_FALSE(_opObserver->transactionPrepared);
- ASSERT(txnParticipant->transactionIsAborted());
+ ASSERT(txnParticipant.transactionIsAborted());
stopCapturingLogMessages();
const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none);
@@ -3569,12 +3235,12 @@ TEST_F(TransactionsMetricsTest, LogTransactionInfoAfterSlowStashedAbort) {
const int metricValue = 1;
setupAdditiveMetrics(metricValue, opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
{ Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); }
- txnParticipant->stashTransactionResources(opCtx());
- const auto txnResourceStashLocker = txnParticipant->getTxnResourceStashLockerForTest();
+ txnParticipant.stashTransactionResources(opCtx());
+ const auto txnResourceStashLocker = txnParticipant.getTxnResourceStashLockerForTest();
ASSERT(txnResourceStashLocker);
const auto lockerInfo = txnResourceStashLocker->getLockerInfo(boost::none);
@@ -3582,7 +3248,7 @@ TEST_F(TransactionsMetricsTest, LogTransactionInfoAfterSlowStashedAbort) {
tickSource->advance(Microseconds(11 * 1000));
startCapturingLogMessages();
- txnParticipant->abortArbitraryTransaction();
+ txnParticipant.abortTransactionIfNotPrepared(opCtx());
stopCapturingLogMessages();
std::string expectedTransactionInfo = "transaction parameters";
@@ -3596,17 +3262,17 @@ TEST_F(TxnParticipantTest, WhenOldestTSRemovedNextOldestBecomesNewOldest) {
// Check that there are no Timestamps in the set.
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalActiveOpTimes(), 0U);
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
- auto firstPrepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
+ auto firstPrepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
// Check that we added a Timestamp to the set.
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalActiveOpTimes(), 1U);
// Check that the oldest prepareTimestamp is equal to firstPrepareTimestamp because there is
// only one prepared transaction on this Service.
auto prepareOpTime = ServerTransactionsMetrics::get(opCtx())->getOldestActiveOpTime();
ASSERT_EQ(prepareOpTime->getTimestamp(), firstPrepareTimestamp);
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
+ ASSERT_FALSE(txnParticipant.transactionIsAborted());
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
auto originalClient = Client::releaseCurrent();
/**
@@ -3628,19 +3294,19 @@ TEST_F(TxnParticipantTest, WhenOldestTSRemovedNextOldestBecomesNewOldest) {
MongoDOperationContextSession newOpCtxSession(newOpCtx.get());
auto newTxnParticipant = TransactionParticipant::get(newOpCtx.get());
- newTxnParticipant->beginOrContinue(newTxnNum, false, true);
- newTxnParticipant->unstashTransactionResources(newOpCtx.get(), "prepareTransaction");
+ newTxnParticipant.beginOrContinue(newOpCtx.get(), newTxnNum, false, true);
+ newTxnParticipant.unstashTransactionResources(newOpCtx.get(), "prepareTransaction");
// secondPrepareTimestamp should be greater than firstPreparedTimestamp because this
// transaction was prepared after.
- secondPrepareTimestamp = newTxnParticipant->prepareTransaction(newOpCtx.get(), {});
+ secondPrepareTimestamp = newTxnParticipant.prepareTransaction(newOpCtx.get(), {});
ASSERT_GT(secondPrepareTimestamp, firstPrepareTimestamp);
// Check that we added a Timestamp to the set.
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalActiveOpTimes(), 2U);
// The oldest prepareTimestamp should still be firstPrepareTimestamp.
prepareOpTime = ServerTransactionsMetrics::get(opCtx())->getOldestActiveOpTime();
ASSERT_EQ(prepareOpTime->getTimestamp(), firstPrepareTimestamp);
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
+ ASSERT_FALSE(txnParticipant.transactionIsAborted());
}
Client::releaseCurrent();
@@ -3648,9 +3314,9 @@ TEST_F(TxnParticipantTest, WhenOldestTSRemovedNextOldestBecomesNewOldest) {
// Switch clients and abort the first transaction. This should cause the oldestActiveTS to be
// equal to the secondPrepareTimestamp.
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT(txnParticipant->transactionIsAborted());
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.abortActiveTransaction(opCtx());
+ ASSERT(txnParticipant.transactionIsAborted());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalActiveOpTimes(), 1U);
prepareOpTime = ServerTransactionsMetrics::get(opCtx())->getOldestActiveOpTime();
ASSERT_EQ(prepareOpTime->getTimestamp(), secondPrepareTimestamp);
@@ -3663,13 +3329,13 @@ TEST_F(TxnParticipantTest, ReturnNullTimestampIfNoOldestActiveTimestamp) {
// Check that there are no Timestamps in the set.
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalActiveOpTimes(), 0U);
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
- txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.prepareTransaction(opCtx(), {});
// Check that we added a Timestamp to the set.
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalActiveOpTimes(), 1U);
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
+ ASSERT_FALSE(txnParticipant.transactionIsAborted());
- txnParticipant->stashTransactionResources(opCtx());
+ txnParticipant.stashTransactionResources(opCtx());
auto originalClient = Client::releaseCurrent();
/**
@@ -3690,20 +3356,20 @@ TEST_F(TxnParticipantTest, ReturnNullTimestampIfNoOldestActiveTimestamp) {
MongoDOperationContextSession newOpCtxSession(newOpCtx.get());
auto newTxnParticipant = TransactionParticipant::get(newOpCtx.get());
- newTxnParticipant->beginOrContinue(newTxnNum, false, true);
- newTxnParticipant->unstashTransactionResources(newOpCtx.get(), "prepareTransaction");
+ newTxnParticipant.beginOrContinue(newOpCtx.get(), newTxnNum, false, true);
+ newTxnParticipant.unstashTransactionResources(newOpCtx.get(), "prepareTransaction");
// secondPrepareTimestamp should be greater than firstPreparedTimestamp because this
// transaction was prepared after.
- newTxnParticipant->prepareTransaction(newOpCtx.get(), {});
+ newTxnParticipant.prepareTransaction(newOpCtx.get(), {});
// Check that we added a Timestamp to the set.
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalActiveOpTimes(), 2U);
// The oldest prepareTimestamp should still be firstPrepareTimestamp.
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
+ ASSERT_FALSE(txnParticipant.transactionIsAborted());
// Abort this transaction and check that we have decremented the total active timestamps
// count.
- newTxnParticipant->abortActiveTransaction(newOpCtx.get());
+ newTxnParticipant.abortActiveTransaction(newOpCtx.get());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalActiveOpTimes(), 1U);
}
@@ -3712,9 +3378,9 @@ TEST_F(TxnParticipantTest, ReturnNullTimestampIfNoOldestActiveTimestamp) {
// Switch clients and abort the first transaction. This means we no longer have an oldest active
// timestamp.
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT(txnParticipant->transactionIsAborted());
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
+ txnParticipant.abortActiveTransaction(opCtx());
+ ASSERT(txnParticipant.transactionIsAborted());
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalActiveOpTimes(), 0U);
ASSERT_FALSE(ServerTransactionsMetrics::get(opCtx())->getOldestActiveOpTime());
}
@@ -3726,8 +3392,8 @@ TEST_F(TxnParticipantTest, ProperlyMaintainOldestNonMajorityCommittedOpTimeSet)
// Check that there are no Timestamps in the set.
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalActiveOpTimes(), 0U);
- txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction");
- auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ txnParticipant.unstashTransactionResources(opCtx(), "prepareTransaction");
+ auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
// Check that we added a Timestamp to the set.
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalActiveOpTimes(), 1U);
@@ -3746,14 +3412,14 @@ TEST_F(TxnParticipantTest, ProperlyMaintainOldestNonMajorityCommittedOpTimeSet)
ServerTransactionsMetrics::get(opCtx())->getFinishOpTimeOfOldestNonMajCommitted_forTest();
ASSERT_EQ(nonMajorityCommittedOpTimeFinishOpTime->getTimestamp(), Timestamp::max());
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
+ ASSERT_FALSE(txnParticipant.transactionIsAborted());
// Since this test uses a mock opObserver, we have to manually set the finishTimestamp on the
// txnParticipant.
auto finishOpTime = repl::OpTime({10, 10}, 0);
repl::ReplClientInfo::forClient(opCtx()->getClient()).setLastOp(finishOpTime);
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT(txnParticipant->transactionIsAborted());
+ txnParticipant.abortActiveTransaction(opCtx());
+ ASSERT(txnParticipant.transactionIsAborted());
// Make sure that we moved the OpTime from the oldestActiveOplogEntryOpTimes to
// oldestNonMajorityCommittedOpTimes along with the abort/commit oplog entry OpTime
@@ -3860,13 +3526,13 @@ TEST_F(TxnParticipantTest, RollbackResetsInMemoryStateOfPreparedTransaction) {
ASSERT_FALSE(ServerTransactionsMetrics::get(opCtx())->getOldestNonMajorityCommittedOpTime());
// Perform an insert as a part of a transaction so that we have a transaction operation.
- txnParticipant->unstashTransactionResources(opCtx(), "insert");
+ txnParticipant.unstashTransactionResources(opCtx(), "insert");
auto operation = repl::OplogEntry::makeInsertOperation(kNss, kUUID, BSON("TestValue" << 0));
- txnParticipant->addTransactionOperation(opCtx(), operation);
+ txnParticipant.addTransactionOperation(opCtx(), operation);
ASSERT_BSONOBJ_EQ(operation.toBSON(),
- txnParticipant->getTransactionOperationsForTest()[0].toBSON());
+ txnParticipant.getTransactionOperationsForTest()[0].toBSON());
- auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {});
+ auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), {});
// Check that we added a Timestamp to oldestActiveOplogEntryOpTimes.
ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalActiveOpTimes(), 1U);
@@ -3878,23 +3544,23 @@ TEST_F(TxnParticipantTest, RollbackResetsInMemoryStateOfPreparedTransaction) {
ServerTransactionsMetrics::get(opCtx())->getOldestNonMajorityCommittedOpTime();
ASSERT_EQ(oldestActiveOpTime->getTimestamp(), prepareTimestamp);
ASSERT_EQ(oldestNonMajorityCommittedOpTime->getTimestamp(), prepareTimestamp);
- ASSERT_FALSE(txnParticipant->transactionIsAborted());
+ ASSERT_FALSE(txnParticipant.transactionIsAborted());
// Make sure the state of txnParticipant is populated correctly after a prepared transaction.
- ASSERT(txnParticipant->transactionIsPrepared());
- ASSERT_EQ(txnParticipant->getTransactionOperationsForTest().size(), 1U);
- ASSERT_EQ(txnParticipant->getPrepareOpTime().getTimestamp(), prepareTimestamp);
- ASSERT_NE(txnParticipant->getActiveTxnNumber(), kUninitializedTxnNumber);
+ ASSERT(txnParticipant.transactionIsPrepared());
+ ASSERT_EQ(txnParticipant.getTransactionOperationsForTest().size(), 1U);
+ ASSERT_EQ(txnParticipant.getPrepareOpTime().getTimestamp(), prepareTimestamp);
+ ASSERT_NE(txnParticipant.getActiveTxnNumber(), kUninitializedTxnNumber);
- txnParticipant->abortPreparedTransactionForRollback();
+ txnParticipant.abortPreparedTransactionForRollback(opCtx());
ServerTransactionsMetrics::get(opCtx())->clearOpTimes();
// After calling abortPreparedTransactionForRollback, the state of txnParticipant should be
// invalidated.
- ASSERT_FALSE(txnParticipant->transactionIsPrepared());
- ASSERT_EQ(txnParticipant->getTransactionOperationsForTest().size(), 0U);
- ASSERT_EQ(txnParticipant->getPrepareOpTime().getTimestamp(), Timestamp());
- ASSERT_EQ(txnParticipant->getActiveTxnNumber(), kUninitializedTxnNumber);
+ ASSERT_FALSE(txnParticipant.transactionIsPrepared());
+ ASSERT_EQ(txnParticipant.getTransactionOperationsForTest().size(), 0U);
+ ASSERT_EQ(txnParticipant.getPrepareOpTime().getTimestamp(), Timestamp());
+ ASSERT_EQ(txnParticipant.getActiveTxnNumber(), kUninitializedTxnNumber);
// After calling clearOpTimes, we should no longer have an oldestActiveOpTime.
ASSERT_FALSE(ServerTransactionsMetrics::get(opCtx())->getOldestActiveOpTime());
@@ -3907,18 +3573,18 @@ TEST_F(TxnParticipantTest, PrepareTransactionAsSecondarySetsThePrepareOpTime) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), prepareOpTime);
- ASSERT(txnParticipant->transactionIsPrepared());
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), prepareOpTime);
+ ASSERT(txnParticipant.transactionIsPrepared());
ASSERT_EQ(prepareTimestamp, prepareOpTime.getTimestamp());
- ASSERT_EQ(txnParticipant->getPrepareOpTime(), prepareOpTime);
+ ASSERT_EQ(txnParticipant.getPrepareOpTime(), prepareOpTime);
// If _prepareOptime was not set and was null, then commitPreparedTransaction would falsely
// succeed everytime. We set the commitTimestamp to be less than the prepareTimestamp to make
// sure this is not the case.
const auto commitTimestamp =
Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() - 1);
- ASSERT_THROWS_CODE(txnParticipant->commitPreparedTransaction(opCtx(), commitTimestamp, {}),
+ ASSERT_THROWS_CODE(txnParticipant.commitPreparedTransaction(opCtx(), commitTimestamp, {}),
AssertionException,
ErrorCodes::InvalidOptions);
}
@@ -3932,18 +3598,18 @@ TEST_F(TxnParticipantTest, CommitPreparedTransactionAsSecondarySetsTheFinishOpTi
repl::UnreplicatedWritesBlock uwb(opCtx());
ASSERT(!opCtx()->writesAreReplicated());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), prepareOpTime);
- ASSERT(txnParticipant->transactionIsPrepared());
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), prepareOpTime);
+ ASSERT(txnParticipant.transactionIsPrepared());
ASSERT_EQ(prepareTimestamp, prepareOpTime.getTimestamp());
- ASSERT_EQ(txnParticipant->getPrepareOpTime(), prepareOpTime);
+ ASSERT_EQ(txnParticipant.getPrepareOpTime(), prepareOpTime);
const auto commitTimestamp =
Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
const auto commitOplogEntryOpTime = repl::OpTime({10, 10}, 0);
- txnParticipant->commitPreparedTransaction(opCtx(), commitTimestamp, commitOplogEntryOpTime);
- ASSERT_EQ(txnParticipant->getFinishOpTimeForTest().get(), commitOplogEntryOpTime);
- ASSERT_TRUE(txnParticipant->transactionIsCommitted());
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTimestamp, commitOplogEntryOpTime);
+ ASSERT_EQ(txnParticipant.getFinishOpTimeForTest().get(), commitOplogEntryOpTime);
+ ASSERT_TRUE(txnParticipant.transactionIsCommitted());
}
DEATH_TEST_F(TxnParticipantTest,
@@ -3957,15 +3623,15 @@ DEATH_TEST_F(TxnParticipantTest,
repl::UnreplicatedWritesBlock uwb(opCtx());
ASSERT(!opCtx()->writesAreReplicated());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), prepareOpTime);
- ASSERT(txnParticipant->transactionIsPrepared());
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), prepareOpTime);
+ ASSERT(txnParticipant.transactionIsPrepared());
ASSERT_EQ(prepareTimestamp, prepareOpTime.getTimestamp());
- ASSERT_EQ(txnParticipant->getPrepareOpTime(), prepareOpTime);
+ ASSERT_EQ(txnParticipant.getPrepareOpTime(), prepareOpTime);
const auto commitTimestamp =
Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
- txnParticipant->commitPreparedTransaction(opCtx(), commitTimestamp, {});
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTimestamp, {});
}
DEATH_TEST_F(TxnParticipantTest,
@@ -3975,16 +3641,16 @@ DEATH_TEST_F(TxnParticipantTest,
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction");
- const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), prepareOpTime);
- ASSERT(txnParticipant->transactionIsPrepared());
+ txnParticipant.unstashTransactionResources(opCtx(), "commitTransaction");
+ const auto prepareTimestamp = txnParticipant.prepareTransaction(opCtx(), prepareOpTime);
+ ASSERT(txnParticipant.transactionIsPrepared());
ASSERT_EQ(prepareTimestamp, prepareOpTime.getTimestamp());
- ASSERT_EQ(txnParticipant->getPrepareOpTime(), prepareOpTime);
+ ASSERT_EQ(txnParticipant.getPrepareOpTime(), prepareOpTime);
const auto commitTimestamp =
Timestamp(prepareTimestamp.getSecs(), prepareTimestamp.getInc() + 1);
const auto commitOplogEntryOpTime = repl::OpTime({10, 10}, 0);
- txnParticipant->commitPreparedTransaction(opCtx(), commitTimestamp, commitOplogEntryOpTime);
+ txnParticipant.commitPreparedTransaction(opCtx(), commitTimestamp, commitOplogEntryOpTime);
}
TEST_F(TxnParticipantTest, AbortTransactionOnSessionCheckoutWithoutRefresh) {
@@ -4004,12 +3670,12 @@ TEST_F(TxnParticipantTest, AbortTransactionOnSessionCheckoutWithoutRefresh) {
MongoDOperationContextSessionWithoutRefresh sessionCheckout(opCtx());
auto txnParticipant = TransactionParticipant::get(opCtx());
- ASSERT(txnParticipant->inMultiDocumentTransaction());
- ASSERT_EQ(txnParticipant->getActiveTxnNumber(), txnNumber);
+ ASSERT(txnParticipant.inMultiDocumentTransaction());
+ ASSERT_EQ(txnParticipant.getActiveTxnNumber(), txnNumber);
- txnParticipant->unstashTransactionResources(opCtx(), "abortTransaction");
- txnParticipant->abortActiveTransaction(opCtx());
- ASSERT_TRUE(txnParticipant->transactionIsAborted());
+ txnParticipant.unstashTransactionResources(opCtx(), "abortTransaction");
+ txnParticipant.abortActiveTransaction(opCtx());
+ ASSERT_TRUE(txnParticipant.transactionIsAborted());
}
} // namespace
diff --git a/src/mongo/dbtests/storage_timestamp_tests.cpp b/src/mongo/dbtests/storage_timestamp_tests.cpp
index 3572d305c92..543f95616dc 100644
--- a/src/mongo/dbtests/storage_timestamp_tests.cpp
+++ b/src/mongo/dbtests/storage_timestamp_tests.cpp
@@ -2638,14 +2638,14 @@ public:
auto txnParticipant = TransactionParticipant::get(_opCtx);
ASSERT(txnParticipant);
- txnParticipant->beginOrContinue(
- *_opCtx->getTxnNumber(), false /* autocommit */, true /* startTransaction */);
- txnParticipant->unstashTransactionResources(_opCtx, "insert");
+ txnParticipant.beginOrContinue(
+ _opCtx, *_opCtx->getTxnNumber(), false /* autocommit */, true /* startTransaction */);
+ txnParticipant.unstashTransactionResources(_opCtx, "insert");
{
AutoGetCollection autoColl(_opCtx, nss, LockMode::MODE_IX, LockMode::MODE_IX);
insertDocument(autoColl.getCollection(), InsertStatement(doc));
}
- txnParticipant->stashTransactionResources(_opCtx);
+ txnParticipant.stashTransactionResources(_opCtx);
{
AutoGetCollection autoColl(_opCtx, nss, LockMode::MODE_IS, LockMode::MODE_IS);
@@ -2687,11 +2687,11 @@ public:
ASSERT(txnParticipant);
logTimestamps();
- txnParticipant->unstashTransactionResources(_opCtx, "insert");
+ txnParticipant.unstashTransactionResources(_opCtx, "insert");
- txnParticipant->commitUnpreparedTransaction(_opCtx);
+ txnParticipant.commitUnpreparedTransaction(_opCtx);
- txnParticipant->stashTransactionResources(_opCtx);
+ txnParticipant.stashTransactionResources(_opCtx);
{
AutoGetCollection autoColl(_opCtx, nss, LockMode::MODE_X, LockMode::MODE_IX);
auto coll = autoColl.getCollection();
@@ -2746,11 +2746,11 @@ public:
assertOplogDocumentExistsAtTimestamp(commitFilter, commitEntryTs, false);
assertOplogDocumentExistsAtTimestamp(commitFilter, commitTimestamp, false);
}
- txnParticipant->unstashTransactionResources(_opCtx, "insert");
+ txnParticipant.unstashTransactionResources(_opCtx, "insert");
- txnParticipant->prepareTransaction(_opCtx, {});
+ txnParticipant.prepareTransaction(_opCtx, {});
- txnParticipant->stashTransactionResources(_opCtx);
+ txnParticipant.stashTransactionResources(_opCtx);
{
const auto prepareFilter = BSON("ts" << prepareTs);
assertOplogDocumentExistsAtTimestamp(prepareFilter, presentTs, false);
@@ -2768,11 +2768,11 @@ public:
assertOplogDocumentExistsAtTimestamp(commitFilter, commitTimestamp, false);
assertOplogDocumentExistsAtTimestamp(commitFilter, nullTs, false);
}
- txnParticipant->unstashTransactionResources(_opCtx, "commitTransaction");
+ txnParticipant.unstashTransactionResources(_opCtx, "commitTransaction");
- txnParticipant->commitPreparedTransaction(_opCtx, commitTimestamp, {});
+ txnParticipant.commitPreparedTransaction(_opCtx, commitTimestamp, {});
- txnParticipant->stashTransactionResources(_opCtx);
+ txnParticipant.stashTransactionResources(_opCtx);
{
AutoGetCollection autoColl(_opCtx, nss, LockMode::MODE_X, LockMode::MODE_IX);
auto coll = autoColl.getCollection();