diff options
author | Tess Avitabile <tess.avitabile@mongodb.com> | 2019-03-27 17:18:47 -0400 |
---|---|---|
committer | Tess Avitabile <tess.avitabile@mongodb.com> | 2019-03-29 16:31:06 -0400 |
commit | 7ea6e5dc1b7ab547dc45211d59eae19ecbda068e (patch) | |
tree | 61c3e41536bd6bab90b8d9c672c79fec15fde90c | |
parent | 75bb4ccaec68d6ce691ced12cb97529509310e52 (diff) | |
download | mongo-7ea6e5dc1b7ab547dc45211d59eae19ecbda068e.tar.gz |
SERVER-40298 Track transaction size
-rw-r--r-- | jstests/noPassthrough/server_transaction_metrics_last_committed_transaction.js | 86 | ||||
-rw-r--r-- | src/mongo/db/server_transactions_metrics.cpp | 17 | ||||
-rw-r--r-- | src/mongo/db/server_transactions_metrics.h | 9 | ||||
-rw-r--r-- | src/mongo/db/session.cpp | 41 | ||||
-rw-r--r-- | src/mongo/db/transactions_stats.idl | 17 |
5 files changed, 152 insertions, 18 deletions
diff --git a/jstests/noPassthrough/server_transaction_metrics_last_committed_transaction.js b/jstests/noPassthrough/server_transaction_metrics_last_committed_transaction.js new file mode 100644 index 00000000000..7f5faa9e4f8 --- /dev/null +++ b/jstests/noPassthrough/server_transaction_metrics_last_committed_transaction.js @@ -0,0 +1,86 @@ +// Tests the 'lastCommittedTransaction' serverStatus section. +// @tags: [uses_transactions] +(function() { + "use strict"; + + const rst = new ReplSetTest({nodes: 1}); + rst.startSet(); + rst.initiate(); + const primary = rst.getPrimary(); + + const dbName = "test"; + const collName = "coll"; + + const sessionOptions = {causalConsistency: false}; + const session = primary.getDB(dbName).getMongo().startSession(sessionOptions); + const sessionDb = session.getDatabase(dbName); + const sessionColl = sessionDb[collName]; + assert.commandWorked(sessionDb.runCommand({create: collName})); + + function checkLastCommittedTransaction(operationCount, writeConcern) { + let res = assert.commandWorked(primary.adminCommand({serverStatus: 1})); + assert(res.hasOwnProperty("transactions"), tojson(res)); + assert(res.transactions.hasOwnProperty("lastCommittedTransaction"), + tojson(res.transactions)); + assert.eq(operationCount, + res.transactions.lastCommittedTransaction.operationCount, + tojson(res.transactions)); + if (operationCount === 0) { + assert.eq(0, + res.transactions.lastCommittedTransaction.oplogOperationBytes, + tojson(res.transactions)); + } else { + assert.lt(0, + res.transactions.lastCommittedTransaction.oplogOperationBytes, + tojson(res.transactions)); + } + assert.docEq(writeConcern, + res.transactions.lastCommittedTransaction.writeConcern, + tojson(res.transactions)); + } + + // Initially the 'lastCommittedTransaction' section is not present. + let res = assert.commandWorked(primary.adminCommand({serverStatus: 1})); + assert(res.hasOwnProperty("transactions"), tojson(res)); + assert(!res.transactions.hasOwnProperty("lastCommittedTransaction"), tojson(res)); + + // Start a transaction. The 'lastCommittedTransaction' section is not updated until the + // transaction commits. + session.startTransaction(); + assert.commandWorked(sessionColl.insert({})); + res = assert.commandWorked(primary.adminCommand({serverStatus: 1})); + assert(res.hasOwnProperty("transactions"), tojson(res)); + assert(!res.transactions.hasOwnProperty("lastCommittedTransaction"), tojson(res)); + + // Commit the transaction. The 'lastCommittedTransaction' section should be updated. + assert.commandWorked(session.commitTransaction_forTesting()); + checkLastCommittedTransaction(1, {}); + + // Run a transaction with multiple write operations. + session.startTransaction(); + assert.commandWorked(sessionColl.insert({})); + assert.commandWorked(sessionColl.insert({})); + assert.commandWorked(session.commitTransaction_forTesting()); + checkLastCommittedTransaction(2, {}); + + // Run a read-only transaction. + session.startTransaction(); + sessionColl.findOne(); + assert.commandWorked(session.commitTransaction_forTesting()); + checkLastCommittedTransaction(0, {}); + + // Run a transaction with non-default writeConcern. + session.startTransaction({writeConcern: {w: 1}}); + assert.commandWorked(sessionColl.insert({})); + assert.commandWorked(session.commitTransaction_forTesting()); + checkLastCommittedTransaction(1, {w: 1, wtimeout: 0}); + + // Run a read-only transaction with non-default writeConcern. + session.startTransaction({writeConcern: {w: "majority"}}); + sessionColl.findOne(); + assert.commandWorked(session.commitTransaction_forTesting()); + checkLastCommittedTransaction(0, {w: "majority", wtimeout: 0}); + + session.endSession(); + rst.stopSet(); +}()); diff --git a/src/mongo/db/server_transactions_metrics.cpp b/src/mongo/db/server_transactions_metrics.cpp index 376425f1707..da5f2b8a112 100644 --- a/src/mongo/db/server_transactions_metrics.cpp +++ b/src/mongo/db/server_transactions_metrics.cpp @@ -113,6 +113,18 @@ void ServerTransactionsMetrics::incrementTotalCommitted() { _totalCommitted.fetchAndAdd(1); } +void ServerTransactionsMetrics::updateLastTransaction(size_t operationCount, + size_t oplogOperationBytes, + BSONObj writeConcern) { + stdx::lock_guard<stdx::mutex> lg(_mutex); + if (!_lastCommittedTransaction) { + _lastCommittedTransaction = LastCommittedTransaction(); + } + _lastCommittedTransaction->setOperationCount(operationCount); + _lastCommittedTransaction->setOplogOperationBytes(oplogOperationBytes); + _lastCommittedTransaction->setWriteConcern(std::move(writeConcern)); +} + void ServerTransactionsMetrics::updateStats(TransactionsStats* stats) { stats->setCurrentActive(_currentActive.load()); stats->setCurrentInactive(_currentInactive.load()); @@ -120,6 +132,11 @@ void ServerTransactionsMetrics::updateStats(TransactionsStats* stats) { stats->setTotalAborted(_totalAborted.load()); stats->setTotalCommitted(_totalCommitted.load()); stats->setTotalStarted(_totalStarted.load()); + + stdx::lock_guard<stdx::mutex> lg(_mutex); + if (_lastCommittedTransaction) { + stats->setLastCommittedTransaction(*_lastCommittedTransaction); + } } class TransactionsSSS : public ServerStatusSection { diff --git a/src/mongo/db/server_transactions_metrics.h b/src/mongo/db/server_transactions_metrics.h index b2020242aaa..02f397cde9e 100644 --- a/src/mongo/db/server_transactions_metrics.h +++ b/src/mongo/db/server_transactions_metrics.h @@ -69,6 +69,10 @@ public: unsigned long long getTotalCommitted() const; void incrementTotalCommitted(); + void updateLastTransaction(size_t operationCount, + size_t oplogOperationBytes, + BSONObj writeConcern); + /** * Appends the accumulated stats to a transactions stats object. */ @@ -92,6 +96,11 @@ private: // The total number of multi-document transaction commits. AtomicUInt64 _totalCommitted{0}; + + // Protects member variables below. + mutable stdx::mutex _mutex; + + boost::optional<LastCommittedTransaction> _lastCommittedTransaction; }; } // namespace mongo diff --git a/src/mongo/db/session.cpp b/src/mongo/db/session.cpp index 47727848aae..d30ba490fae 100644 --- a/src/mongo/db/session.cpp +++ b/src/mongo/db/session.cpp @@ -1074,23 +1074,24 @@ void Session::commitTransaction(OperationContext* opCtx) { void Session::_commitTransaction(stdx::unique_lock<stdx::mutex> lk, OperationContext* opCtx) { invariant(_txnState == MultiDocumentTransactionState::kInProgress); - const bool isMultiDocumentTransaction = _txnState == MultiDocumentTransactionState::kInProgress; - if (isMultiDocumentTransaction) { - // 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); - lk.lock(); - // It's possible some other thread aborted the transaction (e.g. through killSession) while - // the opObserver was running. If that happened, the commit should be reported as failed. - uassert(ErrorCodes::NoSuchTransaction, - str::stream() << "Transaction " << opCtx->getTxnNumber() - << " aborted while attempting to commit", - _txnState == MultiDocumentTransactionState::kInProgress && - _activeTxnNumber == opCtx->getTxnNumber()); - } + const size_t operationCount = _transactionOperations.size(); + const size_t oplogOperationBytes = _transactionOperationBytes; + + // 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); + lk.lock(); + + // It's possible some other thread aborted the transaction (e.g. through killSession) while the + // opObserver was running. If that happened, the commit should be reported as failed. + uassert(ErrorCodes::NoSuchTransaction, + str::stream() << "Transaction " << opCtx->getTxnNumber() + << " aborted while attempting to commit", + _txnState == MultiDocumentTransactionState::kInProgress && + _activeTxnNumber == opCtx->getTxnNumber()); _txnState = MultiDocumentTransactionState::kCommitting; bool committed = false; ON_BLOCK_EXIT([this, &committed, opCtx]() { @@ -1158,7 +1159,11 @@ void Session::_commitTransaction(stdx::unique_lock<stdx::mutex> lk, OperationCon // inactive. ServerTransactionsMetrics::get(opCtx)->incrementTotalCommitted(); ServerTransactionsMetrics::get(opCtx)->decrementCurrentOpen(); - ServerTransactionsMetrics::get(getGlobalServiceContext())->decrementCurrentActive(); + ServerTransactionsMetrics::get(opCtx)->decrementCurrentActive(); + ServerTransactionsMetrics::get(opCtx)->updateLastTransaction( + operationCount, + oplogOperationBytes, + opCtx->getWriteConcern().usedDefault ? BSONObj() : opCtx->getWriteConcern().toBSON()); auto curTime = curTimeMicros64(); Top::get(getGlobalServiceContext()) .incrementGlobalTransactionLatencyStats(_singleTransactionStats.getDuration(curTime)); diff --git a/src/mongo/db/transactions_stats.idl b/src/mongo/db/transactions_stats.idl index b47f49d2b6b..23f0000bf3d 100644 --- a/src/mongo/db/transactions_stats.idl +++ b/src/mongo/db/transactions_stats.idl @@ -38,6 +38,20 @@ imports: structs: + LastCommittedTransaction: + description: "A struct representing the server status subsection on + the last committed transaction" + strict: true + fields: + operationCount: + type: long + default: 0 + oplogOperationBytes: + type: long + default: 0 + writeConcern: + type: object + TransactionsStats: description: "A struct representing the section of the server status command with information about transactions" @@ -70,3 +84,6 @@ structs: totalStarted: type: long default: 0 + lastCommittedTransaction: + type: LastCommittedTransaction + optional: true |