// server_parameters.h /** * 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 * . * * 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 #include #include #include "mongo/base/static_assert.h" #include "mongo/base/status.h" #include "mongo/db/jsobj.h" #include "mongo/platform/atomic_proxy.h" #include "mongo/platform/atomic_word.h" #include "mongo/platform/compiler.h" #include "mongo/stdx/functional.h" #include "mongo/stdx/mutex.h" #include "mongo/util/stringutils.h" namespace mongo { class ServerParameterSet; class OperationContext; /** * Lets you make server level settings easily configurable. * Hooks into (set|get)Parameter, as well as command line processing * * NOTE: ServerParameters set at runtime can be read or written to at anytime, and are not * thread-safe without atomic types or other concurrency techniques. */ class ServerParameter { public: typedef std::map Map; ServerParameter(ServerParameterSet* sps, StringData name, bool allowedToChangeAtStartup, bool allowedToChangeAtRuntime); ServerParameter(ServerParameterSet* sps, StringData name); virtual ~ServerParameter(); std::string name() const { return _name; } /** * @return if you can set on command line or config file */ bool allowedToChangeAtStartup() const { return _allowedToChangeAtStartup; } /** * @param if you can use (get|set)Parameter */ bool allowedToChangeAtRuntime() const { return _allowedToChangeAtRuntime; } virtual void append(OperationContext* opCtx, BSONObjBuilder& b, const std::string& name) = 0; virtual Status set(const BSONElement& newValueElement) = 0; virtual Status setFromString(const std::string& str) = 0; private: std::string _name; bool _allowedToChangeAtStartup; bool _allowedToChangeAtRuntime; }; class ServerParameterSet { public: typedef std::map Map; void add(ServerParameter* sp); const Map& getMap() const { return _map; } static ServerParameterSet* getGlobal(); private: Map _map; }; /** * Server Parameters can be set startup up and/or runtime. * * At startup, --setParameter ... or config file is used. * At runtime, { setParameter : 1, ...} is used. */ enum class ServerParameterType { /** * Parameter can only be set via runCommand. */ kRuntimeOnly, /** * Parameter can only be set via --setParameter, and is only read at startup after command-line * parameters, and the config file are processed. */ kStartupOnly, /** * Parameter can be set at both startup and runtime. */ kStartupAndRuntime, }; /** * Lets you make server level settings easily configurable. * Hooks into (set|get)Parameter, as well as command line processing */ template class BoundServerParameter : public ServerParameter { private: using setter = stdx::function; using getter = stdx::function; using SPT = ServerParameterType; public: BoundServerParameter(const std::string& name, const setter set, const getter get, SPT paramType = SPT::kStartupOnly) : BoundServerParameter(ServerParameterSet::getGlobal(), name, set, get, paramType) {} BoundServerParameter(ServerParameterSet* sps, const std::string& name, const setter set, const getter get, SPT paramType = SPT::kStartupOnly) : ServerParameter(sps, name, paramType == SPT::kStartupOnly || paramType == SPT::kStartupAndRuntime, paramType == SPT::kRuntimeOnly || paramType == SPT::kStartupAndRuntime), _setter(set), _getter(get) {} ~BoundServerParameter() override = default; void append(OperationContext* opCtx, BSONObjBuilder& b, const std::string& name) override { b.append(name, _getter()); } Status set(const BSONElement& newValueElement) override { T newValue; if (!newValueElement.coerce(&newValue)) { return Status(ErrorCodes::BadValue, "Can't coerce value"); } return _setter(newValue); } Status setFromString(const std::string& str) override; private: const setter _setter; const getter _getter; }; template <> inline Status BoundServerParameter::setFromString(const std::string& str) { if ((str == "1") || (str == "true")) { return _setter(true); } if ((str == "0") || (str == "false")) { return _setter(false); } return Status(ErrorCodes::BadValue, "Value is not a valid boolean"); } template <> inline Status BoundServerParameter::setFromString(const std::string& str) { return _setter(str); } template <> inline Status BoundServerParameter>::setFromString( const std::string& str) { std::vector v; splitStringDelim(str, &v, ','); return _setter(v); } template inline Status BoundServerParameter::setFromString(const std::string& str) { T value; Status status = parseNumberFromString(str, &value); if (!status.isOK()) { return status; } return _setter(value); } template class LockedServerParameter : public BoundServerParameter { private: using SPT = ServerParameterType; public: LockedServerParameter(const std::string& name, const T& initval, SPT paramType = SPT::kStartupAndRuntime) : LockedServerParameter(ServerParameterSet::getGlobal(), name, initval, paramType) {} LockedServerParameter(ServerParameterSet* sps, const std::string& name, const T& initval, SPT paramType = SPT::kStartupAndRuntime) : BoundServerParameter(sps, name, [this](const T& v) { return setLocked(v); }, [this]() { return getLocked(); }, paramType), _value(initval) {} ~LockedServerParameter() override = default; Status setLocked(const T& value) { stdx::unique_lock lk(_mutex); _value = value; return Status::OK(); } T getLocked() const { stdx::unique_lock lk(_mutex); return _value; } private: mutable stdx::mutex _mutex; T _value; }; namespace server_parameter_detail { template struct IsOneOf : std::false_type {}; template struct IsOneOf : std::conditional_t::value, std::true_type, IsOneOf> {}; /** * Type trait for ServerParameterType to identify which types are safe to use at runtime because * they have std::atomic or equivalent types. */ template struct IsSafeRuntimeType : IsOneOf {}; /** * Get the type of storage to use for a given tuple of . * * By default, we want std::atomic or equivalent types because they are thread-safe. * If the parameter is a startup only type, then there are no concurrency concerns since * server parameters are processed on the main thread while it is single-threaded during startup. */ template struct StorageTraits { /** * For kStartupOnly parameters, we can use the type T as storage directly. * Otherwise if T is double, use AtomicDouble. Otherwise use AtomicWord. */ using value_type = std::conditional_t< paramType == ServerParameterType::kStartupOnly, T, std::conditional_t::value, AtomicDouble, AtomicWord>>; static T get(value_type* v) { return _get(v); } static void set(value_type* v, const T& newValue) { _set(v, newValue); } private: static T _get(AtomicDouble* v) { return v->load(); } template static T _get(AtomicWord* v) { return v->load(); } template static T _get(U* v) { return *v; } static void _set(AtomicDouble* v, const T& newValue) { v->store(newValue); } template static void _set(AtomicWord* v, const T& newValue) { v->store(newValue); } template static void _set(U* v, const T& newValue) { *v = newValue; } }; } // namespace server_parameter_detail /** * Implementation of BoundServerParameter for reading and writing a server parameter with a given * name and type into a specific C++ variable. * * NOTE: ServerParameters set at runtime can be read or written to at anytime, and are not * thread-safe without atomic types or other concurrency techniques. */ template class ExportedServerParameter : public BoundServerParameter { public: MONGO_STATIC_ASSERT_MSG(paramType == ServerParameterType::kStartupOnly || server_parameter_detail::IsSafeRuntimeType::value, "This type is not supported as a runtime server parameter."); using storage_traits = server_parameter_detail::StorageTraits; using storage_type = typename storage_traits::value_type; using validator_function = stdx::function; /** * Construct an ExportedServerParameter in parameter set "sps", named "name", whose storage * is at "value". * * If allowedToChangeAtStartup is true, the parameter may be set at the command line, * e.g. via the --setParameter switch. If allowedToChangeAtRuntime is true, the parameter * may be set at runtime, e.g. via the setParameter command. */ ExportedServerParameter(ServerParameterSet* sps, const std::string& name, storage_type* value) : BoundServerParameter(sps, name, [this](const T& v) { return set(v); }, [this] { return storage_traits::get(_value); }, paramType), _value(value) {} // Don't let the template method hide our inherited method using BoundServerParameter::set; virtual Status set(const T& newValue) { auto const status = validate(newValue); if (!status.isOK()) { return status; } storage_traits::set(_value, newValue); return Status::OK(); } ExportedServerParameter* withValidator(validator_function validator) { invariant(!_validator); _validator = std::move(validator); return this; } protected: /** * Note that if a subclass overrides the validate member function, the validator provided via * withValidate will not be used. **/ virtual Status validate(const T& potentialNewValue) { if (_validator) { return _validator(potentialNewValue); } return Status::OK(); } storage_type* const _value; // owned elsewhere validator_function _validator; }; } // namespace mongo #define MONGO_EXPORT_SERVER_PARAMETER_IMPL_(NAME, TYPE, INITIAL_VALUE, PARAM_TYPE) \ ExportedServerParameter::storage_type NAME(INITIAL_VALUE); \ MONGO_COMPILER_VARIABLE_UNUSED auto _exportedParameter_##NAME = \ (new ExportedServerParameter( \ ServerParameterSet::getGlobal(), #NAME, &NAME)) /** * Create a global variable of type "TYPE" named "NAME" with the given INITIAL_VALUE. The * value may be set at startup or at runtime. */ #define MONGO_EXPORT_SERVER_PARAMETER(NAME, TYPE, INITIAL_VALUE) \ MONGO_EXPORT_SERVER_PARAMETER_IMPL_( \ NAME, TYPE, INITIAL_VALUE, ServerParameterType::kStartupAndRuntime) /** * Like MONGO_EXPORT_SERVER_PARAMETER, but the value may only be set at startup. */ #define MONGO_EXPORT_STARTUP_SERVER_PARAMETER(NAME, TYPE, INITIAL_VALUE) \ MONGO_EXPORT_SERVER_PARAMETER_IMPL_( \ NAME, TYPE, INITIAL_VALUE, ServerParameterType::kStartupOnly) /** * Like MONGO_EXPORT_SERVER_PARAMETER, but the value may only be set at runtime. */ #define MONGO_EXPORT_RUNTIME_SERVER_PARAMETER(NAME, TYPE, INITIAL_VALUE) \ MONGO_EXPORT_SERVER_PARAMETER_IMPL_( \ NAME, TYPE, INITIAL_VALUE, ServerParameterType::kRuntimeOnly)