summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo')
-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();