summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjinichu <jinnybyun@gmail.com>2018-07-03 15:04:08 -0400
committerjinichu <jinnybyun@gmail.com>2018-07-13 15:41:23 -0400
commitcf1f02c116a9f28b97bc6c41148446342349785c (patch)
tree4120965384eecdf035cfbf6be4208232fe264701
parent97f15caddf7835ef5ed252257903edc52dae9aaf (diff)
downloadmongo-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.cpp15
-rw-r--r--src/mongo/db/session_test.cpp122
-rw-r--r--src/mongo/db/single_transaction_stats.h34
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