diff options
author | Ben Caimano <ben.caimano@10gen.com> | 2020-10-08 02:02:24 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-10-15 17:21:11 +0000 |
commit | 5a9d2dd204f41b4a02fe83d710f3f38c6092b322 (patch) | |
tree | a45a543241ad8b4159e3356ce4d4db151dd5af07 | |
parent | dba33bef953f8d1740523c159c66f0fb3b9b53d4 (diff) | |
download | mongo-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)
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( |