summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Jesse Jiryu Davis <jesse@mongodb.com>2021-05-12 14:16:14 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-05-12 18:39:03 +0000
commita696566eae01ed989738f36e396ef2ec3949728c (patch)
treec6d0696848ce85ad76308dca849ffb80a7d78603
parent2a99e03b813f33342ffe83ccc5df9b8d2c33bf08 (diff)
downloadmongo-a696566eae01ed989738f36e396ef2ec3949728c.tar.gz
SERVER-49336 Set ClientMetadata before CommandInvocations are run
-rw-r--r--src/mongo/db/commands.cpp21
-rw-r--r--src/mongo/db/commands/authentication_commands.cpp1
-rw-r--r--src/mongo/db/curop.cpp14
-rw-r--r--src/mongo/db/introspect.cpp7
-rw-r--r--src/mongo/db/pipeline/pipeline_d.cpp2
-rw-r--r--src/mongo/db/repl/replication_info.cpp33
-rw-r--r--src/mongo/db/s/single_transaction_coordinator_stats.h9
-rw-r--r--src/mongo/db/s/transaction_coordinator_test_fixture.cpp4
-rw-r--r--src/mongo/db/service_entry_point_common.cpp9
-rw-r--r--src/mongo/db/stats/single_transaction_stats.h8
-rw-r--r--src/mongo/db/transaction_participant_test.cpp28
-rw-r--r--src/mongo/embedded/embedded_ismaster.cpp28
-rw-r--r--src/mongo/rpc/SConscript1
-rw-r--r--src/mongo/rpc/metadata.cpp10
-rw-r--r--src/mongo/rpc/metadata/client_metadata.cpp115
-rw-r--r--src/mongo/rpc/metadata/client_metadata.h143
-rw-r--r--src/mongo/rpc/metadata/client_metadata_ismaster.cpp128
-rw-r--r--src/mongo/rpc/metadata/client_metadata_ismaster.h115
-rw-r--r--src/mongo/s/commands/cluster_is_master_cmd.cpp31
-rw-r--r--src/mongo/s/commands/strategy.cpp10
-rw-r--r--src/mongo/s/sharding_egress_metadata_hook.cpp9
-rw-r--r--src/mongo/s/transaction_router_test.cpp6
22 files changed, 310 insertions, 422 deletions
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index 82d68fc21d3..9187d9a2be4 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -53,7 +53,7 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
#include "mongo/rpc/factory.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
+#include "mongo/rpc/metadata/client_metadata.h"
#include "mongo/rpc/op_msg_rpc_impls.h"
#include "mongo/rpc/protocol.h"
#include "mongo/rpc/write_concern_error_detail.h"
@@ -476,6 +476,11 @@ bool CommandHelpers::shouldActivateFailCommandFailPoint(const BSONObj& data,
if (cmd->getName() == "configureFailPoint"_sd) // Banned even if in failCommands.
return false;
+ auto appName = StringData();
+ if (auto clientMetadata = ClientMetadata::get(client)) {
+ appName = clientMetadata->getApplicationName();
+ }
+
if (data.hasField("threadName") &&
(client->desc() !=
data.getStringField(
@@ -483,12 +488,8 @@ bool CommandHelpers::shouldActivateFailCommandFailPoint(const BSONObj& data,
return false;
}
- if (data.hasField("appName")) {
- const auto& clientMetadata = ClientMetadataIsMasterState::get(client).getClientMetadata();
- if (clientMetadata &&
- clientMetadata.get().getApplicationName() != data.getStringField("appName")) {
- return false; // only activate failpoint on connection with a certain appName
- }
+ if (data.hasField("appName") && (appName != data.getStringField("appName"))) {
+ return false; // only activate failpoint on connection with a certain appName
}
if (client->session() && (client->session()->getTags() & transport::Session::kInternalClient)) {
@@ -577,10 +578,8 @@ void CommandHelpers::handleMarkKillOnClientDisconnect(OperationContext* opCtx,
MONGO_FAIL_POINT_BLOCK_IF(
waitInCommandMarkKillOnClientDisconnect, options, [&](const BSONObj& obj) {
- const auto& clientMetadata =
- ClientMetadataIsMasterState::get(opCtx->getClient()).getClientMetadata();
-
- return clientMetadata && (clientMetadata->getApplicationName() == obj["appName"].str());
+ auto md = ClientMetadata::get(opCtx->getClient());
+ return md && (md->getApplicationName() == obj["appName"].str());
}) {
MONGO_FAIL_POINT_PAUSE_WHILE_SET_OR_INTERRUPTED(opCtx,
waitInCommandMarkKillOnClientDisconnect);
diff --git a/src/mongo/db/commands/authentication_commands.cpp b/src/mongo/db/commands/authentication_commands.cpp
index 3a64955228b..b56197b720a 100644
--- a/src/mongo/db/commands/authentication_commands.cpp
+++ b/src/mongo/db/commands/authentication_commands.cpp
@@ -54,7 +54,6 @@
#include "mongo/db/operation_context.h"
#include "mongo/platform/random.h"
#include "mongo/rpc/metadata/client_metadata.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
#include "mongo/stdx/memory.h"
#include "mongo/transport/session.h"
#include "mongo/util/concurrency/mutex.h"
diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp
index 9d26d1c206c..44071593f1d 100644
--- a/src/mongo/db/curop.cpp
+++ b/src/mongo/db/curop.cpp
@@ -49,7 +49,6 @@
#include "mongo/db/query/getmore_request.h"
#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/rpc/metadata/client_metadata.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
#include "mongo/rpc/metadata/impersonated_user_metadata.h"
#include "mongo/util/hex.h"
#include "mongo/util/log.h"
@@ -248,15 +247,13 @@ void CurOp::reportCurrentOpForClient(OperationContext* opCtx,
infoBuilder->append("host", hostName);
client->reportState(*infoBuilder);
- const auto& clientMetadata = ClientMetadataIsMasterState::get(client).getClientMetadata();
-
- if (clientMetadata) {
- auto appName = clientMetadata.get().getApplicationName();
+ if (auto clientMetadata = ClientMetadata::get(client)) {
+ auto appName = clientMetadata->getApplicationName();
if (!appName.empty()) {
infoBuilder->append("appName", appName);
}
- auto clientMetadataDocument = clientMetadata.get().getDocument();
+ auto clientMetadataDocument = clientMetadata->getDocument();
infoBuilder->append("clientMetadata", clientMetadataDocument);
}
@@ -649,9 +646,8 @@ string OpDebug::report(Client* client,
s << curop.getNS();
- const auto& clientMetadata = ClientMetadataIsMasterState::get(client).getClientMetadata();
- if (clientMetadata) {
- auto appName = clientMetadata.get().getApplicationName();
+ if (auto clientMetadata = ClientMetadata::get(client)) {
+ auto appName = clientMetadata->getApplicationName();
if (!appName.empty()) {
s << " appName: \"" << str::escape(appName) << '\"';
}
diff --git a/src/mongo/db/introspect.cpp b/src/mongo/db/introspect.cpp
index a75b91b8aba..a23d80ecbcc 100644
--- a/src/mongo/db/introspect.cpp
+++ b/src/mongo/db/introspect.cpp
@@ -43,7 +43,6 @@
#include "mongo/db/db_raii.h"
#include "mongo/db/jsobj.h"
#include "mongo/rpc/metadata/client_metadata.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
#include "mongo/util/log.h"
#include "mongo/util/scopeguard.h"
@@ -99,10 +98,8 @@ void profile(OperationContext* opCtx, NetworkOp op) {
b.appendDate("ts", jsTime());
b.append("client", opCtx->getClient()->clientAddress());
- const auto& clientMetadata =
- ClientMetadataIsMasterState::get(opCtx->getClient()).getClientMetadata();
- if (clientMetadata) {
- auto appName = clientMetadata.get().getApplicationName();
+ if (auto clientMetadata = ClientMetadata::get(opCtx->getClient())) {
+ auto appName = clientMetadata->getApplicationName();
if (!appName.empty()) {
b.append("appName", appName);
}
diff --git a/src/mongo/db/pipeline/pipeline_d.cpp b/src/mongo/db/pipeline/pipeline_d.cpp
index 75d9948ba05..17d00aca255 100644
--- a/src/mongo/db/pipeline/pipeline_d.cpp
+++ b/src/mongo/db/pipeline/pipeline_d.cpp
@@ -76,7 +76,7 @@
#include "mongo/db/storage/record_store.h"
#include "mongo/db/storage/sorted_data_interface.h"
#include "mongo/db/transaction_participant.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
+#include "mongo/rpc/metadata/client_metadata.h"
#include "mongo/s/catalog_cache.h"
#include "mongo/s/chunk_manager.h"
#include "mongo/s/chunk_version.h"
diff --git a/src/mongo/db/repl/replication_info.cpp b/src/mongo/db/repl/replication_info.cpp
index 2446336e382..780c5851168 100644
--- a/src/mongo/db/repl/replication_info.cpp
+++ b/src/mongo/db/repl/replication_info.cpp
@@ -59,7 +59,6 @@
#include "mongo/db/wire_version.h"
#include "mongo/executor/network_interface.h"
#include "mongo/rpc/metadata/client_metadata.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
#include "mongo/util/fail_point_service.h"
#include "mongo/util/log.h"
#include "mongo/util/map_util.h"
@@ -271,33 +270,11 @@ public:
sessionTagsToSet |= transport::Session::kKeepOpen;
}
- auto& clientMetadataIsMasterState = ClientMetadataIsMasterState::get(opCtx->getClient());
- bool seenIsMaster = clientMetadataIsMasterState.hasSeenIsMaster();
-
- if (!seenIsMaster) {
- clientMetadataIsMasterState.setSeenIsMaster();
- }
-
- BSONElement element = cmdObj[kMetadataDocumentName];
- if (!element.eoo()) {
- if (seenIsMaster) {
- uasserted(ErrorCodes::ClientMetadataCannotBeMutated,
- "The client metadata document may only be sent in the first isMaster");
- }
-
- auto parsedClientMetadata = uassertStatusOK(ClientMetadata::parse(element));
-
- invariant(parsedClientMetadata);
-
- parsedClientMetadata->logClientMetadata(opCtx->getClient());
-
- clientMetadataIsMasterState.setClientMetadata(opCtx->getClient(),
- std::move(parsedClientMetadata));
- }
-
- if (!seenIsMaster) {
- auto sniName = opCtx->getClient()->getSniNameForSession();
- SplitHorizon::setParameters(opCtx->getClient(), std::move(sniName));
+ auto client = opCtx->getClient();
+ if (ClientMetadata::tryFinalize(client)) {
+ // If we are the first hello, then set split horizon parameters.
+ auto sniName = client->getSniNameForSession();
+ SplitHorizon::setParameters(client, std::move(sniName));
}
// Parse the optional 'internalClient' field. This is provided by incoming connections from
diff --git a/src/mongo/db/s/single_transaction_coordinator_stats.h b/src/mongo/db/s/single_transaction_coordinator_stats.h
index a6d8be9b54f..9f7349cf3ed 100644
--- a/src/mongo/db/s/single_transaction_coordinator_stats.h
+++ b/src/mongo/db/s/single_transaction_coordinator_stats.h
@@ -30,7 +30,7 @@
#pragma once
#include "mongo/db/client.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
+#include "mongo/rpc/metadata/client_metadata.h"
#include "mongo/util/tick_source.h"
#include "mongo/util/time_support.h"
@@ -56,10 +56,9 @@ public:
clientHostAndPort = client->getRemote().toString();
}
connectionId = client->getConnectionId();
- if (const auto& metadata =
- ClientMetadataIsMasterState::get(client).getClientMetadata()) {
- clientMetadata = metadata.get().getDocument();
- appName = metadata.get().getApplicationName().toString();
+ if (auto metadata = ClientMetadata::get(client)) {
+ clientMetadata = metadata->getDocument();
+ appName = metadata->getApplicationName().toString();
}
}
};
diff --git a/src/mongo/db/s/transaction_coordinator_test_fixture.cpp b/src/mongo/db/s/transaction_coordinator_test_fixture.cpp
index cf752cc4dae..8bb14a17760 100644
--- a/src/mongo/db/s/transaction_coordinator_test_fixture.cpp
+++ b/src/mongo/db/s/transaction_coordinator_test_fixture.cpp
@@ -39,7 +39,6 @@
#include "mongo/db/operation_context.h"
#include "mongo/db/s/wait_for_majority_service.h"
#include "mongo/rpc/metadata/client_metadata.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
#include "mongo/s/catalog/sharding_catalog_client_mock.h"
#include "mongo/s/catalog/type_shard.h"
#include "mongo/unittest/unittest.h"
@@ -138,7 +137,6 @@ void TransactionCoordinatorTestFixture::associateClientMetadata(Client* client,
&metadataBuilder));
auto clientMetadata = metadataBuilder.obj();
auto clientMetadataParse = ClientMetadata::parse(clientMetadata["client"]);
- ClientMetadataIsMasterState::setClientMetadata(client,
- std::move(clientMetadataParse.getValue()));
+ ClientMetadata::setAndFinalize(client, std::move(clientMetadataParse.getValue()));
}
} // namespace mongo
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index e6997ed7877..41943251f8f 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -85,6 +85,7 @@
#include "mongo/rpc/get_status_from_command_result.h"
#include "mongo/rpc/message.h"
#include "mongo/rpc/metadata.h"
+#include "mongo/rpc/metadata/client_metadata.h"
#include "mongo/rpc/metadata/logical_time_metadata.h"
#include "mongo/rpc/metadata/oplog_query_metadata.h"
#include "mongo/rpc/metadata/repl_set_metadata.h"
@@ -682,6 +683,7 @@ void execCommandDatabase(OperationContext* opCtx,
auto invocation = command->parse(opCtx, request);
OperationSessionInfoFromClient sessionOptions;
+ const auto isHello = command->getName() == "hello"_sd || command->getName() == "isMaster"_sd;
try {
{
@@ -689,6 +691,13 @@ void execCommandDatabase(OperationContext* opCtx,
CurOp::get(opCtx)->setCommand_inlock(command);
}
+ if (isHello) {
+ // Preload generic ClientMetadata ahead of our first hello request. After the first
+ // request, metaElement should always be empty.
+ auto metaElem = request.body[kMetadataDocumentName];
+ ClientMetadata::setFromMetadata(opCtx->getClient(), metaElem);
+ }
+
MONGO_FAIL_POINT_BLOCK(sleepMillisAfterCommandExecutionBegins, arg) {
const BSONObj& data = arg.getData();
auto numMillis = data["millis"].numberInt();
diff --git a/src/mongo/db/stats/single_transaction_stats.h b/src/mongo/db/stats/single_transaction_stats.h
index 0afdb7f3d24..51524c8d01c 100644
--- a/src/mongo/db/stats/single_transaction_stats.h
+++ b/src/mongo/db/stats/single_transaction_stats.h
@@ -31,7 +31,6 @@
#include "mongo/db/curop.h"
#include "mongo/rpc/metadata/client_metadata.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
namespace mongo {
@@ -56,10 +55,9 @@ public:
clientHostAndPort = client->getRemote().toString();
}
connectionId = client->getConnectionId();
- if (const auto& metadata =
- ClientMetadataIsMasterState::get(client).getClientMetadata()) {
- clientMetadata = metadata.get().getDocument();
- appName = metadata.get().getApplicationName().toString();
+ if (auto metadata = ClientMetadata::get(client)) {
+ clientMetadata = metadata->getDocument();
+ appName = metadata->getApplicationName().toString();
}
}
};
diff --git a/src/mongo/db/transaction_participant_test.cpp b/src/mongo/db/transaction_participant_test.cpp
index 631b6bdff14..aa9959ed368 100644
--- a/src/mongo/db/transaction_participant_test.cpp
+++ b/src/mongo/db/transaction_participant_test.cpp
@@ -2663,7 +2663,7 @@ TEST_F(TransactionsMetricsTest, ReportStashedResources) {
auto sessionCheckout = checkOutSession();
- // Create a ClientMetadata object and set it on ClientMetadataIsMasterState.
+ // Create a ClientMetadata object and set it.
BSONObjBuilder builder;
ASSERT_OK(ClientMetadata::serializePrivate("driverName",
"driverVersion",
@@ -2675,9 +2675,7 @@ TEST_F(TransactionsMetricsTest, ReportStashedResources) {
&builder));
auto obj = builder.obj();
auto clientMetadata = ClientMetadata::parse(obj["client"]);
- auto& clientMetadataIsMasterState = ClientMetadataIsMasterState::get(opCtx()->getClient());
- clientMetadataIsMasterState.setClientMetadata(opCtx()->getClient(),
- std::move(clientMetadata.getValue()));
+ ClientMetadata::setAndFinalize(opCtx()->getClient(), std::move(clientMetadata.getValue()));
repl::ReadConcernArgs readConcernArgs;
ASSERT_OK(
@@ -2867,12 +2865,10 @@ BSONObj constructClientMetadata(StringData appName) {
} // namespace
TEST_F(TransactionsMetricsTest, LastClientInfoShouldUpdateUponStash) {
- // Create a ClientMetadata object and set it on ClientMetadataIsMasterState.
+ // Create a ClientMetadata object and set it.
auto obj = constructClientMetadata("appName");
auto clientMetadata = ClientMetadata::parse(obj["client"]);
- auto& clientMetadataIsMasterState = ClientMetadataIsMasterState::get(opCtx()->getClient());
- clientMetadataIsMasterState.setClientMetadata(opCtx()->getClient(),
- std::move(clientMetadata.getValue()));
+ ClientMetadata::setAndFinalize(opCtx()->getClient(), std::move(clientMetadata.getValue()));
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
@@ -2891,8 +2887,7 @@ TEST_F(TransactionsMetricsTest, LastClientInfoShouldUpdateUponStash) {
// Create another ClientMetadata object.
auto newObj = constructClientMetadata("newAppName");
auto newClientMetadata = ClientMetadata::parse(newObj["client"]);
- clientMetadataIsMasterState.setClientMetadata(opCtx()->getClient(),
- std::move(newClientMetadata.getValue()));
+ ClientMetadata::setAndFinalize(opCtx()->getClient(), std::move(newClientMetadata.getValue()));
txnParticipant.unstashTransactionResources(opCtx(), "insert");
txnParticipant.stashTransactionResources(opCtx());
@@ -2904,12 +2899,10 @@ TEST_F(TransactionsMetricsTest, LastClientInfoShouldUpdateUponStash) {
}
TEST_F(TransactionsMetricsTest, LastClientInfoShouldUpdateUponCommit) {
- // Create a ClientMetadata object and set it on ClientMetadataIsMasterState.
+ // Create a ClientMetadata object and set it.
auto obj = constructClientMetadata("appName");
auto clientMetadata = ClientMetadata::parse(obj["client"]);
- auto& clientMetadataIsMasterState = ClientMetadataIsMasterState::get(opCtx()->getClient());
- clientMetadataIsMasterState.setClientMetadata(opCtx()->getClient(),
- std::move(clientMetadata.getValue()));
+ ClientMetadata::setAndFinalize(opCtx()->getClient(), std::move(clientMetadata.getValue()));
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
@@ -2927,13 +2920,10 @@ TEST_F(TransactionsMetricsTest, LastClientInfoShouldUpdateUponCommit) {
}
TEST_F(TransactionsMetricsTest, LastClientInfoShouldUpdateUponAbort) {
- // Create a ClientMetadata object and set it on ClientMetadataIsMasterState.
+ // Create a ClientMetadata object and set it.
auto obj = constructClientMetadata("appName");
auto clientMetadata = ClientMetadata::parse(obj["client"]);
-
- auto& clientMetadataIsMasterState = ClientMetadataIsMasterState::get(opCtx()->getClient());
- clientMetadataIsMasterState.setClientMetadata(opCtx()->getClient(),
- std::move(clientMetadata.getValue()));
+ ClientMetadata::setAndFinalize(opCtx()->getClient(), std::move(clientMetadata.getValue()));
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());
diff --git a/src/mongo/embedded/embedded_ismaster.cpp b/src/mongo/embedded/embedded_ismaster.cpp
index e42c4292dac..aa636ab7280 100644
--- a/src/mongo/embedded/embedded_ismaster.cpp
+++ b/src/mongo/embedded/embedded_ismaster.cpp
@@ -33,7 +33,6 @@
#include "mongo/db/commands.h"
#include "mongo/db/ops/write_ops.h"
#include "mongo/rpc/metadata/client_metadata.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
namespace mongo {
namespace {
@@ -67,30 +66,9 @@ public:
const std::string&,
const BSONObj& cmdObj,
BSONObjBuilder& result) {
-
- auto& clientMetadataIsMasterState = ClientMetadataIsMasterState::get(opCtx->getClient());
- bool seenIsMaster = clientMetadataIsMasterState.hasSeenIsMaster();
- if (!seenIsMaster) {
- clientMetadataIsMasterState.setSeenIsMaster();
- }
-
- BSONElement element = cmdObj[kMetadataDocumentName];
- if (!element.eoo()) {
- if (seenIsMaster) {
- uasserted(ErrorCodes::ClientMetadataCannotBeMutated,
- "The client metadata document may only be sent in the first isMaster");
- }
-
- auto swParseClientMetadata = ClientMetadata::parse(element);
- uassertStatusOK(swParseClientMetadata.getStatus());
-
- invariant(swParseClientMetadata.getValue());
-
- swParseClientMetadata.getValue().get().logClientMetadata(opCtx->getClient());
-
- clientMetadataIsMasterState.setClientMetadata(
- opCtx->getClient(), std::move(swParseClientMetadata.getValue()));
- }
+ auto metaElem = cmdObj[kMetadataDocumentName];
+ ClientMetadata::setFromMetadata(opCtx->getClient(), metaElem);
+ ClientMetadata::tryFinalize(opCtx->getClient());
result.appendBool("ismaster", true);
diff --git a/src/mongo/rpc/SConscript b/src/mongo/rpc/SConscript
index b195aa6534e..bb324eb3346 100644
--- a/src/mongo/rpc/SConscript
+++ b/src/mongo/rpc/SConscript
@@ -181,7 +181,6 @@ env.Library(
target='client_metadata',
source=[
'metadata/client_metadata.cpp',
- 'metadata/client_metadata_ismaster.cpp',
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
diff --git a/src/mongo/rpc/metadata.cpp b/src/mongo/rpc/metadata.cpp
index 6d9e7581698..253522a9efa 100644
--- a/src/mongo/rpc/metadata.cpp
+++ b/src/mongo/rpc/metadata.cpp
@@ -38,7 +38,7 @@
#include "mongo/db/logical_clock.h"
#include "mongo/db/logical_time_validator.h"
#include "mongo/db/repl/replication_coordinator.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
+#include "mongo/rpc/metadata/client_metadata.h"
#include "mongo/rpc/metadata/config_server_metadata.h"
#include "mongo/rpc/metadata/impersonated_user_metadata.h"
#include "mongo/rpc/metadata/logical_time_metadata.h"
@@ -85,7 +85,13 @@ void readRequestMetadata(OperationContext* opCtx, const BSONObj& metadataObj, bo
readImpersonatedUserMetadata(impersonationElem, opCtx);
- uassertStatusOK(ClientMetadataIsMasterState::readFromMetadata(opCtx, clientElem));
+ // We check for "$client" but not "client" here, because currentOp can filter on "client" as
+ // a top-level field.
+ if (clientElem) {
+ // The '$client' field is populated by mongos when it sends requests to shards on behalf of
+ // its own requests. This may or may not be relevant for SERVER-50804.
+ ClientMetadata::setFromMetadataForOperation(opCtx, clientElem);
+ }
ConfigServerMetadata::get(opCtx) =
uassertStatusOK(ConfigServerMetadata::readFromMetadata(configSvrElem));
diff --git a/src/mongo/rpc/metadata/client_metadata.cpp b/src/mongo/rpc/metadata/client_metadata.cpp
index 9b51a4bc750..faa62c32102 100644
--- a/src/mongo/rpc/metadata/client_metadata.cpp
+++ b/src/mongo/rpc/metadata/client_metadata.cpp
@@ -43,8 +43,10 @@
#include "mongo/db/operation_context.h"
#include "mongo/s/is_mongos.h"
#include "mongo/util/log.h"
+#include "mongo/util/net/socket_utils.h"
#include "mongo/util/processinfo.h"
#include "mongo/util/str.h"
+#include "mongo/util/version.h"
namespace mongo {
@@ -70,6 +72,13 @@ constexpr uint32_t kMaxMongoSMetadataDocumentByteLength = 512U;
constexpr uint32_t kMaxMongoDMetadataDocumentByteLength = 1024U;
constexpr uint32_t kMaxApplicationNameByteLength = 128U;
+struct ClientMetadataState {
+ bool isFinalized = false;
+ boost::optional<ClientMetadata> meta;
+};
+const auto getClientState = Client::declareDecoration<ClientMetadataState>();
+const auto getOperationState = OperationContext::declareDecoration<ClientMetadataState>();
+
} // namespace
StatusWith<boost::optional<ClientMetadata>> ClientMetadata::parse(const BSONElement& element) {
@@ -443,4 +452,110 @@ StringData ClientMetadata::fieldName() {
return kClientMetadataFieldName;
}
+bool ClientMetadata::tryFinalize(Client* client) {
+ auto lk = stdx::unique_lock(*client);
+ auto& state = getClientState(client);
+ if (std::exchange(state.isFinalized, true)) {
+ return false;
+ }
+
+ lk.unlock();
+
+ if (state.meta) {
+ // If we reach this point, the ClientMetadata is effectively immutable because isFinalized
+ // is true.
+ state.meta->logClientMetadata(client);
+ }
+
+ return true;
+}
+
+const ClientMetadata* ClientMetadata::getForClient(Client* client) noexcept {
+ auto& state = getClientState(client);
+ if (!state.meta) {
+ // If we haven't finalized, it's still okay to return our existing value.
+ return nullptr;
+ }
+ return &state.meta.get();
+}
+
+const ClientMetadata* ClientMetadata::getForOperation(OperationContext* opCtx) noexcept {
+ auto& state = getOperationState(opCtx);
+ if (!state.isFinalized) {
+ return nullptr;
+ }
+ invariant(state.meta);
+ return &state.meta.get();
+}
+
+const ClientMetadata* ClientMetadata::get(Client* client) noexcept {
+ if (auto opCtx = client->getOperationContext()) {
+ if (auto meta = getForOperation(opCtx)) {
+ return meta;
+ }
+ }
+
+ return getForClient(client);
+}
+
+void ClientMetadata::setAndFinalize(Client* client, boost::optional<ClientMetadata> meta) {
+ auto lk = stdx::lock_guard(*client);
+
+ auto& state = getClientState(client);
+ state.isFinalized = true;
+ state.meta = std::move(meta);
+}
+
+void ClientMetadata::setFromMetadataForOperation(OperationContext* opCtx, BSONElement& elem) {
+ auto lk = stdx::lock_guard(*opCtx->getClient());
+
+ auto& state = getOperationState(opCtx);
+ auto wasFinalized = std::exchange(state.isFinalized, true);
+ uassert(ErrorCodes::ClientMetadataCannotBeMutated,
+ "The client metadata document may only be set once per operation",
+ !state.meta && !wasFinalized);
+
+ state.meta = ClientMetadata::readFromMetadata(elem);
+}
+
+void ClientMetadata::setFromMetadata(Client* client, BSONElement& elem) {
+ auto& state = getClientState(client);
+
+ {
+ auto lk = stdx::lock_guard(*client);
+ if (state.isFinalized) {
+ uassert(ErrorCodes::ClientMetadataCannotBeMutated,
+ "The client metadata document may only be sent in the first hello",
+ elem.eoo());
+ return;
+ }
+ }
+
+ auto meta = ClientMetadata::readFromMetadata(elem);
+ if (meta && isMongos()) {
+ // If we had a full ClientMetadata and we're on mongos, attach some additional client data.
+ meta->setMongoSMetadata(getHostNameCachedAndPort(),
+ client->clientAddress(true),
+ VersionInfoInterface::instance().version());
+ }
+
+ auto lk = stdx::lock_guard(*client);
+ invariant(!state.meta, "ClientMetadata was previously set, it should be set precisely once");
+ state.meta = std::move(meta);
+}
+
+boost::optional<ClientMetadata> ClientMetadata::readFromMetadata(BSONElement& element) {
+ return uassertStatusOK(ClientMetadata::parse(element));
+}
+
+void ClientMetadata::writeToMetadata(BSONObjBuilder* builder) const noexcept {
+ auto& document = getDocument();
+ if (document.isEmpty()) {
+ // Skip appending metadata if there is none
+ return;
+ }
+
+ builder->append(ClientMetadata::fieldName(), document);
+}
+
} // namespace mongo
diff --git a/src/mongo/rpc/metadata/client_metadata.h b/src/mongo/rpc/metadata/client_metadata.h
index eb467caab6a..294a88815ec 100644
--- a/src/mongo/rpc/metadata/client_metadata.h
+++ b/src/mongo/rpc/metadata/client_metadata.h
@@ -46,38 +46,35 @@ constexpr auto kMetadataDocumentName = "client"_sd;
/**
* The ClientMetadata class is responsible for parsing the client metadata document that is received
- * in isMaster from clients. This class also provides static methods for client libraries to create
- * a valid client metadata document.
+ * in the "client" field of the first hello from clients. The client metadata document can also be
+ * parsed from the "$client" field of any operation. This class also provides static methods for
+ * client libraries to write a valid client metadata document.
*
- * Example document of isMaster request with client metadata document:
+ * Example client metadata document:
* {
- * "isMaster" : 1,
- * "client" : {
- * "application" : { // Optional
- * "name" : "string" // Optional with caveats
- * },
- * "driver" : { // Required, Informational Only
- * "name" : "string", // Required, Informational Only
- * "version" : "string" // Required, Informational Only
- * },
- * "os" : { // Required, Informational Only
- * "type" : "string", // Required, Informational Only, See note
- * "name" : "string", // Optional, Informational Only
- * "architecture" : "string", // Optional, Informational Only
- * "version" : "string" // Optional, Informational Only
- * }
- * "mongos" : { // Optional, Informational Only
- * "host" : "string", // Optional, Informational Only
- * "client" : "string", // Optional, Informational Only
- * "version" : "string" // Optional, Informational Only
- * }
- * }
+ * "application" : { // Optional
+ * "name" : "string" // Optional with caveats
+ * },
+ * "driver" : { // Required, Informational Only
+ * "name" : "string", // Required, Informational Only
+ * "version" : "string" // Required, Informational Only
+ * },
+ * "os" : { // Required, Informational Only
+ * "type" : "string", // Required, Informational Only, See note
+ * "name" : "string", // Optional, Informational Only
+ * "architecture" : "string", // Optional, Informational Only
+ * "version" : "string" // Optional, Informational Only
+ * }
+ * "mongos" : { // Optional, Informational Only
+ * "host" : "string", // Optional, Informational Only
+ * "client" : "string", // Optional, Informational Only
+ * "version" : "string" // Optional, Informational Only
+ * }
* }
*
- * For this classes' purposes, the client metadata document is the sub-document in "client". It is
- * allowed to contain additional fields that are not listed in the example above. These additional
- * fields are ignore by this class. The "os" document "type" field is required (defaults to
- * "unknown" in Mongo Drivers). The "driver", and "os" documents while required, are for
+ * It is allowed to contain additional fields that are not listed in the example above. These
+ * additional fields are ignore by this class. The "os" document "type" field is required (defaults
+ * to "unknown" in Mongo Drivers). The "driver", and "os" documents while required, are for
* informational purposes only. The content is logged to disk but otherwise ignored.
*
* See Driver Specification: "MongoDB Handshake" for more information.
@@ -91,7 +88,54 @@ public:
ClientMetadata& operator=(ClientMetadata&&) = default;
/**
- * Parse and validate a client metadata document contained in an isMaster request.
+ * Get the ClientMetadata for the Client.
+ *
+ * This function may return nullptr if there was no ClientMetadata provided for the
+ * Client.
+ *
+ * The pointer to ClientMetadata is valid to use if:
+ * - You hold the Client lock.
+ * - You are on the Client's thread.
+ */
+ static const ClientMetadata* getForClient(Client* client) noexcept;
+
+ /**
+ * Get the ClientMetadata for the OperationContext.
+ *
+ * This function may return nullptr if there was no ClientMetadata provided for the
+ * OperationContext.
+ *
+ * The pointer to ClientMetadata is valid to use if:
+ * - You hold the Client lock.
+ * - You are on the Client's thread.
+ */
+ static const ClientMetadata* getForOperation(OperationContext* opCtx) noexcept;
+
+ /**
+ * Get the prioritized ClientMetadata for the Client.
+ *
+ * This function returns getForOperation() if it returns a valid pointer, otherwise it returns
+ * getForClient().
+ *
+ * The pointer to ClientMetadata is valid to use if:
+ * - You hold the Client lock.
+ * - You are on the Client's thread.
+ */
+ static const ClientMetadata* get(Client* client) noexcept;
+
+ /**
+ * Set the ClientMetadata for the Client directly.
+ *
+ * This should only be used in testing. It sets the ClientMetadata as finalized but does not
+ * check if it was previously finalized. It allows the user to replace the ClientMetadata for
+ * a Client, which is disallowed if done via setFromMetadata().
+ *
+ * This function takes the Client lock.
+ */
+ static void setAndFinalize(Client* client, boost::optional<ClientMetadata> meta);
+
+ /**
+ * Parse and validate a client metadata document contained in a hello request.
*
* Empty or non-existent sub-documents are permitted. Non-empty documents are required to have
* the fields driver.name, driver.version, and os.type which must be strings.
@@ -155,6 +199,47 @@ public:
BSONObjBuilder* builder);
/**
+ * Mark the ClientMetadata as finalized.
+ *
+ * Once this function is called, no future hello can mutate the ClientMetadata.
+ *
+ * This function takes the Client lock.
+ */
+ static bool tryFinalize(Client* client);
+
+ /**
+ * Set the ClientMetadata for the Client by reading it from the given BSONElement.
+ *
+ * This function throws if the ClientMetadata has already been finalized but the BSONElement is
+ * an object. ClientMetadata is allowed to be set via the first hello only.
+ *
+ * This function takes the Client lock.
+ */
+ static void setFromMetadata(Client* client, BSONElement& elem);
+
+ /**
+ * Set the ClientMetadata for the OperationContext by reading it from the given BSONElement.
+ *
+ * This function throws if called more than once for the same OperationContext.
+ *
+ * This function takes the Client lock.
+ */
+ static void setFromMetadataForOperation(OperationContext* opCtx, BSONElement& elem);
+
+ /**
+ * Read from the $client field in requests.
+ *
+ * Throws an error if the $client section is not valid. It is valid for it to not exist though.
+ */
+ static boost::optional<ClientMetadata> readFromMetadata(BSONElement& elem);
+
+ /**
+ * Write the $client section to request bodies if there is a non-empty client metadata
+ * connection with the current client.
+ */
+ void writeToMetadata(BSONObjBuilder* builder) const noexcept;
+
+ /**
* Modify the existing client metadata document to include a mongos section.
*
* hostAndPort is "host:port" of the running MongoS.
diff --git a/src/mongo/rpc/metadata/client_metadata_ismaster.cpp b/src/mongo/rpc/metadata/client_metadata_ismaster.cpp
deleted file mode 100644
index d095fa6ed1d..00000000000
--- a/src/mongo/rpc/metadata/client_metadata_ismaster.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
-
-#include <string>
-
-#include "mongo/base/init.h"
-#include "mongo/base/status.h"
-#include "mongo/db/client.h"
-#include "mongo/db/operation_context.h"
-#include "mongo/db/service_context.h"
-#include "mongo/stdx/memory.h"
-
-namespace mongo {
-
-namespace {
-
-const auto getClientMetadataIsMasterState =
- Client::declareDecoration<ClientMetadataIsMasterState>();
-
-} // namespace
-
-ClientMetadataIsMasterState& ClientMetadataIsMasterState::get(Client* client) {
- return getClientMetadataIsMasterState(*client);
-}
-
-bool ClientMetadataIsMasterState::hasSeenIsMaster() const {
- return _hasSeenIsMaster;
-}
-
-void ClientMetadataIsMasterState::setSeenIsMaster() {
- invariant(!_hasSeenIsMaster);
- _hasSeenIsMaster = true;
-}
-
-const boost::optional<ClientMetadata>& ClientMetadataIsMasterState::getClientMetadata() const {
- return _clientMetadata;
-}
-
-void ClientMetadataIsMasterState::setClientMetadata(Client* client,
- boost::optional<ClientMetadata> clientMetadata,
- bool setViaMetadata) {
- auto& state = get(client);
-
- stdx::lock_guard<Client> lk(*client);
- state._clientMetadata = std::move(clientMetadata);
- state._setViaMetadata = setViaMetadata;
-}
-
-
-Status ClientMetadataIsMasterState::readFromMetadata(OperationContext* opCtx,
- BSONElement& element) {
- auto& clientMetadataIsMasterState = ClientMetadataIsMasterState::get(opCtx->getClient());
-
- // If client metadata is not present in network requests, reset the in-memory metadata to be
- // blank so that the wrong
- // app name is not propagated.
- if (element.eoo()) {
- auto client = opCtx->getClient();
-
- if (clientMetadataIsMasterState._setViaMetadata && !client->isInDirectClient()) {
- clientMetadataIsMasterState.setClientMetadata(client, boost::none, true);
- }
-
- return Status::OK();
- }
-
- auto swParseClientMetadata = ClientMetadata::parse(element);
-
- if (!swParseClientMetadata.getStatus().isOK()) {
- return swParseClientMetadata.getStatus();
- }
-
- clientMetadataIsMasterState.setClientMetadata(
- opCtx->getClient(), std::move(swParseClientMetadata.getValue()), true);
-
- return Status::OK();
-}
-
-void ClientMetadataIsMasterState::writeToMetadata(OperationContext* opCtx,
- BSONObjBuilder* builder) {
- // We may be asked to write metadata on background threads that are not associated with an
- // operation context
- if (!opCtx) {
- return;
- }
-
- const auto& clientMetadata =
- ClientMetadataIsMasterState::get(opCtx->getClient()).getClientMetadata();
-
- // Skip appending metadata if there is none
- if (!clientMetadata || clientMetadata.get().getDocument().isEmpty()) {
- return;
- }
-
- builder->append(ClientMetadata::fieldName(), clientMetadata.get().getDocument());
-}
-
-} // namespace mongo
diff --git a/src/mongo/rpc/metadata/client_metadata_ismaster.h b/src/mongo/rpc/metadata/client_metadata_ismaster.h
deleted file mode 100644
index 91214a8ccab..00000000000
--- a/src/mongo/rpc/metadata/client_metadata_ismaster.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include <boost/optional.hpp>
-#include <memory>
-#include <string>
-
-#include "mongo/rpc/metadata/client_metadata.h"
-
-namespace mongo {
-
-class Client;
-
-/**
- * ClientMetadataIsMasterState is responsible for tracking whether the client metadata document has
- * been received by the specified Client object.
- */
-class ClientMetadataIsMasterState {
- ClientMetadataIsMasterState(const ClientMetadataIsMasterState&) = delete;
- ClientMetadataIsMasterState& operator=(const ClientMetadataIsMasterState&) = delete;
-
-public:
- ClientMetadataIsMasterState() = default;
-
- static ClientMetadataIsMasterState& get(Client* client);
-
- /**
- * Get the optional client metadata object.
- */
- const boost::optional<ClientMetadata>& getClientMetadata() const;
-
- /**
- * Set the optional client metadata object.
- */
- static void setClientMetadata(Client* client,
- boost::optional<ClientMetadata> clientMetadata,
- bool setViaMetadata = false);
-
- /**
- * Check a flag to indicate that isMaster has been seen for this Client.
- */
- bool hasSeenIsMaster() const;
-
- /**
- * Set a flag to indicate that isMaster has been seen for this Client.
- */
- void setSeenIsMaster();
-
- /**
- * Read from the $client field in requests.
- *
- * Returns an error if the $client section is not valid. It is valid for it to not exist though.
- *
- * Thread-Safety:
- * None - must be only be read and written from the thread owning "Client".
- */
- static Status readFromMetadata(OperationContext* opCtx, BSONElement& elem);
-
- /**
- * Write the $client section to request bodies if there is a non-empty client metadata
- * connection with the current client.
- *
- * Thread-Safety:
- * None - must be only be read and written from the thread owning "Client".
- */
- static void writeToMetadata(OperationContext* opCtx, BSONObjBuilder* builder);
-
-private:
- // Optional client metadata document.
- // Set if client sees isMaster cmd or the $client field.
- // Thread-Safety:
- // Can be read and written from the thread owning "Client".
- // Can be read from other threads if they hold the "Client" lock.
- boost::optional<ClientMetadata> _clientMetadata{boost::none};
-
- // Indicates whether we have seen an is master for this client.
- // Thread-Safety:
- // None - must be only be read and written from the thread owning "Client".
- bool _hasSeenIsMaster{false};
-
- // Indicates whether we have set isMaster based on metadata or via isMaster
- // Thread-Safety:
- // None - must be only be read and written from the thread owning "Client".
- bool _setViaMetadata{false};
-};
-
-} // namespace mongo
diff --git a/src/mongo/s/commands/cluster_is_master_cmd.cpp b/src/mongo/s/commands/cluster_is_master_cmd.cpp
index 260e15800f8..d5a13cd2a3c 100644
--- a/src/mongo/s/commands/cluster_is_master_cmd.cpp
+++ b/src/mongo/s/commands/cluster_is_master_cmd.cpp
@@ -39,7 +39,6 @@
#include "mongo/db/ops/write_ops.h"
#include "mongo/db/wire_version.h"
#include "mongo/rpc/metadata/client_metadata.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
#include "mongo/transport/message_compressor_manager.h"
#include "mongo/util/map_util.h"
#include "mongo/util/net/socket_utils.h"
@@ -84,34 +83,8 @@ public:
BSONObjBuilder& result) override {
CommandHelpers::handleMarkKillOnClientDisconnect(opCtx);
- auto& clientMetadataIsMasterState = ClientMetadataIsMasterState::get(opCtx->getClient());
- bool seenIsMaster = clientMetadataIsMasterState.hasSeenIsMaster();
- if (!seenIsMaster) {
- clientMetadataIsMasterState.setSeenIsMaster();
- }
-
- BSONElement element = cmdObj[kMetadataDocumentName];
- if (!element.eoo()) {
- if (seenIsMaster) {
- uasserted(ErrorCodes::ClientMetadataCannotBeMutated,
- "The client metadata document may only be sent in the first isMaster");
- }
-
- auto swParseClientMetadata = ClientMetadata::parse(element);
- uassertStatusOK(swParseClientMetadata.getStatus());
-
- invariant(swParseClientMetadata.getValue());
-
- swParseClientMetadata.getValue().get().logClientMetadata(opCtx->getClient());
-
- swParseClientMetadata.getValue().get().setMongoSMetadata(
- getHostNameCachedAndPort(),
- opCtx->getClient()->clientAddress(true),
- VersionInfoInterface::instance().version());
-
- clientMetadataIsMasterState.setClientMetadata(
- opCtx->getClient(), std::move(swParseClientMetadata.getValue()));
- }
+ auto client = opCtx->getClient();
+ ClientMetadata::tryFinalize(client);
if (useLegacyResponseFields()) {
result.appendBool("ismaster", true);
diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp
index e03b3cbeb7c..b5342cb1509 100644
--- a/src/mongo/s/commands/strategy.cpp
+++ b/src/mongo/s/commands/strategy.cpp
@@ -62,6 +62,7 @@
#include "mongo/db/views/resolved_view.h"
#include "mongo/rpc/factory.h"
#include "mongo/rpc/get_status_from_command_result.h"
+#include "mongo/rpc/metadata/client_metadata.h"
#include "mongo/rpc/metadata/logical_time_metadata.h"
#include "mongo/rpc/metadata/tracking_metadata.h"
#include "mongo/rpc/op_msg.h"
@@ -354,6 +355,8 @@ void runCommand(OperationContext* opCtx,
return;
}
+ const auto isHello = command->getName() == "hello"_sd || command->getName() == "isMaster"_sd;
+
CommandHelpers::uassertShouldAttemptParse(opCtx, command, request);
// Parse the 'maxTimeMS' command option, and use it to set a deadline for the operation on
@@ -418,6 +421,13 @@ void runCommand(OperationContext* opCtx,
boost::optional<RouterOperationContextSession> routerSession;
try {
+ if (isHello) {
+ // Preload generic ClientMetadata ahead of our first hello request. After the first
+ // request, metaElement should always be empty.
+ auto metaElem = request.body[kMetadataDocumentName];
+ ClientMetadata::setFromMetadata(opCtx->getClient(), metaElem);
+ }
+
CommandHelpers::evaluateFailCommandFailPoint(opCtx, invocation.get());
if (osi.getAutocommit()) {
routerSession.emplace(opCtx);
diff --git a/src/mongo/s/sharding_egress_metadata_hook.cpp b/src/mongo/s/sharding_egress_metadata_hook.cpp
index 10e837a2430..ec01d1be976 100644
--- a/src/mongo/s/sharding_egress_metadata_hook.cpp
+++ b/src/mongo/s/sharding_egress_metadata_hook.cpp
@@ -35,7 +35,7 @@
#include "mongo/base/status.h"
#include "mongo/db/service_context.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
+#include "mongo/rpc/metadata/client_metadata.h"
#include "mongo/rpc/metadata/config_server_metadata.h"
#include "mongo/rpc/metadata/impersonated_user_metadata.h"
#include "mongo/rpc/metadata/metadata_hook.h"
@@ -57,7 +57,12 @@ Status ShardingEgressMetadataHook::writeRequestMetadata(OperationContext* opCtx,
BSONObjBuilder* metadataBob) {
try {
writeAuthDataToImpersonatedUserMetadata(opCtx, metadataBob);
- ClientMetadataIsMasterState::writeToMetadata(opCtx, metadataBob);
+ if (opCtx) {
+ auto md = ClientMetadata::get(opCtx->getClient());
+ if (md) {
+ md->writeToMetadata(metadataBob);
+ }
+ }
rpc::ConfigServerMetadata(_getConfigServerOpTime()).writeToMetadata(metadataBob);
return Status::OK();
} catch (...) {
diff --git a/src/mongo/s/transaction_router_test.cpp b/src/mongo/s/transaction_router_test.cpp
index 17b122340c6..40844906eb5 100644
--- a/src/mongo/s/transaction_router_test.cpp
+++ b/src/mongo/s/transaction_router_test.cpp
@@ -4617,10 +4617,8 @@ TEST_F(TransactionRouterMetricsTest, ReportResources) {
auto obj = builder.obj();
auto clientMetadata = ClientMetadata::parse(obj["client"]);
- auto& clientMetadataIsMasterState =
- ClientMetadataIsMasterState::get(operationContext()->getClient());
- clientMetadataIsMasterState.setClientMetadata(operationContext()->getClient(),
- std::move(clientMetadata.getValue()));
+ ClientMetadata::setAndFinalize(operationContext()->getClient(),
+ std::move(clientMetadata.getValue()));
repl::ReadConcernArgs readConcernArgs;
ASSERT_OK(