summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Caimano <ben.caimano@10gen.com>2020-10-08 02:02:24 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-10-15 17:21:11 +0000
commit5a9d2dd204f41b4a02fe83d710f3f38c6092b322 (patch)
treea45a543241ad8b4159e3356ce4d4db151dd5af07
parentdba33bef953f8d1740523c159c66f0fb3b9b53d4 (diff)
downloadmongo-5a9d2dd204f41b4a02fe83d710f3f38c6092b322.tar.gz
SERVER-49336 Set ClientMetadata before CommandInvocations are run
This patch does the following: - Always parses ClientMetadata before command implementations are run. - Marks general ClientMetadata as final in the first hello. - Removes client_metadata_ismaster.* (SERVER-50517) - Switches references to "isMaster" to "hello" (SERVER-50517) - Binds "$client" metadata as a decoration (SERVER-50804)
-rw-r--r--src/mongo/db/client_metadata_propagation_egress_hook.cpp11
-rw-r--r--src/mongo/db/commands.cpp9
-rw-r--r--src/mongo/db/commands/authentication_commands.cpp5
-rw-r--r--src/mongo/db/curop.cpp25
-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.cpp18
-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.cpp26
-rw-r--r--src/mongo/rpc/SConscript1
-rw-r--r--src/mongo/rpc/metadata.cpp11
-rw-r--r--src/mongo/rpc/metadata/client_metadata.cpp116
-rw-r--r--src/mongo/rpc/metadata/client_metadata.h144
-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.cpp31
-rw-r--r--src/mongo/s/sharding_egress_metadata_hook.cpp2
-rw-r--r--src/mongo/s/transaction_router_test.cpp6
23 files changed, 329 insertions, 441 deletions
diff --git a/src/mongo/db/client_metadata_propagation_egress_hook.cpp b/src/mongo/db/client_metadata_propagation_egress_hook.cpp
index 2da3c615fd5..3da25eaf5e4 100644
--- a/src/mongo/db/client_metadata_propagation_egress_hook.cpp
+++ b/src/mongo/db/client_metadata_propagation_egress_hook.cpp
@@ -29,7 +29,7 @@
#include "mongo/db/client_metadata_propagation_egress_hook.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
+#include "mongo/rpc/metadata/client_metadata.h"
#include "mongo/rpc/metadata/impersonated_user_metadata.h"
namespace mongo {
@@ -37,9 +37,16 @@ namespace rpc {
Status ClientMetadataPropagationEgressHook::writeRequestMetadata(OperationContext* opCtx,
BSONObjBuilder* metadataBob) {
+ if (!opCtx) {
+ return Status::OK();
+ }
+
try {
writeAuthDataToImpersonatedUserMetadata(opCtx, metadataBob);
- ClientMetadataIsMasterState::writeToMetadata(opCtx, metadataBob);
+
+ if (auto metadata = ClientMetadata::get(opCtx->getClient())) {
+ metadata->writeToMetadata(metadataBob);
+ }
return Status::OK();
} catch (...) {
return exceptionToStatus();
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index 8d9a8de296b..2232eee33ce 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -56,7 +56,7 @@
#include "mongo/db/read_write_concern_defaults.h"
#include "mongo/logv2/log.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"
@@ -553,8 +553,8 @@ bool CommandHelpers::shouldActivateFailCommandFailPoint(const BSONObj& data,
auto threadName = client->desc();
auto appName = StringData();
- if (const auto& clientMetadata = ClientMetadataIsMasterState::get(client).getClientMetadata()) {
- appName = clientMetadata.get().getApplicationName();
+ if (auto clientMetadata = ClientMetadata::get(client)) {
+ appName = clientMetadata->getApplicationName();
}
auto isInternalClient = client->session()->getTags() & transport::Session::kInternalClient;
@@ -703,8 +703,7 @@ void CommandHelpers::handleMarkKillOnClientDisconnect(OperationContext* opCtx,
waitInCommandMarkKillOnClientDisconnect.executeIf(
[&](const BSONObj&) { waitInCommandMarkKillOnClientDisconnect.pauseWhileSet(opCtx); },
[&](const BSONObj& obj) {
- const auto& md =
- ClientMetadataIsMasterState::get(opCtx->getClient()).getClientMetadata();
+ auto md = ClientMetadata::get(opCtx->getClient());
return md && (md->getApplicationName() == obj["appName"].str());
});
}
diff --git a/src/mongo/db/commands/authentication_commands.cpp b/src/mongo/db/commands/authentication_commands.cpp
index 4bdb7f67b62..b9c48b18495 100644
--- a/src/mongo/db/commands/authentication_commands.cpp
+++ b/src/mongo/db/commands/authentication_commands.cpp
@@ -58,7 +58,6 @@
#include "mongo/logv2/log.h"
#include "mongo/platform/random.h"
#include "mongo/rpc/metadata/client_metadata.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
#include "mongo/transport/session.h"
#include "mongo/util/concurrency/mutex.h"
#include "mongo/util/net/ssl_manager.h"
@@ -114,9 +113,7 @@ Status _authenticateX509(OperationContext* opCtx, const UserName& user, const BS
"authentication. The current configuration does not allow "
"x.509 cluster authentication, check the --clusterAuthMode flag");
}
- auto& clientMetadata =
- ClientMetadataIsMasterState::get(opCtx->getClient()).getClientMetadata();
- if (clientMetadata) {
+ if (auto clientMetadata = ClientMetadata::get(opCtx->getClient())) {
auto clientMetadataDoc = clientMetadata->getDocument();
auto driverName = clientMetadataDoc.getObjectField("driver"_sd)
.getField("name"_sd)
diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp
index 78af5154bae..e00d6a3b6ba 100644
--- a/src/mongo/db/curop.cpp
+++ b/src/mongo/db/curop.cpp
@@ -52,7 +52,6 @@
#include "mongo/db/query/plan_summary_stats.h"
#include "mongo/logv2/log.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_with_sampling.h"
@@ -261,15 +260,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);
}
@@ -784,9 +781,8 @@ string OpDebug::report(OperationContext* opCtx, const SingleThreadedLockStats* l
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) << '\"';
}
@@ -956,9 +952,8 @@ void OpDebug::report(OperationContext* opCtx,
pAttrs->addDeepCopy("ns", curop.getNS());
- const auto& clientMetadata = ClientMetadataIsMasterState::get(client).getClientMetadata();
- if (clientMetadata) {
- StringData appName = clientMetadata.get().getApplicationName();
+ if (auto clientMetadata = ClientMetadata::get(client)) {
+ StringData appName = clientMetadata->getApplicationName();
if (!appName.empty()) {
pAttrs->add("appName", appName);
}
@@ -1299,10 +1294,8 @@ std::function<BSONObj(ProfileFilter::Args)> OpDebug::appendStaged(StringSet requ
b.append(field, args.opCtx->getClient()->clientAddress());
});
addIfNeeded("appName", [](auto field, auto args, auto& b) {
- const auto& clientMetadata =
- ClientMetadataIsMasterState::get(args.opCtx->getClient()).getClientMetadata();
- if (clientMetadata) {
- auto appName = clientMetadata.get().getApplicationName();
+ if (auto clientMetadata = ClientMetadata::get(args.opCtx->getClient())) {
+ auto appName = clientMetadata->getApplicationName();
if (!appName.empty()) {
b.append(field, appName);
}
diff --git a/src/mongo/db/introspect.cpp b/src/mongo/db/introspect.cpp
index 307723838fd..89fea0c89cb 100644
--- a/src/mongo/db/introspect.cpp
+++ b/src/mongo/db/introspect.cpp
@@ -46,7 +46,6 @@
#include "mongo/db/stats/resource_consumption_metrics.h"
#include "mongo/logv2/log.h"
#include "mongo/rpc/metadata/client_metadata.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
#include "mongo/util/scopeguard.h"
namespace mongo {
@@ -79,10 +78,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 a2a383ed0fe..2915c9c99f6 100644
--- a/src/mongo/db/pipeline/pipeline_d.cpp
+++ b/src/mongo/db/pipeline/pipeline_d.cpp
@@ -77,7 +77,7 @@
#include "mongo/db/stats/top.h"
#include "mongo/db/storage/record_store.h"
#include "mongo/db/storage/sorted_data_interface.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
+#include "mongo/rpc/metadata/client_metadata.h"
#include "mongo/s/query/document_source_merge_cursors.h"
#include "mongo/util/time_support.h"
diff --git a/src/mongo/db/repl/replication_info.cpp b/src/mongo/db/repl/replication_info.cpp
index 183888255a3..3a024c8450a 100644
--- a/src/mongo/db/repl/replication_info.cpp
+++ b/src/mongo/db/repl/replication_info.cpp
@@ -61,7 +61,6 @@
#include "mongo/executor/network_interface.h"
#include "mongo/logv2/log.h"
#include "mongo/rpc/metadata/client_metadata.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
#include "mongo/transport/ismaster_metrics.h"
#include "mongo/util/decimal_counter.h"
#include "mongo/util/fail_point.h"
@@ -289,33 +288,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 256d0905278..377a1993213 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/repl/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"
@@ -137,7 +136,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 b4fa419ce16..540a4480ca1 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -91,6 +91,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/oplog_query_metadata.h"
#include "mongo/rpc/metadata/repl_set_metadata.h"
#include "mongo/rpc/metadata/tracking_metadata.h"
@@ -990,6 +991,8 @@ void execCommandDatabase(OperationContext* opCtx,
const auto isInternalClient = opCtx->getClient()->session() &&
(opCtx->getClient()->session()->getTags() & transport::Session::kInternalClient);
+ const auto isHello = command->getName() == "hello"_sd || command->getName() == "isMaster"_sd;
+
try {
const auto apiParamsFromClient = initializeAPIParameters(opCtx, request.body, command);
Client* client = opCtx->getClient();
@@ -1000,11 +1003,17 @@ void execCommandDatabase(OperationContext* opCtx,
APIParameters::get(opCtx) = APIParameters::fromClient(apiParamsFromClient);
}
+ 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);
+ }
+
auto& apiParams = APIParameters::get(opCtx);
auto& apiVersionMetrics = APIVersionMetrics::get(opCtx->getServiceContext());
- const auto& clientMetadata = ClientMetadataIsMasterState::get(client).getClientMetadata();
- if (clientMetadata) {
- auto appName = clientMetadata.get().getApplicationName().toString();
+ if (auto clientMetadata = ClientMetadata::get(client)) {
+ auto appName = clientMetadata->getApplicationName().toString();
apiVersionMetrics.update(appName, apiParams);
}
@@ -1166,8 +1175,7 @@ void execCommandDatabase(OperationContext* opCtx,
// The "hello" or "isMaster" commands should not inherit the deadline from the user op it is
// operating as a part of as that can interfere with replica set monitoring and host
// selection.
- bool ignoreMaxTimeMSOpOnly =
- command->getName() == "hello"_sd || command->getName() == "isMaster"_sd;
+ const bool ignoreMaxTimeMSOpOnly = isHello;
if ((maxTimeMS > 0 || maxTimeMSOpOnly > 0) &&
command->getLogicalOp() != LogicalOp::opGetMore) {
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 c2c18a05c8f..831cab5b825 100644
--- a/src/mongo/db/transaction_participant_test.cpp
+++ b/src/mongo/db/transaction_participant_test.cpp
@@ -2704,7 +2704,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",
@@ -2716,9 +2716,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(
@@ -2948,12 +2946,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());
@@ -2972,8 +2968,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());
@@ -2985,12 +2980,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());
@@ -3008,13 +3001,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 b304a7a9148..94f1a557ee1 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 {
@@ -69,29 +68,10 @@ public:
BSONObjBuilder& result) {
auto wireSpec = WireSpec::instance().get();
- 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 e5d6a85c1bc..aeec34c5414 100644
--- a/src/mongo/rpc/SConscript
+++ b/src/mongo/rpc/SConscript
@@ -134,7 +134,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 a9747811af1..a6e9bde0ebf 100644
--- a/src/mongo/rpc/metadata.cpp
+++ b/src/mongo/rpc/metadata.cpp
@@ -37,7 +37,7 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/logical_time_validator.h"
#include "mongo/db/vector_clock.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/sharding_metadata.h"
@@ -59,6 +59,7 @@ void readRequestMetadata(OperationContext* opCtx,
BSONElement configSvrElem;
BSONElement trackingElem;
BSONElement clientElem;
+ BSONElement helloClientElem;
BSONElement impersonationElem;
BSONElement clientOperationKeyElem;
@@ -96,7 +97,13 @@ void readRequestMetadata(OperationContext* opCtx,
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 e9203e66bb2..a4b78080ec9 100644
--- a/src/mongo/rpc/metadata/client_metadata.cpp
+++ b/src/mongo/rpc/metadata/client_metadata.cpp
@@ -44,8 +44,11 @@
#include "mongo/logv2/log.h"
#include "mongo/s/is_mongos.h"
#include "mongo/util/debug_util.h"
+#include "mongo/util/net/socket_utils.h"
#include "mongo/util/processinfo.h"
#include "mongo/util/str.h"
+#include "mongo/util/testing_proctor.h"
+#include "mongo/util/version.h"
namespace mongo {
@@ -72,6 +75,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) {
@@ -442,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.isFinalized || !state.meta) {
+ 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) {
+ invariant(TestingProctor::instance().isEnabled());
+
+ 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",
+ !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);
+ 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 524fdda3710..ff17cf60720 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,55 @@ 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 for either of two reasons:
+ * - The ClientMetadata hasn't been finalized yet via tryFinalize().
+ * - The ClientMetadata wasn't provided at all.
+ *
+ * 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 +200,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 15a3dc7482d..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 <memory>
-#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"
-
-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 532f29c7e04..1ae4b497576 100644
--- a/src/mongo/s/commands/cluster_is_master_cmd.cpp
+++ b/src/mongo/s/commands/cluster_is_master_cmd.cpp
@@ -42,7 +42,6 @@
#include "mongo/db/wire_version.h"
#include "mongo/logv2/log.h"
#include "mongo/rpc/metadata/client_metadata.h"
-#include "mongo/rpc/metadata/client_metadata_ismaster.h"
#include "mongo/rpc/topology_version_gen.h"
#include "mongo/s/mongos_topology_coordinator.h"
#include "mongo/transport/message_compressor_manager.h"
@@ -99,34 +98,8 @@ public:
waitInHello.pauseWhileSet(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 a client is following the awaitable isMaster protocol, maxAwaitTimeMS should be
// present if and only if topologyVersion is present in the request.
diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp
index d2b88b8c78f..364b385ec8d 100644
--- a/src/mongo/s/commands/strategy.cpp
+++ b/src/mongo/s/commands/strategy.cpp
@@ -68,6 +68,7 @@
#include "mongo/logv2/log.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/tracking_metadata.h"
#include "mongo/rpc/op_msg.h"
#include "mongo/rpc/op_msg_rpc_impls.h"
@@ -312,11 +313,12 @@ void runCommand(OperationContext* opCtx,
return;
}
+ const auto isHello = command->getName() == "hello"_sd || command->getName() == "isMaster"_sd;
+
opCtx->setExhaust(OpMsg::isFlagSet(m, OpMsg::kExhaustSupported));
const auto session = opCtx->getClient()->session();
if (session) {
- if (!opCtx->isExhaust() ||
- (command->getName() != "hello"_sd && command->getName() != "isMaster"_sd)) {
+ if (!opCtx->isExhaust() || !isHello) {
InExhaustHello::get(session.get())->setInExhaust(false, commandName);
}
}
@@ -390,19 +392,26 @@ void runCommand(OperationContext* opCtx,
return;
}
- auto& apiParams = APIParameters::get(opCtx);
- auto& apiVersionMetrics = APIVersionMetrics::get(opCtx->getServiceContext());
- const auto& clientMetadata = ClientMetadataIsMasterState::get(client).getClientMetadata();
- if (clientMetadata) {
- auto appName = clientMetadata.get().getApplicationName().toString();
- apiVersionMetrics.update(appName, apiParams);
- }
-
- 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);
+ }
+
+ auto& apiParams = APIParameters::get(opCtx);
+ auto& apiVersionMetrics = APIVersionMetrics::get(opCtx->getServiceContext());
+ if (auto clientMetadata = ClientMetadata::get(client)) {
+ auto appName = clientMetadata->getApplicationName().toString();
+ apiVersionMetrics.update(appName, apiParams);
+ }
+
rpc::readRequestMetadata(opCtx, request.body, command->requiresAuth());
CommandHelpers::evaluateFailCommandFailPoint(opCtx, invocation.get());
+
+ boost::optional<RouterOperationContextSession> routerSession;
bool startTransaction = false;
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 3db174e499e..9f0d11eeff7 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"
diff --git a/src/mongo/s/transaction_router_test.cpp b/src/mongo/s/transaction_router_test.cpp
index bd6e503c0e1..964c8a38994 100644
--- a/src/mongo/s/transaction_router_test.cpp
+++ b/src/mongo/s/transaction_router_test.cpp
@@ -4848,10 +4848,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(