diff options
author | jinichu <jinnybyun@gmail.com> | 2018-07-03 15:04:08 -0400 |
---|---|---|
committer | jinichu <jinnybyun@gmail.com> | 2018-07-13 15:41:23 -0400 |
commit | cf1f02c116a9f28b97bc6c41148446342349785c (patch) | |
tree | 4120965384eecdf035cfbf6be4208232fe264701 | |
parent | 97f15caddf7835ef5ed252257903edc52dae9aaf (diff) | |
download | mongo-cf1f02c116a9f28b97bc6c41148446342349785c.tar.gz |
SERVER-35168 Added ClientInfo to Session to track info about the last client to run a transaction operation on Session
-rw-r--r-- | src/mongo/db/session.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/session_test.cpp | 122 | ||||
-rw-r--r-- | src/mongo/db/single_transaction_stats.h | 34 |
3 files changed, 171 insertions, 0 deletions
diff --git a/src/mongo/db/session.cpp b/src/mongo/db/session.cpp index 2baefb054a3..df347194ea7 100644 --- a/src/mongo/db/session.cpp +++ b/src/mongo/db/session.cpp @@ -742,6 +742,11 @@ void Session::stashTransactionResources(OperationContext* opCtx) { // We accept possible slight inaccuracies in these counters from non-atomicity. ServerTransactionsMetrics::get(opCtx)->decrementCurrentActive(); ServerTransactionsMetrics::get(opCtx)->incrementCurrentInactive(); + + // Update the LastClientInfo object stored in the SingleTransactionStats instance on the Session + // with this Client's information. This is the last client that ran a transaction operation on + // the Session. + _singleTransactionStats->getLastClientInfo()->update(opCtx->getClient()); } void Session::unstashTransactionResources(OperationContext* opCtx, const std::string& cmdName) { @@ -918,6 +923,10 @@ void Session::abortActiveTransaction(OperationContext* opCtx) { // SingleTransactionStats instance on the Session. _singleTransactionStats->getOpDebug()->additiveMetrics.add( CurOp::get(opCtx)->debug().additiveMetrics); + + // Update the LastClientInfo object stored in the SingleTransactionStats instance on the Session + // with this Client's information. + _singleTransactionStats->getLastClientInfo()->update(opCtx->getClient()); } void Session::_abortTransaction(WithLock wl) { @@ -1063,6 +1072,9 @@ void Session::_commitTransaction(stdx::unique_lock<stdx::mutex> lk, OperationCon // SingleTransactionStats instance on the Session. _singleTransactionStats->getOpDebug()->additiveMetrics.add( CurOp::get(opCtx)->debug().additiveMetrics); + // Update the LastClientInfo object stored in the SingleTransactionStats instance on + // the Session with this Client's information. + _singleTransactionStats->getLastClientInfo()->update(opCtx->getClient()); } } // We must clear the recovery unit and locker so any post-transaction writes can run without @@ -1099,6 +1111,9 @@ void Session::_commitTransaction(stdx::unique_lock<stdx::mutex> lk, OperationCon // SingleTransactionStats instance on the Session. _singleTransactionStats->getOpDebug()->additiveMetrics.add( CurOp::get(opCtx)->debug().additiveMetrics); + // Update the LastClientInfo object stored in the SingleTransactionStats instance on the Session + // with this Client's information. + _singleTransactionStats->getLastClientInfo()->update(opCtx->getClient()); } BSONObj Session::reportStashedState() const { diff --git a/src/mongo/db/session_test.cpp b/src/mongo/db/session_test.cpp index 8d1cf2f6afd..41236161fae 100644 --- a/src/mongo/db/session_test.cpp +++ b/src/mongo/db/session_test.cpp @@ -2279,5 +2279,127 @@ TEST_F(TransactionsMetricsTest, TimeInactiveMicrosShouldIncreaseUntilCommit) { timeInactiveSoFar); } +namespace { + +/* + * Constructs a ClientMetadata BSONObj with the given application name. + */ +BSONObj constructClientMetadata(StringData appName) { + BSONObjBuilder builder; + ASSERT_OK(ClientMetadata::serializePrivate("driverName", + "driverVersion", + "osType", + "osName", + "osArchitecture", + "osVersion", + appName, + &builder)); + return builder.obj(); +} +} // namespace + +TEST_F(TransactionsMetricsTest, LastClientInfoShouldUpdateUponStash) { + const auto sessionId = makeLogicalSessionIdForTest(); + Session session(sessionId); + session.refreshFromStorageIfNeeded(opCtx()); + + const TxnNumber txnNum = 1; + opCtx()->setLogicalSessionId(sessionId); + opCtx()->setTxnNumber(txnNum); + + // Create a ClientMetadata object and set it on ClientMetadataIsMasterState. + auto obj = constructClientMetadata("appName"); + auto clientMetadata = ClientMetadata::parse(obj["client"]); + auto& clientMetadataIsMasterState = ClientMetadataIsMasterState::get(opCtx()->getClient()); + clientMetadataIsMasterState.setClientMetadata(opCtx()->getClient(), + std::move(clientMetadata.getValue())); + + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"); + session.unstashTransactionResources(opCtx(), "insert"); + // The transaction machinery cannot store an empty locker. + { Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); } + session.stashTransactionResources(opCtx()); + + // LastClientInfo should have been set. + ASSERT_EQ(session.getSingleTransactionStats()->getLastClientInfo()->client, ""); + ASSERT_EQ(session.getSingleTransactionStats()->getLastClientInfo()->connectionId, 0); + ASSERT_EQ(session.getSingleTransactionStats()->getLastClientInfo()->appName, "appName"); + ASSERT_BSONOBJ_EQ(session.getSingleTransactionStats()->getLastClientInfo()->clientMetadata, + obj.getField("client").Obj()); + + // Create another ClientMetadata object. + auto newObj = constructClientMetadata("newAppName"); + auto newClientMetadata = ClientMetadata::parse(newObj["client"]); + clientMetadataIsMasterState.setClientMetadata(opCtx()->getClient(), + std::move(newClientMetadata.getValue())); + + session.unstashTransactionResources(opCtx(), "insert"); + session.stashTransactionResources(opCtx()); + + // LastClientInfo's clientMetadata should have been updated to the new ClientMetadata object. + ASSERT_EQ(session.getSingleTransactionStats()->getLastClientInfo()->appName, "newAppName"); + ASSERT_BSONOBJ_EQ(session.getSingleTransactionStats()->getLastClientInfo()->clientMetadata, + newObj.getField("client").Obj()); +} + +TEST_F(TransactionsMetricsTest, LastClientInfoShouldUpdateUponCommit) { + const auto sessionId = makeLogicalSessionIdForTest(); + Session session(sessionId); + session.refreshFromStorageIfNeeded(opCtx()); + + const TxnNumber txnNum = 1; + opCtx()->setLogicalSessionId(sessionId); + opCtx()->setTxnNumber(txnNum); + + // Create a ClientMetadata object and set it on ClientMetadataIsMasterState. + auto obj = constructClientMetadata("appName"); + auto clientMetadata = ClientMetadata::parse(obj["client"]); + auto& clientMetadataIsMasterState = ClientMetadataIsMasterState::get(opCtx()->getClient()); + clientMetadataIsMasterState.setClientMetadata(opCtx()->getClient(), + std::move(clientMetadata.getValue())); + + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"); + session.unstashTransactionResources(opCtx(), "insert"); + // The transaction machinery cannot store an empty locker. + Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); + session.commitTransaction(opCtx()); + + // LastClientInfo should have been set. + ASSERT_EQ(session.getSingleTransactionStats()->getLastClientInfo()->client, ""); + ASSERT_EQ(session.getSingleTransactionStats()->getLastClientInfo()->connectionId, 0); + ASSERT_EQ(session.getSingleTransactionStats()->getLastClientInfo()->appName, "appName"); + ASSERT_BSONOBJ_EQ(session.getSingleTransactionStats()->getLastClientInfo()->clientMetadata, + obj.getField("client").Obj()); +} + +TEST_F(TransactionsMetricsTest, LastClientInfoShouldUpdateUponAbort) { + const auto sessionId = makeLogicalSessionIdForTest(); + Session session(sessionId); + session.refreshFromStorageIfNeeded(opCtx()); + + const TxnNumber txnNum = 1; + opCtx()->setLogicalSessionId(sessionId); + opCtx()->setTxnNumber(txnNum); + + // Create a ClientMetadata object and set it on ClientMetadataIsMasterState. + auto obj = constructClientMetadata("appName"); + auto clientMetadata = ClientMetadata::parse(obj["client"]); + + auto& clientMetadataIsMasterState = ClientMetadataIsMasterState::get(opCtx()->getClient()); + clientMetadataIsMasterState.setClientMetadata(opCtx()->getClient(), + std::move(clientMetadata.getValue())); + + session.beginOrContinueTxn(opCtx(), txnNum, false, true, "testDB", "insert"); + session.unstashTransactionResources(opCtx(), "insert"); + session.abortActiveTransaction(opCtx()); + + // LastClientInfo should have been set. + ASSERT_EQ(session.getSingleTransactionStats()->getLastClientInfo()->client, ""); + ASSERT_EQ(session.getSingleTransactionStats()->getLastClientInfo()->connectionId, 0); + ASSERT_EQ(session.getSingleTransactionStats()->getLastClientInfo()->appName, "appName"); + ASSERT_BSONOBJ_EQ(session.getSingleTransactionStats()->getLastClientInfo()->clientMetadata, + obj.getField("client").Obj()); +} + } // namespace } // namespace mongo diff --git a/src/mongo/db/single_transaction_stats.h b/src/mongo/db/single_transaction_stats.h index 82addddfab9..ca4dd2411ad 100644 --- a/src/mongo/db/single_transaction_stats.h +++ b/src/mongo/db/single_transaction_stats.h @@ -29,6 +29,8 @@ #pragma once #include "mongo/db/curop.h" +#include "mongo/rpc/metadata/client_metadata.h" +#include "mongo/rpc/metadata/client_metadata_ismaster.h" namespace mongo { @@ -37,6 +39,28 @@ namespace mongo { */ class SingleTransactionStats { public: + /* + * Stores information about the last client to run a transaction operation. + */ + struct LastClientInfo { + std::string client; + long long connectionId; + BSONObj clientMetadata; + std::string appName; + + void update(Client* opCtxClient) { + if (opCtxClient->hasRemote()) { + client = opCtxClient->getRemote().toString(); + } + connectionId = opCtxClient->getConnectionId(); + if (const auto& metadata = + ClientMetadataIsMasterState::get(opCtxClient).getClientMetadata()) { + clientMetadata = metadata.get().getDocument(); + appName = metadata.get().getApplicationName().toString(); + } + } + }; + /** * Returns the start time of the transaction in microseconds. * @@ -111,6 +135,13 @@ public: return &_opDebug; } + /* + * Returns the LastClientInfo object stored in this SingleTransactionStats instance. + */ + LastClientInfo* getLastClientInfo() { + return &_lastClientInfo; + } + private: // The start time of the transaction in microseconds. unsigned long long _startTime{0}; @@ -127,6 +158,9 @@ private: // Tracks and accumulates stats from all operations that run inside the transaction. OpDebug _opDebug; + + // Holds information about the last client to run a transaction operation. + LastClientInfo _lastClientInfo; }; } // namespace mongo |