diff options
-rw-r--r-- | src/mongo/db/single_transaction_stats.cpp | 17 | ||||
-rw-r--r-- | src/mongo/db/single_transaction_stats.h | 6 | ||||
-rw-r--r-- | src/mongo/db/transaction_participant.cpp | 21 | ||||
-rw-r--r-- | src/mongo/db/transaction_participant.h | 8 | ||||
-rw-r--r-- | src/mongo/db/transaction_participant_test.cpp | 72 |
5 files changed, 81 insertions, 43 deletions
diff --git a/src/mongo/db/single_transaction_stats.cpp b/src/mongo/db/single_transaction_stats.cpp index 0660752e41e..e0137d63148 100644 --- a/src/mongo/db/single_transaction_stats.cpp +++ b/src/mongo/db/single_transaction_stats.cpp @@ -55,13 +55,16 @@ Microseconds SingleTransactionStats::getDuration(TickSource* tickSource, Microseconds SingleTransactionStats::getPreparedDuration(TickSource* tickSource, TickSource::Tick curTick) const { invariant(_startTime > 0); - invariant(_preparedStartTime > 0); - - // If the transaction hasn't ended yet, we return how long it has currently been running for. - if (_endTime == 0) { - return tickSource->ticksTo<Microseconds>(curTick - _preparedStartTime); + if (_preparedStartTime != boost::none) { + // If the transaction hasn't ended yet, we return how long it has currently been running + // for. + invariant(_preparedStartTime.get() > 0); + if (_endTime == 0) { + return tickSource->ticksTo<Microseconds>(curTick - _preparedStartTime.get()); + } + return tickSource->ticksTo<Microseconds>(_endTime - _preparedStartTime.get()); } - return tickSource->ticksTo<Microseconds>(_endTime - _preparedStartTime); + return Microseconds(0); } void SingleTransactionStats::setPreparedStartTime(TickSource::Tick time) { @@ -140,7 +143,7 @@ void SingleTransactionStats::report(BSONObjBuilder* builder, builder->append("timeActiveMicros", timeActive); builder->append("timeInactiveMicros", timeInactive); - if (_preparedStartTime > 0) { + if (_preparedStartTime != boost::none) { auto timePrepared = durationCount<Microseconds>(getPreparedDuration(tickSource, curTick)); builder->append("timePreparedMicros", timePrepared); } diff --git a/src/mongo/db/single_transaction_stats.h b/src/mongo/db/single_transaction_stats.h index 6c5bbe860c2..e13bdd8635c 100644 --- a/src/mongo/db/single_transaction_stats.h +++ b/src/mongo/db/single_transaction_stats.h @@ -259,8 +259,10 @@ private: // Holds information about the last client to run a transaction operation. LastClientInfo _lastClientInfo; - // The time at which a transaction enters the prepared state. - TickSource::Tick _preparedStartTime{0}; + // The time at which a transaction becomes prepared. It is possible for _preparedStartTime to + // not be set in a transaction that is in state kPrepared if an exception is thrown after the + // transaction transitions to the prepared state but before setPreparedStartTime is called. + boost::optional<TickSource::Tick> _preparedStartTime{boost::none}; }; } // namespace mongo diff --git a/src/mongo/db/transaction_participant.cpp b/src/mongo/db/transaction_participant.cpp index b8b76d1864d..c45caef9ed8 100644 --- a/src/mongo/db/transaction_participant.cpp +++ b/src/mongo/db/transaction_participant.cpp @@ -1434,8 +1434,7 @@ void TransactionParticipant::_reportTransactionStats(WithLock wl, std::string TransactionParticipant::_transactionInfoForLog( const SingleThreadedLockStats* lockStats, TransactionState::StateFlag terminationCause, - repl::ReadConcernArgs readConcernArgs, - bool wasPrepared) { + repl::ReadConcernArgs readConcernArgs) { invariant(lockStats); invariant(terminationCause == TransactionState::kCommitted || terminationCause == TransactionState::kAborted); @@ -1488,11 +1487,14 @@ std::string TransactionParticipant::_transactionInfoForLog( s << " " << duration_cast<Milliseconds>(singleTransactionStats.getDuration(tickSource, curTick)); - s << " wasPrepared:" << wasPrepared; - if (wasPrepared) { - s << " totalPreparedDurationMicros:" - << durationCount<Microseconds>( - singleTransactionStats.getPreparedDuration(tickSource, curTick)); + // It is possible for a slow transaction to have aborted in the prepared state if an + // exception was thrown before prepareTransaction succeeds. + const auto totalPreparedDuration = durationCount<Microseconds>( + singleTransactionStats.getPreparedDuration(tickSource, curTick)); + const bool txnWasPrepared = totalPreparedDuration > 0; + s << " wasPrepared:" << txnWasPrepared; + if (txnWasPrepared) { + s << " totalPreparedDurationMicros:" << totalPreparedDuration; } return s.str(); @@ -1508,10 +1510,9 @@ void TransactionParticipant::_logSlowTransaction(WithLock wl, // Log the transaction if its duration is longer than the slowMS command threshold. if (_transactionMetricsObserver.getSingleTransactionStats().getDuration( tickSource, tickSource->getTicks()) > Milliseconds(serverGlobalParams.slowMS)) { - bool wasPrepared = !_prepareOpTime.isNull(); log(logger::LogComponent::kTransaction) - << "transaction " << _transactionInfoForLog( - lockStats, terminationCause, readConcernArgs, wasPrepared); + << "transaction " + << _transactionInfoForLog(lockStats, terminationCause, readConcernArgs); } } } diff --git a/src/mongo/db/transaction_participant.h b/src/mongo/db/transaction_participant.h index e778de94cd3..e7c69a69c76 100644 --- a/src/mongo/db/transaction_participant.h +++ b/src/mongo/db/transaction_participant.h @@ -280,12 +280,11 @@ public: std::string transactionInfoForLogForTest(const SingleThreadedLockStats* lockStats, bool committed, - repl::ReadConcernArgs readConcernArgs, - bool wasPrepared) { + repl::ReadConcernArgs readConcernArgs) { stdx::lock_guard<stdx::mutex> lk(_mutex); TransactionState::StateFlag terminationCause = committed ? TransactionState::kCommitted : TransactionState::kAborted; - return _transactionInfoForLog(lockStats, terminationCause, readConcernArgs, wasPrepared); + return _transactionInfoForLog(lockStats, terminationCause, readConcernArgs); } /** @@ -695,8 +694,7 @@ private: // passed in order for this method to be called. std::string _transactionInfoForLog(const SingleThreadedLockStats* lockStats, TransactionState::StateFlag terminationCause, - repl::ReadConcernArgs readConcernArgs, - bool wasPrepared); + repl::ReadConcernArgs readConcernArgs); // 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. diff --git a/src/mongo/db/transaction_participant_test.cpp b/src/mongo/db/transaction_participant_test.cpp index 0f0eaa0c534..be01a7d2db2 100644 --- a/src/mongo/db/transaction_participant_test.cpp +++ b/src/mongo/db/transaction_participant_test.cpp @@ -3177,8 +3177,8 @@ TEST_F(TransactionsMetricsTest, TestTransactionInfoForLogAfterCommit) { const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none); ASSERT(lockerInfo); - std::string testTransactionInfo = txnParticipant->transactionInfoForLogForTest( - &lockerInfo->stats, true, readConcernArgs, false); + std::string testTransactionInfo = + txnParticipant->transactionInfoForLogForTest(&lockerInfo->stats, true, readConcernArgs); std::string expectedTransactionInfo = buildTransactionInfoString(opCtx(), @@ -3223,8 +3223,8 @@ TEST_F(TransactionsMetricsTest, TestPreparedTransactionInfoForLogAfterCommit) { const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none); ASSERT(lockerInfo); - std::string testTransactionInfo = txnParticipant->transactionInfoForLogForTest( - &lockerInfo->stats, true, readConcernArgs, true); + std::string testTransactionInfo = + txnParticipant->transactionInfoForLogForTest(&lockerInfo->stats, true, readConcernArgs); std::string expectedTransactionInfo = buildTransactionInfoString(opCtx(), @@ -3261,8 +3261,8 @@ TEST_F(TransactionsMetricsTest, TestTransactionInfoForLogAfterAbort) { const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none); ASSERT(lockerInfo); - std::string testTransactionInfo = txnParticipant->transactionInfoForLogForTest( - &lockerInfo->stats, false, readConcernArgs, false); + std::string testTransactionInfo = + txnParticipant->transactionInfoForLogForTest(&lockerInfo->stats, false, readConcernArgs); std::string expectedTransactionInfo = buildTransactionInfoString(opCtx(), @@ -3304,8 +3304,8 @@ TEST_F(TransactionsMetricsTest, TestPreparedTransactionInfoForLogAfterAbort) { const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none); ASSERT(lockerInfo); - std::string testTransactionInfo = txnParticipant->transactionInfoForLogForTest( - &lockerInfo->stats, false, readConcernArgs, true); + std::string testTransactionInfo = + txnParticipant->transactionInfoForLogForTest(&lockerInfo->stats, false, readConcernArgs); std::string expectedTransactionInfo = buildTransactionInfoString(opCtx(), @@ -3338,7 +3338,7 @@ DEATH_TEST_F(TransactionsMetricsTest, TestTransactionInfoForLogWithNoLockerInfoS txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction"); txnParticipant->commitUnpreparedTransaction(opCtx()); - txnParticipant->transactionInfoForLogForTest(nullptr, true, readConcernArgs, false); + txnParticipant->transactionInfoForLogForTest(nullptr, true, readConcernArgs); } TEST_F(TransactionsMetricsTest, LogTransactionInfoAfterSlowCommit) { @@ -3372,8 +3372,7 @@ TEST_F(TransactionsMetricsTest, LogTransactionInfoAfterSlowCommit) { const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none); ASSERT(lockerInfo); std::string expectedTransactionInfo = "transaction " + - txnParticipant->transactionInfoForLogForTest( - &lockerInfo->stats, true, readConcernArgs, false); + txnParticipant->transactionInfoForLogForTest(&lockerInfo->stats, true, readConcernArgs); ASSERT_EQUALS(1, countLogLinesContaining(expectedTransactionInfo)); } @@ -3411,8 +3410,7 @@ TEST_F(TransactionsMetricsTest, LogPreparedTransactionInfoAfterSlowCommit) { const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none); ASSERT(lockerInfo); std::string expectedTransactionInfo = "transaction " + - txnParticipant->transactionInfoForLogForTest( - &lockerInfo->stats, true, readConcernArgs, true); + txnParticipant->transactionInfoForLogForTest(&lockerInfo->stats, true, readConcernArgs); ASSERT_EQUALS(1, countLogLinesContaining(expectedTransactionInfo)); } @@ -3447,8 +3445,7 @@ TEST_F(TransactionsMetricsTest, LogTransactionInfoAfterSlowAbort) { const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none); ASSERT(lockerInfo); std::string expectedTransactionInfo = "transaction " + - txnParticipant->transactionInfoForLogForTest( - &lockerInfo->stats, false, readConcernArgs, false); + txnParticipant->transactionInfoForLogForTest(&lockerInfo->stats, false, readConcernArgs); ASSERT_EQUALS(1, countLogLinesContaining(expectedTransactionInfo)); } @@ -3484,8 +3481,46 @@ TEST_F(TransactionsMetricsTest, LogPreparedTransactionInfoAfterSlowAbort) { const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none); ASSERT(lockerInfo); std::string expectedTransactionInfo = "transaction " + - txnParticipant->transactionInfoForLogForTest( - &lockerInfo->stats, false, readConcernArgs, true); + txnParticipant->transactionInfoForLogForTest(&lockerInfo->stats, false, readConcernArgs); + ASSERT_EQUALS(1, countLogLinesContaining(expectedTransactionInfo)); +} + +TEST_F(TransactionsMetricsTest, LogTransactionInfoAfterExceptionInPrepare) { + auto tickSource = initMockTickSource(); + auto sessionCheckout = checkOutSession(); + + repl::ReadConcernArgs readConcernArgs; + ASSERT_OK(readConcernArgs.initialize(BSON("find" + << "test" + << repl::ReadConcernArgs::kReadConcernFieldName + << BSON(repl::ReadConcernArgs::kLevelFieldName + << "snapshot")))); + repl::ReadConcernArgs::get(opCtx()) = readConcernArgs; + + auto txnParticipant = TransactionParticipant::get(opCtx()); + + // Initialize SingleTransactionStats AdditiveMetrics objects. + const int metricValue = 1; + setupAdditiveMetrics(metricValue, opCtx()); + + txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction"); + serverGlobalParams.slowMS = 10; + tickSource->advance(Microseconds(11 * 1000)); + + _opObserver->onTransactionPrepareThrowsException = true; + + startCapturingLogMessages(); + ASSERT_THROWS_CODE(txnParticipant->prepareTransaction(opCtx(), {}), + AssertionException, + ErrorCodes::OperationFailed); + ASSERT_FALSE(_opObserver->transactionPrepared); + ASSERT(txnParticipant->transactionIsAborted()); + stopCapturingLogMessages(); + + const auto lockerInfo = opCtx()->lockState()->getLockerInfo(boost::none); + ASSERT(lockerInfo); + std::string expectedTransactionInfo = "transaction " + + txnParticipant->transactionInfoForLogForTest(&lockerInfo->stats, false, readConcernArgs); ASSERT_EQUALS(1, countLogLinesContaining(expectedTransactionInfo)); } @@ -3525,8 +3560,7 @@ TEST_F(TransactionsMetricsTest, LogTransactionInfoAfterSlowStashedAbort) { stopCapturingLogMessages(); std::string expectedTransactionInfo = "transaction " + - txnParticipant->transactionInfoForLogForTest( - &lockerInfo->stats, false, readConcernArgs, false); + txnParticipant->transactionInfoForLogForTest(&lockerInfo->stats, false, readConcernArgs); ASSERT_EQUALS(1, countLogLinesContaining(expectedTransactionInfo)); } |