diff options
author | Varun Ravichandran <varun.ravichandran@mongodb.com> | 2022-05-09 14:50:24 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-05-28 03:13:16 +0000 |
commit | c9e3facf5dd94fd39066f54d44ee846f750c988b (patch) | |
tree | 415dd8026b69345e965a5ee57c19b6a9a964b7b2 /src | |
parent | 136275a221896f712ee6ba874f6fb0aeb260cb28 (diff) | |
download | mongo-c9e3facf5dd94fd39066f54d44ee846f750c988b.tar.gz |
SERVER-62264: Periodically refresh cluster server parameters on mongos
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/commands/SConscript | 8 | ||||
-rw-r--r-- | src/mongo/db/commands/get_cluster_parameter_command.cpp | 73 | ||||
-rw-r--r-- | src/mongo/db/commands/get_cluster_parameter_invocation.cpp | 195 | ||||
-rw-r--r-- | src/mongo/db/commands/get_cluster_parameter_invocation.h | 61 | ||||
-rw-r--r-- | src/mongo/db/s/SConscript | 4 | ||||
-rw-r--r-- | src/mongo/idl/SConscript | 16 | ||||
-rw-r--r-- | src/mongo/idl/cluster_server_parameter_refresher.cpp | 207 | ||||
-rw-r--r-- | src/mongo/idl/cluster_server_parameter_refresher.h | 76 | ||||
-rw-r--r-- | src/mongo/idl/cluster_server_parameter_refresher.idl | 48 | ||||
-rw-r--r-- | src/mongo/s/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/s/commands/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_get_cluster_parameter_cmd.cpp | 132 | ||||
-rw-r--r-- | src/mongo/s/mongos_main.cpp | 5 |
13 files changed, 634 insertions, 194 deletions
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript index ceaa29595f8..073190f124e 100644 --- a/src/mongo/db/commands/SConscript +++ b/src/mongo/db/commands/SConscript @@ -587,6 +587,7 @@ env.Library( '$BUILD_DIR/mongo/db/transaction_api', '$BUILD_DIR/mongo/idl/idl_parser', '$BUILD_DIR/mongo/util/net/ssl_manager', + 'cluster_server_parameter_commands_invocation', 'core', 'create_command', 'kill_common', @@ -597,7 +598,6 @@ env.Library( 'rwc_defaults_commands', 'server_status', 'servers', - 'set_cluster_parameter_invocation', 'set_feature_compatibility_version_idl', 'set_index_commit_quorum_idl', 'set_user_write_block_mode_idl', @@ -611,13 +611,15 @@ env.Library( ) env.Library( - target='set_cluster_parameter_invocation', + target='cluster_server_parameter_commands_invocation', source=[ + 'get_cluster_parameter_invocation.cpp', 'set_cluster_parameter_invocation.cpp', ], LIBDEPS=[ '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/db/audit', + '$BUILD_DIR/mongo/s/grid', '$BUILD_DIR/mongo/s/write_ops/cluster_write_ops', 'cluster_server_parameter_cmds_idl', ], @@ -821,11 +823,11 @@ env.CppUnitTest( "$BUILD_DIR/mongo/db/repl/storage_interface_impl", "$BUILD_DIR/mongo/db/service_context_d_test_fixture", '$BUILD_DIR/mongo/idl/idl_parser', + "cluster_server_parameter_commands_invocation", "core", "create_command", "mongod", "servers", - "set_cluster_parameter_invocation", "standalone", ], ) diff --git a/src/mongo/db/commands/get_cluster_parameter_command.cpp b/src/mongo/db/commands/get_cluster_parameter_command.cpp index d20ba275743..9a3ac246e23 100644 --- a/src/mongo/db/commands/get_cluster_parameter_command.cpp +++ b/src/mongo/db/commands/get_cluster_parameter_command.cpp @@ -34,6 +34,7 @@ #include "mongo/db/auth/authorization_session.h" #include "mongo/db/commands.h" #include "mongo/db/commands/cluster_server_parameter_cmds_gen.h" +#include "mongo/db/commands/get_cluster_parameter_invocation.h" #include "mongo/db/repl/replication_coordinator.h" #include "mongo/idl/cluster_server_parameter_gen.h" #include "mongo/logv2/log.h" @@ -68,11 +69,6 @@ public: using InvocationBase::InvocationBase; Reply typedRun(OperationContext* opCtx) { - uassert( - ErrorCodes::IllegalOperation, - "featureFlagClusterWideConfig not enabled", - gFeatureFlagClusterWideConfig.isEnabled(serverGlobalParams.featureCompatibility)); - // TODO SERVER-65249: This will eventually be made specific to the parameter being set // so that some parameters will be able to use getClusterParameter even on standalones. uassert(ErrorCodes::IllegalOperation, @@ -80,71 +76,8 @@ public: repl::ReplicationCoordinator::get(opCtx)->getReplicationMode() != repl::ReplicationCoordinator::modeNone); - const stdx::variant<std::string, std::vector<std::string>>& cmdBody = - request().getCommandParameter(); - ServerParameterSet* clusterParameters = ServerParameterSet::getClusterParameterSet(); - std::vector<BSONObj> parameterValues; - std::vector<std::string> parameterNames; - - audit::logGetClusterParameter(opCtx->getClient(), cmdBody); - - // For each parameter, generate a BSON representation of it and retrieve its name. - auto makeBSON = [&](ServerParameter* requestedParameter) { - // Skip any disabled cluster parameters. - if (requestedParameter->isEnabled()) { - BSONObjBuilder bob; - requestedParameter->append(opCtx, bob, requestedParameter->name()); - parameterValues.push_back(bob.obj()); - parameterNames.push_back(requestedParameter->name()); - } - }; - - stdx::visit( - visit_helper::Overloaded{ - [&](const std::string& strParameterName) { - if (strParameterName == "*"_sd) { - // Retrieve all cluster parameter values. - Map clusterParameterMap = clusterParameters->getMap(); - parameterValues.reserve(clusterParameterMap.size()); - parameterNames.reserve(clusterParameterMap.size()); - for (const auto& param : clusterParameterMap) { - makeBSON(param.second); - } - } else { - // Any other string must correspond to a single parameter name. Return - // an error if a disabled cluster parameter is explicitly requested. - ServerParameter* sp = clusterParameters->get(strParameterName); - uassert(ErrorCodes::BadValue, - str::stream() << "Server parameter: '" << strParameterName - << "' is currently disabled", - sp->isEnabled()); - makeBSON(sp); - } - }, - [&](const std::vector<std::string>& listParameterNames) { - uassert(ErrorCodes::BadValue, - "Must supply at least one cluster server parameter name to " - "getClusterParameter", - listParameterNames.size() > 0); - parameterValues.reserve(listParameterNames.size()); - parameterNames.reserve(listParameterNames.size()); - for (const auto& requestedParameterName : listParameterNames) { - ServerParameter* sp = clusterParameters->get(requestedParameterName); - uassert(ErrorCodes::BadValue, - str::stream() << "Server parameter: '" << requestedParameterName - << "' is currently disabled'", - sp->isEnabled()); - makeBSON(sp); - } - }}, - cmdBody); - - LOGV2_DEBUG(6226100, - 2, - "Retrieved parameter values for cluster server parameters", - "parameterNames"_attr = parameterNames); - - return Reply(parameterValues); + GetClusterParameterInvocation invocation; + return invocation.getCachedParameters(opCtx, request()); } private: diff --git a/src/mongo/db/commands/get_cluster_parameter_invocation.cpp b/src/mongo/db/commands/get_cluster_parameter_invocation.cpp new file mode 100644 index 00000000000..b95acf4896f --- /dev/null +++ b/src/mongo/db/commands/get_cluster_parameter_invocation.cpp @@ -0,0 +1,195 @@ +/** + * Copyright (C) 2022-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/db/audit.h" +#include "mongo/db/commands/get_cluster_parameter_invocation.h" +#include "mongo/idl/cluster_server_parameter_gen.h" +#include "mongo/logv2/log.h" +#include "mongo/s/grid.h" + +#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kCommand + +namespace mongo { + +std::pair<std::vector<std::string>, std::vector<BSONObj>> +GetClusterParameterInvocation::retrieveRequestedParameters(OperationContext* opCtx, + const CmdBody& cmdBody) { + uassert(ErrorCodes::IllegalOperation, + "featureFlagClusterWideConfig not enabled", + gFeatureFlagClusterWideConfig.isEnabled(serverGlobalParams.featureCompatibility)); + + ServerParameterSet* clusterParameters = ServerParameterSet::getClusterParameterSet(); + std::vector<std::string> parameterNames; + std::vector<BSONObj> parameterValues; + + audit::logGetClusterParameter(opCtx->getClient(), cmdBody); + + // For each parameter, generate a BSON representation of it and retrieve its name. + auto makeBSON = [&](ServerParameter* requestedParameter) { + // Skip any disabled cluster parameters. + if (requestedParameter->isEnabled()) { + BSONObjBuilder bob; + requestedParameter->append(opCtx, bob, requestedParameter->name()); + parameterValues.push_back(bob.obj().getOwned()); + parameterNames.push_back(requestedParameter->name()); + } + }; + + stdx::visit( + visit_helper::Overloaded{ + [&](const std::string& strParameterName) { + if (strParameterName == "*"_sd) { + // Retrieve all cluster parameter values. + Map clusterParameterMap = clusterParameters->getMap(); + parameterValues.reserve(clusterParameterMap.size()); + parameterNames.reserve(clusterParameterMap.size()); + for (const auto& param : clusterParameterMap) { + makeBSON(param.second); + } + } else { + // Any other string must correspond to a single parameter name. Return + // an error if a disabled cluster parameter is explicitly requested. + ServerParameter* sp = clusterParameters->get(strParameterName); + uassert(ErrorCodes::BadValue, + str::stream() << "Server parameter: '" << strParameterName + << "' is currently disabled", + sp->isEnabled()); + makeBSON(sp); + } + }, + [&](const std::vector<std::string>& listParameterNames) { + uassert(ErrorCodes::BadValue, + "Must supply at least one cluster server parameter name to " + "getClusterParameter", + listParameterNames.size() > 0); + parameterValues.reserve(listParameterNames.size()); + parameterNames.reserve(listParameterNames.size()); + for (const auto& requestedParameterName : listParameterNames) { + ServerParameter* sp = clusterParameters->get(requestedParameterName); + uassert(ErrorCodes::BadValue, + str::stream() << "Server parameter: '" << requestedParameterName + << "' is currently disabled'", + sp->isEnabled()); + makeBSON(sp); + } + }}, + cmdBody); + + return {std::move(parameterNames), std::move(parameterValues)}; +} + +GetClusterParameterInvocation::Reply GetClusterParameterInvocation::getCachedParameters( + OperationContext* opCtx, const GetClusterParameter& request) { + const CmdBody& cmdBody = request.getCommandParameter(); + + auto [parameterNames, parameterValues] = retrieveRequestedParameters(opCtx, cmdBody); + + LOGV2_DEBUG(6226100, + 2, + "Retrieved parameter values for cluster server parameters", + "parameterNames"_attr = parameterNames); + + return Reply(parameterValues); +} + +GetClusterParameterInvocation::Reply GetClusterParameterInvocation::getDurableParameters( + OperationContext* opCtx, const GetClusterParameter& request) { + auto configServers = Grid::get(opCtx)->shardRegistry()->getConfigShard(); + + // Create the query document such that all documents in config.clusterParmeters with _id + // in the requested list of ServerParameters are returned. + const CmdBody& cmdBody = request.getCommandParameter(); + ServerParameterSet* clusterParameters = ServerParameterSet::getClusterParameterSet(); + + BSONObjBuilder queryDocBuilder; + BSONObjBuilder inObjBuilder = queryDocBuilder.subobjStart("_id"_sd); + BSONArrayBuilder parameterNameBuilder = inObjBuilder.subarrayStart("$in"_sd); + + auto [requestedParameterNames, parameterValues] = retrieveRequestedParameters(opCtx, cmdBody); + + for (const auto& parameterValue : parameterValues) { + parameterNameBuilder.append(parameterValue["_id"_sd].String()); + } + + parameterNameBuilder.doneFast(); + inObjBuilder.doneFast(); + + // Perform the majority read on the config server primary. + BSONObj query = queryDocBuilder.obj(); + LOGV2_DEBUG(6226101, 2, "Querying config servers for cluster parameters", "query"_attr = query); + auto findResponse = uassertStatusOK( + configServers->exhaustiveFindOnConfig(opCtx, + ReadPreferenceSetting{ReadPreference::PrimaryOnly}, + repl::ReadConcernLevel::kMajorityReadConcern, + NamespaceString::kClusterParametersNamespace, + query, + BSONObj(), + boost::none)); + + // Any parameters that are not included in the response don't have a cluster parameter + // document yet, which means they still are using the default value. + std::vector<BSONObj> retrievedParameters = std::move(findResponse.docs); + if (retrievedParameters.size() < requestedParameterNames.size()) { + std::vector<std::string> onDiskParameterNames; + onDiskParameterNames.reserve(retrievedParameters.size()); + std::transform( + retrievedParameters.begin(), + retrievedParameters.end(), + std::back_inserter(onDiskParameterNames), + [&](const auto& onDiskParameter) { return onDiskParameter["_id"_sd].String(); }); + + // Sort and find the set difference of the requested parameters and the parameters + // returned. + std::vector<std::string> defaultParameterNames; + + defaultParameterNames.reserve(requestedParameterNames.size() - onDiskParameterNames.size()); + + std::sort(onDiskParameterNames.begin(), onDiskParameterNames.end()); + std::sort(requestedParameterNames.begin(), requestedParameterNames.end()); + std::set_difference(requestedParameterNames.begin(), + requestedParameterNames.end(), + onDiskParameterNames.begin(), + onDiskParameterNames.end(), + std::back_inserter(defaultParameterNames)); + + for (const auto& defaultParameterName : defaultParameterNames) { + auto defaultParameter = clusterParameters->get(defaultParameterName); + BSONObjBuilder bob; + defaultParameter->append(opCtx, bob, defaultParameterName); + retrievedParameters.push_back(bob.obj()); + } + } + + return Reply(retrievedParameters); +} + +} // namespace mongo diff --git a/src/mongo/db/commands/get_cluster_parameter_invocation.h b/src/mongo/db/commands/get_cluster_parameter_invocation.h new file mode 100644 index 00000000000..66b63737c27 --- /dev/null +++ b/src/mongo/db/commands/get_cluster_parameter_invocation.h @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2022-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 "mongo/db/commands/cluster_server_parameter_cmds_gen.h" +#include "mongo/idl/server_parameter.h" + +namespace mongo { + +class GetClusterParameterInvocation { +public: + using Request = GetClusterParameter; + using Reply = GetClusterParameter::Reply; + using Map = ServerParameterSet::Map; + using CmdBody = stdx::variant<std::string, std::vector<std::string>>; + + GetClusterParameterInvocation() = default; + + // Retrieves in-memory parameters. Used by mongod getClusterParameter and mongoses + // with featureFlagClusterWideConfigM2 enabled. + Reply getCachedParameters(OperationContext* opCtx, const GetClusterParameter& request); + + // Retrieves durable cluster server parameters from config server. Used by mongoses with + // featureFlagClusterWideConfigM2 disabled. + Reply getDurableParameters(OperationContext* opCtx, const GetClusterParameter& request); + +private: + // Parses the command body and retrieves the BSON representation and names of the requested + // cluster parameters. + std::pair<std::vector<std::string>, std::vector<BSONObj>> retrieveRequestedParameters( + OperationContext* opCtx, const CmdBody& cmdBody); +}; + +} // namespace mongo diff --git a/src/mongo/db/s/SConscript b/src/mongo/db/s/SConscript index e0cf8313b31..6fbd14c7300 100644 --- a/src/mongo/db/s/SConscript +++ b/src/mongo/db/s/SConscript @@ -285,8 +285,8 @@ env.Library( '$BUILD_DIR/mongo/db/audit', '$BUILD_DIR/mongo/db/catalog/collection_options', '$BUILD_DIR/mongo/db/catalog_raii', + '$BUILD_DIR/mongo/db/commands/cluster_server_parameter_commands_invocation', '$BUILD_DIR/mongo/db/commands/mongod_fcv', - '$BUILD_DIR/mongo/db/commands/set_cluster_parameter_invocation', '$BUILD_DIR/mongo/db/commands/set_feature_compatibility_version_idl', '$BUILD_DIR/mongo/db/common', '$BUILD_DIR/mongo/db/concurrency/exception_util', @@ -447,12 +447,12 @@ env.Library( '$BUILD_DIR/mongo/db/bson/dotted_path_support', '$BUILD_DIR/mongo/db/catalog/catalog_helpers', '$BUILD_DIR/mongo/db/cloner', + '$BUILD_DIR/mongo/db/commands/cluster_server_parameter_commands_invocation', '$BUILD_DIR/mongo/db/commands/core', '$BUILD_DIR/mongo/db/commands/create_command', '$BUILD_DIR/mongo/db/commands/mongod_fcv', '$BUILD_DIR/mongo/db/commands/rename_collection_idl', '$BUILD_DIR/mongo/db/commands/server_status', - '$BUILD_DIR/mongo/db/commands/set_cluster_parameter_invocation', '$BUILD_DIR/mongo/db/commands/test_commands_enabled', '$BUILD_DIR/mongo/db/commands/txn_cmd_request', '$BUILD_DIR/mongo/db/fle_crud', diff --git a/src/mongo/idl/SConscript b/src/mongo/idl/SConscript index 6ac300aaae0..df82f0216e1 100644 --- a/src/mongo/idl/SConscript +++ b/src/mongo/idl/SConscript @@ -92,6 +92,22 @@ env.Library( ) env.Library( + target='cluster_server_parameter_refresher', + source=[ + 'cluster_server_parameter_refresher.cpp', + 'cluster_server_parameter_refresher.idl', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/audit', + ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/s/grid', + 'cluster_server_parameter', + ], +) + +env.Library( target='cluster_server_parameter_test_parameter', source=[ 'cluster_server_parameter_test.idl', diff --git a/src/mongo/idl/cluster_server_parameter_refresher.cpp b/src/mongo/idl/cluster_server_parameter_refresher.cpp new file mode 100644 index 00000000000..42f49040305 --- /dev/null +++ b/src/mongo/idl/cluster_server_parameter_refresher.cpp @@ -0,0 +1,207 @@ +/** + * Copyright (C) 2022-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/idl/cluster_server_parameter_refresher.h" + +#include "mongo/db/audit.h" +#include "mongo/idl/cluster_server_parameter_refresher_gen.h" +#include "mongo/logv2/log.h" +#include "mongo/s/grid.h" + +#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kControl + +namespace mongo { +namespace { + +const auto getClusterServerParameterRefresher = + ServiceContext::declareDecoration<std::unique_ptr<ClusterServerParameterRefresher>>(); + +Seconds loadInterval() { + return Seconds(clusterServerParameterRefreshIntervalSecs.load()); +} + +StatusWith<std::vector<BSONObj>> getClusterParametersFromConfigServer( + OperationContext* opCtx, const LogicalTime& latestTime) { + BSONObjBuilder queryObjBuilder; + BSONObjBuilder clusterParameterTimeObjBuilder = + queryObjBuilder.subobjStart("clusterParameterTime"_sd); + clusterParameterTimeObjBuilder.appendTimestamp("$gt"_sd, latestTime.asTimestamp().asInt64()); + clusterParameterTimeObjBuilder.doneFast(); + + BSONObj query = queryObjBuilder.obj(); + + // Attempt to retrieve cluster parameter documents from the config server. + // exhaustiveFindOnConfig makes up to 3 total attempts if it receives a retriable error before + // giving up. + LOGV2_DEBUG(6226404, 3, "Retrieving cluster server parameters from config server"); + auto configServers = Grid::get(opCtx)->shardRegistry()->getConfigShard(); + auto swFindResponse = + configServers->exhaustiveFindOnConfig(opCtx, + ReadPreferenceSetting{ReadPreference::PrimaryOnly}, + repl::ReadConcernLevel::kMajorityReadConcern, + NamespaceString::kClusterParametersNamespace, + query, + BSONObj(), + boost::none); + + // If the error is not retriable or persists beyond the max number of retry attempts, give up + // and throw an error. + if (!swFindResponse.isOK()) { + return swFindResponse.getStatus(); + } + + return swFindResponse.getValue().docs; +} + +} // namespace + +Status clusterServerParameterRefreshIntervalSecsNotify(const int& newValue) { + LOGV2_DEBUG(6226400, + 5, + "Set clusterServerParameterRefresher interval seconds", + "clusterServerParameterRefreshIntervalSecs"_attr = loadInterval()); + if (hasGlobalServiceContext()) { + auto service = getGlobalServiceContext(); + if (getClusterServerParameterRefresher(service)) { + getClusterServerParameterRefresher(service)->setPeriod(loadInterval()); + } + } + + return Status::OK(); +} + +ClusterServerParameterRefresher* ClusterServerParameterRefresher::get(OperationContext* opCtx) { + return get(opCtx->getServiceContext()); +} + +ClusterServerParameterRefresher* ClusterServerParameterRefresher::get(ServiceContext* serviceCtx) { + return getClusterServerParameterRefresher(serviceCtx).get(); +} + +void ClusterServerParameterRefresher::setPeriod(Milliseconds period) { + _job->setPeriod(period); +} + +Status ClusterServerParameterRefresher::refreshParameters(OperationContext* opCtx) { + // Query the config servers for all cluster parameter documents with + // clusterParameterTime greater than the largest in-memory timestamp. + auto swClusterParameterDocs = + getClusterParametersFromConfigServer(opCtx, _latestClusterParameterTime); + if (!swClusterParameterDocs.isOK()) { + LOGV2_WARNING(6226401, + "Could not refresh cluster server parameters from config servers. Will retry " + "after refresh interval elapses", + "clusterServerParameterRefreshIntervalSecs"_attr = loadInterval(), + "reason"_attr = swClusterParameterDocs.getStatus().reason()); + return swClusterParameterDocs.getStatus(); + } + + // Set each in-memory cluster parameter that was returned in the response. Then, advance the + // latest clusterParameterTime to the latest one returned if all of the cluster parameters are + // successfully set in-memory. + Timestamp latestTime; + bool isSuccessful = true; + Status setStatus = Status::OK(); + ServerParameterSet* clusterParameterCache = ServerParameterSet::getClusterParameterSet(); + std::vector<BSONObj> clusterParameterDocs = swClusterParameterDocs.getValue(); + std::vector<BSONObj> updatedParameters; + updatedParameters.reserve(clusterParameterDocs.size()); + + for (const auto& clusterParameterDoc : clusterParameterDocs) { + Timestamp clusterParameterTime = clusterParameterDoc["clusterParameterTime"_sd].timestamp(); + latestTime = (clusterParameterTime > latestTime) ? clusterParameterTime : latestTime; + + auto clusterParameterName = clusterParameterDoc["_id"_sd].String(); + ServerParameter* sp = clusterParameterCache->get(clusterParameterName); + + BSONObjBuilder oldClusterParameterBob; + sp->append(opCtx, oldClusterParameterBob, clusterParameterName); + + setStatus = sp->set(clusterParameterDoc); + if (!setStatus.isOK()) { + LOGV2_WARNING(6226402, + "Could not set in-memory cluster server parameter", + "parameter"_attr = clusterParameterName, + "reason"_attr = setStatus.reason()); + isSuccessful = false; + } + + BSONObjBuilder updatedClusterParameterBob; + sp->append(opCtx, updatedClusterParameterBob, clusterParameterName); + BSONObj updatedClusterParameterBSON = updatedClusterParameterBob.obj().getOwned(); + + audit::logUpdateCachedClusterParameter(opCtx->getClient(), + oldClusterParameterBob.obj().getOwned(), + updatedClusterParameterBSON); + + updatedParameters.emplace_back( + updatedClusterParameterBSON.removeField("clusterParameterTime"_sd)); + } + + if (isSuccessful) { + _latestClusterParameterTime = LogicalTime(latestTime); + LOGV2_DEBUG(6226403, + 3, + "Updated cluster server parameters", + "clusterParameterDocuments"_attr = updatedParameters); + } + + return setStatus; +} + +void ClusterServerParameterRefresher::start(ServiceContext* serviceCtx, OperationContext* opCtx) { + auto refresher = std::make_unique<ClusterServerParameterRefresher>(); + + auto periodicRunner = serviceCtx->getPeriodicRunner(); + invariant(periodicRunner); + + PeriodicRunner::PeriodicJob job( + "ClusterServerParameterRefresher", + [serviceCtx](Client* client) { getClusterServerParameterRefresher(serviceCtx)->run(); }, + loadInterval()); + + refresher->_job = std::make_unique<PeriodicJobAnchor>(periodicRunner->makeJob(std::move(job))); + + // Make sure the invalidator is moved to the service context by the time we call start() + getClusterServerParameterRefresher(serviceCtx) = std::move(refresher); + getClusterServerParameterRefresher(serviceCtx)->_job->start(); +} + +void ClusterServerParameterRefresher::run() { + auto opCtx = cc().makeOperationContext(); + auto status = refreshParameters(opCtx.get()); + if (!status.isOK()) { + LOGV2_DEBUG( + 6226405, 1, "Cluster server parameter refresh failed", "reason"_attr = status.reason()); + } +} + +} // namespace mongo diff --git a/src/mongo/idl/cluster_server_parameter_refresher.h b/src/mongo/idl/cluster_server_parameter_refresher.h new file mode 100644 index 00000000000..b1b3397e501 --- /dev/null +++ b/src/mongo/idl/cluster_server_parameter_refresher.h @@ -0,0 +1,76 @@ +/** + * Copyright (C) 2022-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 "mongo/db/operation_context.h" +#include "mongo/db/service_context.h" + +namespace mongo { + +/** + * Runs a background job on mongos only that periodically refreshes its in-memory cache of cluster + * server parameters with updated values from the config servers. + */ +class ClusterServerParameterRefresher { +public: + ClusterServerParameterRefresher() = default; + + static ClusterServerParameterRefresher* get(OperationContext* opCtx); + static ClusterServerParameterRefresher* get(ServiceContext* serviceCtx); + + /** + * Create a new ClusterServerParameterRefresher as a decorator on the service context + * and start the background job. + */ + static void start(ServiceContext* serviceCtx, OperationContext* opCtx); + + /** + * Refreshes all cluster server parameters that have been updated on the config servers since + * _latestClusterParameterTime. Called periodically in the run method, which executes in a + * background thread. Also called in-line during getClusterParameter on mongos to ensure that + * cached values returned are up-to-date. + */ + Status refreshParameters(OperationContext* opCtx); + + /** + * Set the period of the background job. This should only be used internally (by the + * setParameter). + */ + void setPeriod(Milliseconds period); + +private: + void run(); + + std::unique_ptr<PeriodicJobAnchor> _job; + LogicalTime _latestClusterParameterTime = LogicalTime::kUninitialized; +}; + +Status clusterServerParameterRefreshIntervalSecsNotify(const int& newValue); + +} // namespace mongo diff --git a/src/mongo/idl/cluster_server_parameter_refresher.idl b/src/mongo/idl/cluster_server_parameter_refresher.idl new file mode 100644 index 00000000000..6609b83f544 --- /dev/null +++ b/src/mongo/idl/cluster_server_parameter_refresher.idl @@ -0,0 +1,48 @@ +# Copyright (C) 2022-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. + +global: + cpp_namespace: "mongo" + cpp_includes: + - "mongo/idl/cluster_server_parameter_refresher.h" + +imports: + - "mongo/idl/basic_types.idl" + +server_parameters: + clusterServerParameterRefreshIntervalSecs: + description: > + On a mongos instance, specifies the interval (in seconds) at which the mongos instance + refreshes its in-memory cache of cluster server parameters. + cpp_varname: clusterServerParameterRefreshIntervalSecs + cpp_vartype: AtomicWord<int> + set_at: [startup, runtime] + default: 30 + on_update: clusterServerParameterRefreshIntervalSecsNotify + validator: + gte: 1 + lte: 86400 diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript index ac21f54fe9c..2ad44fbf093 100644 --- a/src/mongo/s/SConscript +++ b/src/mongo/s/SConscript @@ -539,6 +539,7 @@ env.Library( '$BUILD_DIR/mongo/db/service_liaison_mongos', '$BUILD_DIR/mongo/db/session_catalog', '$BUILD_DIR/mongo/db/startup_warnings_common', + '$BUILD_DIR/mongo/idl/cluster_server_parameter_refresher', '$BUILD_DIR/mongo/transport/service_entry_point', '$BUILD_DIR/mongo/transport/transport_layer_manager', '$BUILD_DIR/mongo/util/latch_analyzer' diff --git a/src/mongo/s/commands/SConscript b/src/mongo/s/commands/SConscript index 543fa0a59b8..d83d9c5c638 100644 --- a/src/mongo/s/commands/SConscript +++ b/src/mongo/s/commands/SConscript @@ -104,6 +104,7 @@ env.Library( '$BUILD_DIR/mongo/db/auth/auth_checks', '$BUILD_DIR/mongo/db/change_stream_options_manager', '$BUILD_DIR/mongo/db/commands/cluster_server_parameter_cmds_idl', + '$BUILD_DIR/mongo/db/commands/cluster_server_parameter_commands_invocation', '$BUILD_DIR/mongo/db/commands/core', '$BUILD_DIR/mongo/db/commands/create_command', '$BUILD_DIR/mongo/db/commands/current_op_common', @@ -143,6 +144,7 @@ env.Library( '$BUILD_DIR/mongo/db/views/views', '$BUILD_DIR/mongo/executor/async_multicaster', '$BUILD_DIR/mongo/executor/async_request_executor', + '$BUILD_DIR/mongo/idl/cluster_server_parameter_refresher', '$BUILD_DIR/mongo/idl/server_parameter', '$BUILD_DIR/mongo/rpc/rewrite_state_change_errors', '$BUILD_DIR/mongo/s/load_balancer_support', diff --git a/src/mongo/s/commands/cluster_get_cluster_parameter_cmd.cpp b/src/mongo/s/commands/cluster_get_cluster_parameter_cmd.cpp index a86e662c947..3e741c4071b 100644 --- a/src/mongo/s/commands/cluster_get_cluster_parameter_cmd.cpp +++ b/src/mongo/s/commands/cluster_get_cluster_parameter_cmd.cpp @@ -34,7 +34,9 @@ #include "mongo/db/auth/authorization_session.h" #include "mongo/db/commands.h" #include "mongo/db/commands/cluster_server_parameter_cmds_gen.h" +#include "mongo/db/commands/get_cluster_parameter_invocation.h" #include "mongo/idl/cluster_server_parameter_gen.h" +#include "mongo/idl/cluster_server_parameter_refresher.h" #include "mongo/logv2/log.h" #include "mongo/s/cluster_commands_helpers.h" #include "mongo/s/grid.h" @@ -61,7 +63,7 @@ public: } std::string help() const override { - return "Get majority-written cluster parameter value(s) from the config servers"; + return "Refresh and get cached cluster server parameter value from mongos."; } class Invocation final : public InvocationBase { @@ -69,126 +71,18 @@ public: using InvocationBase::InvocationBase; Reply typedRun(OperationContext* opCtx) { - uassert( - ErrorCodes::IllegalOperation, - "featureFlagClusterWideConfig not enabled", - gFeatureFlagClusterWideConfig.isEnabled(serverGlobalParams.featureCompatibility)); - - // For now, the mongos implementation retrieves the names of the requested cluster - // server parameters and queries them from the config.clusterParameters namespace on - // the config servers. This may change after SERVER-62264, when cluster server - // parameters will be cached on mongoses as well. - auto configServers = Grid::get(opCtx)->shardRegistry()->getConfigShard(); - - // Create the query document such that all documents in config.clusterParmeters with _id - // in the requested list of ServerParameters are returned. - const stdx::variant<std::string, std::vector<std::string>>& cmdBody = - request().getCommandParameter(); - ServerParameterSet* clusterParameters = ServerParameterSet::getClusterParameterSet(); - - audit::logGetClusterParameter(opCtx->getClient(), cmdBody); - - std::vector<std::string> requestedParameterNames; - BSONObjBuilder queryDocBuilder; - BSONObjBuilder inObjBuilder = queryDocBuilder.subobjStart("_id"_sd); - BSONArrayBuilder parameterNameBuilder = inObjBuilder.subarrayStart("$in"_sd); - - stdx::visit( - visit_helper::Overloaded{ - [&](const std::string& strParameterName) { - if (strParameterName == "*"_sd) { - // Append all cluster parameter names. - Map clusterParameterMap = clusterParameters->getMap(); - requestedParameterNames.reserve(clusterParameterMap.size()); - for (const auto& param : clusterParameterMap) { - // Skip any disabled test parameters. - if (param.second->isEnabled()) { - parameterNameBuilder.append(param.first); - requestedParameterNames.push_back(param.first); - } - } - } else { - // Return an error if a disabled cluster parameter is explicitly - // requested. - uassert(ErrorCodes::BadValue, - str::stream() << "Server parameter: '" << strParameterName - << "' is currently disabled'", - clusterParameters->get(strParameterName)->isEnabled()); - parameterNameBuilder.append(strParameterName); - requestedParameterNames.push_back(strParameterName); - } - }, - [&](const std::vector<std::string>& listParameterNames) { - uassert(ErrorCodes::BadValue, - "Must supply at least one cluster server parameter name to " - "getClusterParameter", - listParameterNames.size() > 0); - requestedParameterNames.reserve(listParameterNames.size()); - for (const auto& requestedParameterName : listParameterNames) { - // Return an error if a disabled cluster parameter is explicitly - // requested. - uassert(ErrorCodes::BadValue, - str::stream() << "Server parameter: '" << requestedParameterName - << "' is currently disabled'", - clusterParameters->get(requestedParameterName)->isEnabled()); - parameterNameBuilder.append(requestedParameterName); - requestedParameterNames.push_back(requestedParameterName); - } - }}, - cmdBody); - - parameterNameBuilder.doneFast(); - inObjBuilder.doneFast(); - - // Perform the majority read on the config server primary. - BSONObj query = queryDocBuilder.obj(); - LOGV2(6226101, "Querying config servers for cluster parameters", "query"_attr = query); - auto findResponse = uassertStatusOK(configServers->exhaustiveFindOnConfig( - opCtx, - ReadPreferenceSetting{ReadPreference::PrimaryOnly}, - repl::ReadConcernLevel::kMajorityReadConcern, - NamespaceString::kClusterParametersNamespace, - query, - BSONObj(), - boost::none)); - - // Any parameters that are not included in the response don't have a cluster parameter - // document yet, which means they still are using the default value. - std::vector<BSONObj> retrievedParameters = std::move(findResponse.docs); - if (retrievedParameters.size() < requestedParameterNames.size()) { - std::vector<std::string> onDiskParameterNames; - onDiskParameterNames.reserve(retrievedParameters.size()); - std::transform(retrievedParameters.begin(), - retrievedParameters.end(), - std::back_inserter(onDiskParameterNames), - [&](const auto& onDiskParameter) { - return onDiskParameter["_id"_sd].String(); - }); - - // Sort and find the set difference of the requested parameters and the parameters - // returned. - std::vector<std::string> defaultParameterNames; - - defaultParameterNames.reserve(requestedParameterNames.size() - - onDiskParameterNames.size()); - - std::sort(onDiskParameterNames.begin(), onDiskParameterNames.end()); - std::sort(requestedParameterNames.begin(), requestedParameterNames.end()); - std::set_difference(requestedParameterNames.begin(), - requestedParameterNames.end(), - onDiskParameterNames.begin(), - onDiskParameterNames.end(), - std::back_inserter(defaultParameterNames)); - - for (const auto& defaultParameterName : defaultParameterNames) { - auto defaultParameter = clusterParameters->get(defaultParameterName); - BSONObjBuilder bob; - defaultParameter->append(opCtx, bob, defaultParameterName); - retrievedParameters.push_back(bob.obj()); - } + GetClusterParameterInvocation invocation; + if (gFeatureFlagClusterWideConfigM2.isEnabled( + serverGlobalParams.featureCompatibility)) { + // Refresh cached cluster server parameters via a majority read from the config + // servers. + uassertStatusOK( + ClusterServerParameterRefresher::get(opCtx)->refreshParameters(opCtx)); + + return invocation.getCachedParameters(opCtx, request()); } - return Reply(retrievedParameters); + return invocation.getDurableParameters(opCtx, request()); } private: diff --git a/src/mongo/s/mongos_main.cpp b/src/mongo/s/mongos_main.cpp index 8927a858ee0..0a1ffa80f9a 100644 --- a/src/mongo/s/mongos_main.cpp +++ b/src/mongo/s/mongos_main.cpp @@ -68,6 +68,7 @@ #include "mongo/db/vector_clock_metadata_hook.h" #include "mongo/db/wire_version.h" #include "mongo/executor/task_executor_pool.h" +#include "mongo/idl/cluster_server_parameter_refresher.h" #include "mongo/logv2/log.h" #include "mongo/platform/process_id.h" #include "mongo/rpc/metadata/egress_metadata_hook_list.h" @@ -764,6 +765,10 @@ ExitCode runMongosServer(ServiceContext* serviceContext) { clusterCursorCleanupJob.go(); UserCacheInvalidator::start(serviceContext, opCtx); + if (gFeatureFlagClusterWideConfigM2.isEnabled(serverGlobalParams.featureCompatibility)) { + ClusterServerParameterRefresher::start(serviceContext, opCtx); + } + if (audit::initializeSynchronizeJob) { audit::initializeSynchronizeJob(serviceContext); } |