diff options
author | Jason Chan <jason.chan@mongodb.com> | 2018-11-01 10:48:37 -0400 |
---|---|---|
committer | Jason Chan <jason.chan@mongodb.com> | 2018-11-01 10:50:28 -0400 |
commit | 970b1de0ec350ec5487fb230391c7bae33dd0fc2 (patch) | |
tree | d69035e821cf2d9b563df6dfde689f0ece051f74 /src/mongo | |
parent | c06cea15dcc13c7e8777be5da229b1423ae7465b (diff) | |
download | mongo-970b1de0ec350ec5487fb230391c7bae33dd0fc2.tar.gz |
SERVER-36501 serverStatus support for prepared transactions
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/server_transactions_metrics.cpp | 41 | ||||
-rw-r--r-- | src/mongo/db/server_transactions_metrics.h | 27 | ||||
-rw-r--r-- | src/mongo/db/transaction_metrics_observer.cpp | 18 | ||||
-rw-r--r-- | src/mongo/db/transaction_metrics_observer.h | 9 | ||||
-rw-r--r-- | src/mongo/db/transaction_participant.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/transaction_participant_test.cpp | 129 | ||||
-rw-r--r-- | src/mongo/db/transactions_stats.idl | 11 |
7 files changed, 236 insertions, 11 deletions
diff --git a/src/mongo/db/server_transactions_metrics.cpp b/src/mongo/db/server_transactions_metrics.cpp index 3386bc19c42..627953e54fb 100644 --- a/src/mongo/db/server_transactions_metrics.cpp +++ b/src/mongo/db/server_transactions_metrics.cpp @@ -113,7 +113,31 @@ void ServerTransactionsMetrics::incrementTotalCommitted() { _totalCommitted.fetchAndAdd(1); } -boost::optional<repl::OpTime> ServerTransactionsMetrics::getOldestActiveOpTime() const { +unsigned long long ServerTransactionsMetrics::getTotalPrepared() const { + return _totalPrepared.load(); +} + +void ServerTransactionsMetrics::incrementTotalPrepared() { + _totalPrepared.fetchAndAdd(1); +} + +unsigned long long ServerTransactionsMetrics::getTotalPreparedThenCommitted() const { + return _totalPreparedThenCommitted.load(); +} + +void ServerTransactionsMetrics::incrementTotalPreparedThenCommitted() { + _totalPreparedThenCommitted.fetchAndAdd(1); +} + +unsigned long long ServerTransactionsMetrics::getTotalPreparedThenAborted() const { + return _totalPreparedThenAborted.load(); +} + +void ServerTransactionsMetrics::incrementTotalPreparedThenAborted() { + _totalPreparedThenAborted.fetchAndAdd(1); +} + +boost::optional<repl::OpTime> ServerTransactionsMetrics::_calculateOldestActiveOpTime() const { if (_oldestActiveOplogEntryOpTimes.empty()) { return boost::none; } @@ -140,6 +164,7 @@ void ServerTransactionsMetrics::addActiveOpTime(repl::OpTime oldestOplogEntryOpT << "oldestNonMajorityCommittedOpTimes." << "oldestOplogEntryOpTime: " << oldestOplogEntryOpTime.toString()); + _oldestActiveOplogEntryOpTime = _calculateOldestActiveOpTime(); } void ServerTransactionsMetrics::removeActiveOpTime(repl::OpTime oldestOplogEntryOpTime, @@ -183,6 +208,7 @@ void ServerTransactionsMetrics::removeActiveOpTime(repl::OpTime oldestOplogEntry << oldestOplogEntryOpTime.toString() << "finishOpTime: " << finishOpTime->toString()); + _oldestActiveOplogEntryOpTime = _calculateOldestActiveOpTime(); } boost::optional<repl::OpTime> ServerTransactionsMetrics::getOldestNonMajorityCommittedOpTime() @@ -219,6 +245,10 @@ ServerTransactionsMetrics::getFinishOpTimeOfOldestNonMajCommitted_forTest() cons return oldestNonMajorityCommittedOpTime; } +boost::optional<repl::OpTime> ServerTransactionsMetrics::getOldestActiveOpTime() const { + return _oldestActiveOplogEntryOpTime; +} + unsigned int ServerTransactionsMetrics::getTotalActiveOpTimes() const { return _oldestActiveOplogEntryOpTimes.size(); } @@ -230,6 +260,15 @@ void ServerTransactionsMetrics::updateStats(TransactionsStats* stats) { stats->setTotalAborted(_totalAborted.load()); stats->setTotalCommitted(_totalCommitted.load()); stats->setTotalStarted(_totalStarted.load()); + stats->setTotalPrepared(_totalPrepared.load()); + stats->setTotalPreparedThenCommitted(_totalPreparedThenCommitted.load()); + stats->setTotalPreparedThenAborted(_totalPreparedThenAborted.load()); + // To avoid compression loss, we have Timestamp(0, 0) be the default value if no oldest active + // transaction optime is stored. + Timestamp oldestActiveOplogEntryTimestamp = (_oldestActiveOplogEntryOpTime != boost::none) + ? _oldestActiveOplogEntryOpTime->getTimestamp() + : Timestamp(); + stats->setOldestActiveOplogEntryTimestamp(oldestActiveOplogEntryTimestamp); } class TransactionsSSS : public ServerStatusSection { diff --git a/src/mongo/db/server_transactions_metrics.h b/src/mongo/db/server_transactions_metrics.h index 7dbc208e0c6..be872863067 100644 --- a/src/mongo/db/server_transactions_metrics.h +++ b/src/mongo/db/server_transactions_metrics.h @@ -72,6 +72,15 @@ public: unsigned long long getTotalCommitted() const; void incrementTotalCommitted(); + unsigned long long getTotalPrepared() const; + void incrementTotalPrepared(); + + unsigned long long getTotalPreparedThenCommitted() const; + void incrementTotalPreparedThenCommitted(); + + unsigned long long getTotalPreparedThenAborted() const; + void incrementTotalPreparedThenAborted(); + /** * Returns the OpTime of the oldest oplog entry written across all open transactions. * Returns boost::none if there are no transaction oplog entry OpTimes stored. @@ -130,6 +139,12 @@ public: void updateStats(TransactionsStats* stats); private: + /** + * Returns the first and oldest optime in the ordered set of active oplog entry optimes. + * Returns boost::none if there are no transaction oplog entry optimes stored. + */ + boost::optional<repl::OpTime> _calculateOldestActiveOpTime() const; + // The number of multi-document transactions currently active. AtomicUInt64 _currentActive{0}; @@ -148,6 +163,18 @@ private: // The total number of multi-document transaction commits. AtomicUInt64 _totalCommitted{0}; + // The total number of prepared transactions since the last server startup. + AtomicUInt64 _totalPrepared{0}; + + // The total number of prepared transaction commits. + AtomicUInt64 _totalPreparedThenCommitted{0}; + + // The total number of prepared transaction aborts. + AtomicUInt64 _totalPreparedThenAborted{0}; + + // The optime of the oldest oplog entry for any active transaction. + boost::optional<repl::OpTime> _oldestActiveOplogEntryOpTime; + // Maintain the oldest oplog entry OpTime across all active transactions. Currently, we only // write an oplog entry for an ongoing transaction if it is in the `prepare` state. By // maintaining an ordered set of OpTimes, the OpTime at the beginning will be the oldest. diff --git a/src/mongo/db/transaction_metrics_observer.cpp b/src/mongo/db/transaction_metrics_observer.cpp index eea17ad5379..28a6f26e696 100644 --- a/src/mongo/db/transaction_metrics_observer.cpp +++ b/src/mongo/db/transaction_metrics_observer.cpp @@ -100,7 +100,8 @@ void TransactionMetricsObserver::onCommit(ServerTransactionsMetrics* serverTrans TickSource* tickSource, boost::optional<repl::OpTime> oldestOplogEntryOpTime, boost::optional<repl::OpTime> commitOpTime, - Top* top) { + Top* top, + bool wasPrepared) { invariant((oldestOplogEntryOpTime != boost::none && commitOpTime != boost::none) || (oldestOplogEntryOpTime == boost::none && commitOpTime == boost::none)); // @@ -123,6 +124,10 @@ void TransactionMetricsObserver::onCommit(ServerTransactionsMetrics* serverTrans serverTransactionsMetrics->decrementCurrentOpen(); serverTransactionsMetrics->decrementCurrentActive(); + if (wasPrepared) { + serverTransactionsMetrics->incrementTotalPreparedThenCommitted(); + } + auto duration = durationCount<Microseconds>(_singleTransactionStats.getDuration(tickSource, curTick)); top->incrementGlobalTransactionLatencyStats(static_cast<uint64_t>(duration)); @@ -137,7 +142,8 @@ void TransactionMetricsObserver::onAbortActive(ServerTransactionsMetrics* server TickSource* tickSource, boost::optional<repl::OpTime> oldestOplogEntryOpTime, boost::optional<repl::OpTime> abortOpTime, - Top* top) { + Top* top, + bool wasPrepared) { invariant((oldestOplogEntryOpTime != boost::none && abortOpTime != boost::none) || (oldestOplogEntryOpTime == boost::none && abortOpTime == boost::none)); @@ -157,6 +163,10 @@ void TransactionMetricsObserver::onAbortActive(ServerTransactionsMetrics* server // serverTransactionsMetrics->decrementCurrentActive(); + if (wasPrepared) { + serverTransactionsMetrics->incrementTotalPreparedThenAborted(); + } + // Remove this transaction's oldest oplog entry OpTime if one was written. if (oldestOplogEntryOpTime) { serverTransactionsMetrics->removeActiveOpTime(*oldestOplogEntryOpTime, abortOpTime); @@ -215,11 +225,13 @@ void TransactionMetricsObserver::_onAbort(ServerTransactionsMetrics* serverTrans } void TransactionMetricsObserver::onPrepare(ServerTransactionsMetrics* serverTransactionsMetrics, - repl::OpTime prepareOpTime) { + repl::OpTime prepareOpTime, + TickSource::Tick curTick) { // Since we currently only write an oplog entry for an in progress transaction when it is in // the prepare state, the prepareOpTime is currently the oldest OpTime written to the // oplog for this transaction. serverTransactionsMetrics->addActiveOpTime(prepareOpTime); + serverTransactionsMetrics->incrementTotalPrepared(); } } // namespace mongo diff --git a/src/mongo/db/transaction_metrics_observer.h b/src/mongo/db/transaction_metrics_observer.h index 715aa14244f..6600615ceac 100644 --- a/src/mongo/db/transaction_metrics_observer.h +++ b/src/mongo/db/transaction_metrics_observer.h @@ -77,7 +77,8 @@ public: TickSource* tickSource, boost::optional<repl::OpTime> oldestOplogEntryOpTime, boost::optional<repl::OpTime> commitOpTime, - Top* top); + Top* top, + bool wasPrepared); /** * Updates relevant metrics when an active transaction aborts. Also removes this transaction's @@ -89,7 +90,8 @@ public: TickSource* tickSource, boost::optional<repl::OpTime> oldestOplogEntryOpTime, boost::optional<repl::OpTime> abortOpTime, - Top* top); + Top* top, + bool wasPrepared); /** * Updates relevant metrics when an inactive transaction aborts. Also removes this transaction's @@ -109,7 +111,8 @@ public: * an active transaction, to the oldestActiveOplogEntryTS set. */ void onPrepare(ServerTransactionsMetrics* serverTransactionsMetrics, - repl::OpTime prepareOpTime); + repl::OpTime prepareOpTime, + TickSource::Tick curTick); /** * Updates relevant metrics when an operation running on the transaction completes. An operation diff --git a/src/mongo/db/transaction_participant.cpp b/src/mongo/db/transaction_participant.cpp index 17d440103a2..68e85ca5ec8 100644 --- a/src/mongo/db/transaction_participant.cpp +++ b/src/mongo/db/transaction_participant.cpp @@ -835,8 +835,10 @@ Timestamp TransactionParticipant::prepareTransaction(OperationContext* opCtx, // 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); + *_oldestOplogEntryOpTime, + tickSource->getTicks()); } return prepareOplogSlot.opTime.getTimestamp(); @@ -1012,7 +1014,7 @@ void TransactionParticipant::_finishCommitTransaction(WithLock lk, OperationCont if (_speculativeTransactionReadOpTime > clientInfo.getLastOp()) { clientInfo.setLastOp(_speculativeTransactionReadOpTime); } - + const bool isCommittingWithPrepare = _txnState.isCommittingWithPrepare(lk); _txnState.transitionTo(lk, TransactionState::kCommitted); { @@ -1022,7 +1024,8 @@ void TransactionParticipant::_finishCommitTransaction(WithLock lk, OperationCont tickSource, _oldestOplogEntryOpTime, _finishOpTime, - &Top::get(getGlobalServiceContext())); + &Top::get(getGlobalServiceContext()), + isCommittingWithPrepare); _transactionMetricsObserver.onTransactionOperation( opCtx->getClient(), CurOp::get(opCtx)->debug().additiveMetrics); } @@ -1194,7 +1197,8 @@ void TransactionParticipant::_abortTransactionOnSession(WithLock wl) { tickSource, _oldestOplogEntryOpTime, _finishOpTime, - &Top::get(getGlobalServiceContext())); + &Top::get(getGlobalServiceContext()), + _txnState.isPrepared(lm)); } _transactionOperationBytes = 0; diff --git a/src/mongo/db/transaction_participant_test.cpp b/src/mongo/db/transaction_participant_test.cpp index d8e68714408..8330d1dc187 100644 --- a/src/mongo/db/transaction_participant_test.cpp +++ b/src/mongo/db/transaction_participant_test.cpp @@ -1682,6 +1682,17 @@ TEST_F(TransactionsMetricsTest, IncrementTotalStartedUponStartTransaction) { beforeTransactionStart + 1U); } +TEST_F(TransactionsMetricsTest, IncrementPreparedTransaction) { + OperationContextSessionMongod opCtxSession(opCtx(), true, makeSessionInfo()); + auto txnParticipant = TransactionParticipant::get(opCtx()); + unsigned long long beforePrepareCount = + ServerTransactionsMetrics::get(opCtx())->getTotalPrepared(); + txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction"); + txnParticipant->prepareTransaction(opCtx(), {}); + + ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalPrepared(), beforePrepareCount + 1U); +} + TEST_F(TransactionsMetricsTest, IncrementTotalCommittedOnCommit) { OperationContextSessionMongod opCtxSession(opCtx(), true, makeSessionInfo()); auto txnParticipant = TransactionParticipant::get(opCtx()); @@ -1696,6 +1707,23 @@ TEST_F(TransactionsMetricsTest, IncrementTotalCommittedOnCommit) { ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalCommitted(), beforeCommitCount + 1U); } +TEST_F(TransactionsMetricsTest, IncrementTotalPreparedThenCommitted) { + OperationContextSessionMongod opCtxSession(opCtx(), true, makeSessionInfo()); + auto txnParticipant = TransactionParticipant::get(opCtx()); + txnParticipant->unstashTransactionResources(opCtx(), "commitTransaction"); + const auto prepareTimestamp = txnParticipant->prepareTransaction(opCtx(), {}); + + unsigned long long beforePreparedThenCommittedCount = + ServerTransactionsMetrics::get(opCtx())->getTotalPreparedThenCommitted(); + + txnParticipant->commitPreparedTransaction(opCtx(), prepareTimestamp); + + ASSERT_TRUE(txnParticipant->transactionIsCommitted()); + ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalPreparedThenCommitted(), + beforePreparedThenCommittedCount + 1U); +} + + TEST_F(TransactionsMetricsTest, IncrementTotalAbortedUponAbort) { OperationContextSessionMongod opCtxSession(opCtx(), true, makeSessionInfo()); auto txnParticipant = TransactionParticipant::get(opCtx()); @@ -1710,6 +1738,22 @@ TEST_F(TransactionsMetricsTest, IncrementTotalAbortedUponAbort) { ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalAborted(), beforeAbortCount + 1U); } +TEST_F(TransactionsMetricsTest, IncrementTotalPreparedThenAborted) { + unsigned long long beforePreparedThenAbortedCount = + ServerTransactionsMetrics::get(opCtx())->getTotalPreparedThenAborted(); + + OperationContextSessionMongod opCtxSession(opCtx(), true, makeSessionInfo()); + auto txnParticipant = TransactionParticipant::get(opCtx()); + txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction"); + txnParticipant->prepareTransaction(opCtx(), {}); + + txnParticipant->abortActiveTransaction(opCtx()); + ASSERT(txnParticipant->transactionIsAborted()); + ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalPreparedThenAborted(), + beforePreparedThenAbortedCount + 1U); +} + + TEST_F(TransactionsMetricsTest, TrackTotalOpenTransactionsWithAbort) { unsigned long long beforeTransactionStart = ServerTransactionsMetrics::get(opCtx())->getCurrentOpen(); @@ -1854,6 +1898,91 @@ TEST_F(TransactionsMetricsTest, TrackTotalActiveAndInactiveTransactionsWithUnsta ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(), beforeInactiveCounter); } +TEST_F(TransactionsMetricsTest, TrackCurrentActiveAndInactivePreparedTransactionsOnCommit) { + unsigned long long beforeActivePreparedCounter = + ServerTransactionsMetrics::get(opCtx())->getCurrentActive(); + unsigned long long beforeInactivePreparedCounter = + ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(); + OperationContextSessionMongod opCtxSession(opCtx(), true, makeSessionInfo()); + unsigned long long beforePrepareCount = + ServerTransactionsMetrics::get(opCtx())->getTotalPrepared(); + unsigned long long beforePreparedThenCommittedCount = + ServerTransactionsMetrics::get(opCtx())->getTotalPreparedThenCommitted(); + + // 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(), {}); + ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(), + beforeActivePreparedCounter + 1U); + ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(), + beforeInactivePreparedCounter); + 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()); + 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"); + 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(), prepareTimestamp); + ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(), + beforeActivePreparedCounter); + ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(), + beforeInactivePreparedCounter); + ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getTotalPreparedThenCommitted(), + beforePreparedThenCommittedCount + 1U); +} + +TEST_F(TransactionsMetricsTest, + TrackCurrentActiveAndInactivePreparedTransactionsWithUnstashedAbort) { + unsigned long long beforeActivePreparedCounter = + ServerTransactionsMetrics::get(opCtx())->getCurrentActive(); + unsigned long long beforeInactivePreparedCounter = + ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(); + OperationContextSessionMongod opCtxSession(opCtx(), true, makeSessionInfo()); + + auto txnParticipant = TransactionParticipant::get(opCtx()); + + // Tests that unstashing a transaction increments the active counter only. + txnParticipant->unstashTransactionResources(opCtx(), "prepareTransaction"); + txnParticipant->prepareTransaction(opCtx(), {}); + ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(), + beforeActivePreparedCounter + 1U); + ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(), + beforeInactivePreparedCounter); + + // Tests that stashing a prepared transaction decrements the active counter and increments the + // inactive counter. + 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"); + ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(), + beforeActivePreparedCounter + 1U); + ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(), + beforeInactivePreparedCounter); + txnParticipant->abortActiveTransaction(opCtx()); + ASSERT(txnParticipant->transactionIsAborted()); + ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentActive(), + beforeActivePreparedCounter); + ASSERT_EQ(ServerTransactionsMetrics::get(opCtx())->getCurrentInactive(), + beforeInactivePreparedCounter); +} + TEST_F(TransactionsMetricsTest, SingleTransactionStatsDurationShouldBeSetUponCommit) { auto tickSource = initMockTickSource(); diff --git a/src/mongo/db/transactions_stats.idl b/src/mongo/db/transactions_stats.idl index b47f49d2b6b..13c9843d1a5 100644 --- a/src/mongo/db/transactions_stats.idl +++ b/src/mongo/db/transactions_stats.idl @@ -70,3 +70,14 @@ structs: totalStarted: type: long default: 0 + totalPrepared: + type: long + default: 0 + totalPreparedThenCommitted: + type: long + default: 0 + totalPreparedThenAborted: + type: long + default: 0 + oldestActiveOplogEntryTimestamp: + type: timestamp |