summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTess Avitabile <tess.avitabile@mongodb.com>2019-03-27 17:18:47 -0400
committerTess Avitabile <tess.avitabile@mongodb.com>2019-03-29 16:31:06 -0400
commit7ea6e5dc1b7ab547dc45211d59eae19ecbda068e (patch)
tree61c3e41536bd6bab90b8d9c672c79fec15fde90c
parent75bb4ccaec68d6ce691ced12cb97529509310e52 (diff)
downloadmongo-7ea6e5dc1b7ab547dc45211d59eae19ecbda068e.tar.gz
SERVER-40298 Track transaction size
-rw-r--r--jstests/noPassthrough/server_transaction_metrics_last_committed_transaction.js86
-rw-r--r--src/mongo/db/server_transactions_metrics.cpp17
-rw-r--r--src/mongo/db/server_transactions_metrics.h9
-rw-r--r--src/mongo/db/session.cpp41
-rw-r--r--src/mongo/db/transactions_stats.idl17
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