diff options
author | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2016-04-15 18:41:32 -0400 |
---|---|---|
committer | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2016-04-20 16:31:12 -0400 |
commit | 0e45dbdbfda0ff381308b37d75235cad1da3db54 (patch) | |
tree | 5917a27620cc0c034a5e20d609917ce7660ebe21 | |
parent | f3c0c1cc6957efb0c48b1b757c3de2f20749db02 (diff) | |
download | mongo-0e45dbdbfda0ff381308b37d75235cad1da3db54.tar.gz |
SERVER-23696 Get rid of SettingsType
Splits all the functionality of SettingsType into the constituent
ChunkSizeSettings and BalancerSettings and moves it under the
BalancerConfiguration utility.
-rw-r--r-- | src/mongo/db/s/sharding_state_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/s/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/s/balancer/balancer_configuration.cpp | 280 | ||||
-rw-r--r-- | src/mongo/s/balancer/balancer_configuration.h | 147 | ||||
-rw-r--r-- | src/mongo/s/balancer/balancer_configuration_test.cpp | 263 | ||||
-rw-r--r-- | src/mongo/s/catalog/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/s/catalog/replset/catalog_manager_replica_set_shard_collection_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/s/catalog/replset/catalog_manager_replica_set_test.cpp | 97 | ||||
-rw-r--r-- | src/mongo/s/catalog/type_settings.cpp | 269 | ||||
-rw-r--r-- | src/mongo/s/catalog/type_settings.h | 192 | ||||
-rw-r--r-- | src/mongo/s/catalog/type_settings_test.cpp | 191 | ||||
-rw-r--r-- | src/mongo/s/mongos_options.cpp | 2 | ||||
-rw-r--r-- | src/mongo/s/mongos_options.h | 2 | ||||
-rw-r--r-- | src/mongo/s/sharding_initialization.cpp | 2 | ||||
-rw-r--r-- | src/mongo/s/sharding_test_fixture.cpp | 2 |
15 files changed, 581 insertions, 874 deletions
diff --git a/src/mongo/db/s/sharding_state_test.cpp b/src/mongo/db/s/sharding_state_test.cpp index 3fb118ec87e..664f3961211 100644 --- a/src/mongo/db/s/sharding_state_test.cpp +++ b/src/mongo/db/s/sharding_state_test.cpp @@ -83,7 +83,7 @@ void initGrid(OperationContext* txn, const ConnectionString& configConnString) { stdx::make_unique<CatalogCache>(), std::move(shardRegistry), stdx::make_unique<ClusterCursorManager>(txn->getServiceContext()->getPreciseClockSource()), - stdx::make_unique<BalancerConfiguration>(BalancerConfiguration::kDefaultMaxChunkSizeBytes), + stdx::make_unique<BalancerConfiguration>(ChunkSizeSettingsType::kDefaultMaxChunkSizeBytes), std::move(executorPool), mockNetwork); } diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript index 02f45fd0876..9c5b15b7daf 100644 --- a/src/mongo/s/SConscript +++ b/src/mongo/s/SConscript @@ -230,12 +230,14 @@ env.CppUnitTest( target='mongoscore_test', source=[ 'balancer_policy_tests.cpp', + 'balancer/balancer_configuration_test.cpp', 'balancer/cluster_statistics_test.cpp', 'shard_key_pattern_test.cpp', ], LIBDEPS=[ 'coreshard', 'mongoscore', + 'sharding_test_fixture', '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', '$BUILD_DIR/mongo/db/service_context_noop_init', ] diff --git a/src/mongo/s/balancer/balancer_configuration.cpp b/src/mongo/s/balancer/balancer_configuration.cpp index b40aa1e8c32..8c96ffa03f8 100644 --- a/src/mongo/s/balancer/balancer_configuration.cpp +++ b/src/mongo/s/balancer/balancer_configuration.cpp @@ -33,48 +33,56 @@ #include "mongo/s/balancer/balancer_configuration.h" #include "mongo/base/status.h" +#include "mongo/base/status_with.h" +#include "mongo/bson/bsonobj.h" +#include "mongo/bson/util/bson_extract.h" #include "mongo/s/catalog/catalog_manager.h" #include "mongo/s/grid.h" #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" namespace mongo { +namespace { -const uint64_t BalancerConfiguration::kDefaultMaxChunkSizeBytes{64 * 1024 * 1024}; +const char kValue[] = "value"; +const char kStopped[] = "stopped"; +const char kActiveWindow[] = "activeWindow"; +const char kWaitForDelete[] = "_waitForDelete"; + +} // namespace + +const char BalancerSettingsType::kKey[] = "balancer"; + +const char ChunkSizeSettingsType::kKey[] = "chunksize"; +const uint64_t ChunkSizeSettingsType::kDefaultMaxChunkSizeBytes{64 * 1024 * 1024}; BalancerConfiguration::BalancerConfiguration(uint64_t defaultMaxChunkSizeBytes) - : _secondaryThrottle( - MigrationSecondaryThrottleOptions::create(MigrationSecondaryThrottleOptions::kDefault)), + : _balancerSettings(BalancerSettingsType::createDefault()), _defaultMaxChunkSizeBytes(defaultMaxChunkSizeBytes) { - invariant(checkMaxChunkSizeValid(defaultMaxChunkSizeBytes)); - - _useDefaultBalancerSettings(); - _useDefaultChunkSizeSettings(); + // The default must always be created with the max chunk size value prevalidated + invariant(ChunkSizeSettingsType::checkMaxChunkSizeValid(_defaultMaxChunkSizeBytes)); + _maxChunkSizeBytes.store(_defaultMaxChunkSizeBytes); } BalancerConfiguration::~BalancerConfiguration() = default; bool BalancerConfiguration::isBalancerActive() const { - if (!_shouldBalance.loadRelaxed()) { - return false; - } - stdx::lock_guard<stdx::mutex> lk(_balancerSettingsMutex); - if (_balancerSettings.isBalancerActiveWindowSet()) { - return _balancerSettings.inBalancingWindow(boost::posix_time::second_clock::local_time()); + if (!_balancerSettings.shouldBalance()) { + return false; } - return true; + return _balancerSettings.isTimeInBalancingWindow(boost::posix_time::second_clock::local_time()); } MigrationSecondaryThrottleOptions BalancerConfiguration::getSecondaryThrottle() const { stdx::lock_guard<stdx::mutex> lk(_balancerSettingsMutex); - return _secondaryThrottle; + return _balancerSettings.getSecondaryThrottle(); } bool BalancerConfiguration::waitForDelete() const { stdx::lock_guard<stdx::mutex> lk(_balancerSettingsMutex); - return _waitForDelete; + return _balancerSettings.waitForDelete(); } Status BalancerConfiguration::refreshAndCheck(OperationContext* txn) { @@ -97,105 +105,203 @@ Status BalancerConfiguration::refreshAndCheck(OperationContext* txn) { return Status::OK(); } -bool BalancerConfiguration::checkMaxChunkSizeValid(uint64_t maxChunkSize) { - if (maxChunkSize >= (1024 * 1024) && maxChunkSize <= (1024 * 1024 * 1024)) { - return true; +Status BalancerConfiguration::_refreshBalancerSettings(OperationContext* txn) { + BalancerSettingsType settings = BalancerSettingsType::createDefault(); + + auto settingsObjStatus = + Grid::get(txn)->catalogManager(txn)->getGlobalSettings(txn, BalancerSettingsType::kKey); + if (settingsObjStatus.isOK()) { + auto settingsStatus = BalancerSettingsType::fromBSON(settingsObjStatus.getValue()); + if (!settingsStatus.isOK()) { + return settingsStatus.getStatus(); + } + + settings = std::move(settingsStatus.getValue()); + } else if (settingsObjStatus != ErrorCodes::NoMatchingDocument) { + return settingsObjStatus.getStatus(); } - return false; + stdx::lock_guard<stdx::mutex> lk(_balancerSettingsMutex); + _balancerSettings = std::move(settings); + + return Status::OK(); } -Status BalancerConfiguration::_refreshBalancerSettings(OperationContext* txn) { - SettingsType balancerSettings; - - auto balanceSettingsStatus = - Grid::get(txn)->catalogManager(txn)->getGlobalSettings(txn, SettingsType::BalancerDocKey); - if (balanceSettingsStatus.isOK()) { - auto settingsTypeStatus = - SettingsType::fromBSON(std::move(balanceSettingsStatus.getValue())); - if (!settingsTypeStatus.isOK()) { - return settingsTypeStatus.getStatus(); +Status BalancerConfiguration::_refreshChunkSizeSettings(OperationContext* txn) { + ChunkSizeSettingsType settings = + ChunkSizeSettingsType::createDefault(_defaultMaxChunkSizeBytes); + + auto settingsObjStatus = + grid.catalogManager(txn)->getGlobalSettings(txn, ChunkSizeSettingsType::kKey); + if (settingsObjStatus.isOK()) { + auto settingsStatus = ChunkSizeSettingsType::fromBSON(settingsObjStatus.getValue()); + if (!settingsStatus.isOK()) { + return settingsStatus.getStatus(); } - balancerSettings = std::move(settingsTypeStatus.getValue()); - } else if (balanceSettingsStatus.getStatus() != ErrorCodes::NoMatchingDocument) { - return balanceSettingsStatus.getStatus(); - } else { - _useDefaultBalancerSettings(); - return Status::OK(); + + settings = std::move(settingsStatus.getValue()); + } else if (settingsObjStatus != ErrorCodes::NoMatchingDocument) { + return settingsObjStatus.getStatus(); } - if (balancerSettings.isBalancerStoppedSet() && balancerSettings.getBalancerStopped()) { - _shouldBalance.store(false); - } else { - _shouldBalance.store(true); + if (settings.getMaxChunkSizeBytes() != getMaxChunkSizeBytes()) { + log() << "MaxChunkSize changing from " << getMaxChunkSizeBytes() / (1024 * 1024) << "MB" + << " to " << settings.getMaxChunkSizeBytes() / (1024 * 1024) << "MB"; + + _maxChunkSizeBytes.store(settings.getMaxChunkSizeBytes()); } - stdx::lock_guard<stdx::mutex> lk(_balancerSettingsMutex); - _balancerSettings = std::move(balancerSettings); + return Status::OK(); +} - if (_balancerSettings.isKeySet()) { - _secondaryThrottle = - uassertStatusOK(MigrationSecondaryThrottleOptions::createFromBalancerConfig( - _balancerSettings.toBSON())); - } else { - _secondaryThrottle = - MigrationSecondaryThrottleOptions::create(MigrationSecondaryThrottleOptions::kDefault); +BalancerSettingsType::BalancerSettingsType() + : _secondaryThrottle( + MigrationSecondaryThrottleOptions::create(MigrationSecondaryThrottleOptions::kDefault)) {} + +BalancerSettingsType BalancerSettingsType::createDefault() { + return BalancerSettingsType(); +} + +StatusWith<BalancerSettingsType> BalancerSettingsType::fromBSON(const BSONObj& obj) { + BalancerSettingsType settings; + + { + bool stopped; + Status status = bsonExtractBooleanFieldWithDefault(obj, kStopped, false, &stopped); + if (!status.isOK()) + return status; + settings._shouldBalance = !stopped; } - if (_balancerSettings.isWaitForDeleteSet() && _balancerSettings.getWaitForDelete()) { - _waitForDelete = true; - } else { - _waitForDelete = false; + { + BSONElement activeWindowElem; + Status status = bsonExtractTypedField(obj, kActiveWindow, Object, &activeWindowElem); + if (status.isOK()) { + const BSONObj balancingWindowObj = activeWindowElem.Obj(); + if (balancingWindowObj.isEmpty()) { + return Status(ErrorCodes::BadValue, "activeWindow not specified"); + } + + // Check if both 'start' and 'stop' are present + const std::string start = balancingWindowObj.getField("start").str(); + const std::string stop = balancingWindowObj.getField("stop").str(); + + if (start.empty() || stop.empty()) { + return Status(ErrorCodes::BadValue, + str::stream() + << "must specify both start and stop of balancing window: " + << balancingWindowObj); + } + + // Check that both 'start' and 'stop' are valid time-of-day + boost::posix_time::ptime startTime; + boost::posix_time::ptime stopTime; + if (!toPointInTime(start, &startTime) || !toPointInTime(stop, &stopTime)) { + return Status(ErrorCodes::BadValue, + str::stream() << kActiveWindow << " format is " + << " { start: \"hh:mm\" , stop: \"hh:mm\" }"); + } + + // Check that start and stop designate different time points + if (startTime == stopTime) { + return Status(ErrorCodes::BadValue, + str::stream() << "start and stop times must be different"); + } + + settings._activeWindowStart = startTime; + settings._activeWindowStop = stopTime; + } else if (status != ErrorCodes::NoSuchKey) { + return status; + } } - return Status::OK(); -} + { + auto secondaryThrottleStatus = + MigrationSecondaryThrottleOptions::createFromBalancerConfig(obj); + if (!secondaryThrottleStatus.isOK()) { + return secondaryThrottleStatus.getStatus(); + } + + settings._secondaryThrottle = std::move(secondaryThrottleStatus.getValue()); + } + + { + bool waitForDelete; + Status status = + bsonExtractBooleanFieldWithDefault(obj, kWaitForDelete, false, &waitForDelete); + if (!status.isOK()) + return status; + + settings._waitForDelete = waitForDelete; + } -void BalancerConfiguration::_useDefaultBalancerSettings() { - _shouldBalance.store(true); - _balancerSettings = SettingsType{}; - _waitForDelete = false; - _secondaryThrottle = - MigrationSecondaryThrottleOptions::create(MigrationSecondaryThrottleOptions::kDefault); + return settings; } -Status BalancerConfiguration::_refreshChunkSizeSettings(OperationContext* txn) { - SettingsType chunkSizeSettings; - - auto chunkSizeSettingsStatus = - grid.catalogManager(txn)->getGlobalSettings(txn, SettingsType::ChunkSizeDocKey); - if (chunkSizeSettingsStatus.isOK()) { - auto settingsTypeStatus = - SettingsType::fromBSON(std::move(chunkSizeSettingsStatus.getValue())); - if (!settingsTypeStatus.isOK()) { - return settingsTypeStatus.getStatus(); +bool BalancerSettingsType::isTimeInBalancingWindow(const boost::posix_time::ptime& now) const { + invariant(!_activeWindowStart == !_activeWindowStop); + + if (!_activeWindowStart) { + return true; + } + + LOG(1).stream() << "inBalancingWindow: " + << " now: " << now << " startTime: " << *_activeWindowStart + << " stopTime: " << *_activeWindowStop; + + if (*_activeWindowStop > *_activeWindowStart) { + if ((now >= *_activeWindowStart) && (now <= *_activeWindowStop)) { + return true; + } + } else if (*_activeWindowStart > *_activeWindowStop) { + if ((now >= *_activeWindowStart) || (now <= *_activeWindowStop)) { + return true; } - chunkSizeSettings = std::move(settingsTypeStatus.getValue()); - } else if (chunkSizeSettingsStatus.getStatus() != ErrorCodes::NoMatchingDocument) { - return chunkSizeSettingsStatus.getStatus(); } else { - _useDefaultChunkSizeSettings(); - return Status::OK(); + MONGO_UNREACHABLE; } - const uint64_t newMaxChunkSizeBytes = chunkSizeSettings.getChunkSizeMB() * 1024 * 1024; + return false; +} + +ChunkSizeSettingsType::ChunkSizeSettingsType() = default; + +ChunkSizeSettingsType ChunkSizeSettingsType::createDefault(int maxChunkSizeBytes) { + // The default must always be created with the max chunk size value prevalidated + invariant(ChunkSizeSettingsType::checkMaxChunkSizeValid(maxChunkSizeBytes)); - if (!checkMaxChunkSizeValid(newMaxChunkSizeBytes)) { + ChunkSizeSettingsType settings; + settings._maxChunkSizeBytes = maxChunkSizeBytes; + + return settings; +} + +StatusWith<ChunkSizeSettingsType> ChunkSizeSettingsType::fromBSON(const BSONObj& obj) { + long long maxChunkSizeMB; + Status status = bsonExtractIntegerField(obj, kValue, &maxChunkSizeMB); + if (!status.isOK()) + return status; + + const uint64_t maxChunkSizeBytes = maxChunkSizeMB * 1024 * 1024; + + if (!checkMaxChunkSizeValid(maxChunkSizeBytes)) { return {ErrorCodes::BadValue, - str::stream() << chunkSizeSettings.getChunkSizeMB() - << " is not a valid value for MaxChunkSize"}; + str::stream() << maxChunkSizeMB << " is not a valid value for " << kKey}; } - if (newMaxChunkSizeBytes != getMaxChunkSizeBytes()) { - log() << "MaxChunkSize changing from " << getMaxChunkSizeBytes() / (1024 * 1024) << "MB" - << " to " << newMaxChunkSizeBytes / (1024 * 1024) << "MB"; - } + ChunkSizeSettingsType settings; + settings._maxChunkSizeBytes = maxChunkSizeBytes; - return Status::OK(); + return settings; } -void BalancerConfiguration::_useDefaultChunkSizeSettings() { - _maxChunkSizeBytes.store(_defaultMaxChunkSizeBytes); +bool ChunkSizeSettingsType::checkMaxChunkSizeValid(uint64_t maxChunkSizeBytes) { + if (maxChunkSizeBytes >= (1024 * 1024) && maxChunkSizeBytes <= (1024 * 1024 * 1024)) { + return true; + } + + return false; } + } // namespace mongo diff --git a/src/mongo/s/balancer/balancer_configuration.h b/src/mongo/s/balancer/balancer_configuration.h index fafc8a7bd1d..0cc2cb48cd4 100644 --- a/src/mongo/s/balancer/balancer_configuration.h +++ b/src/mongo/s/balancer/balancer_configuration.h @@ -28,34 +28,141 @@ #pragma once +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/optional.hpp> #include <cstdint> #include "mongo/base/disallow_copying.h" -#include "mongo/s/catalog/type_settings.h" #include "mongo/s/migration_secondary_throttle_options.h" #include "mongo/stdx/mutex.h" #include "mongo/platform/atomic_word.h" namespace mongo { +class BSONObj; class MigrationSecondaryThrottleOptions; class OperationContext; class Status; +template <typename T> +class StatusWith; /** - * Contains settings, which control the behaviour of the balancer. + * Utility class to parse the balancer settings document, which has the following format: + * + * balancer: { + * stopped: <true|false>, + * activeWindow: { start: "<HH:MM>", stop: "<HH:MM>" } + * } */ -class BalancerConfiguration { - MONGO_DISALLOW_COPYING(BalancerConfiguration); +class BalancerSettingsType { +public: + // The key under which this setting is stored on the config server + static const char kKey[]; + + /** + * Constructs a settings object with the default values. To be used when no balancer settings + * have been specified. + */ + static BalancerSettingsType createDefault(); + + /** + * Interprets the BSON content as balancer settings and extracts the respective values. + */ + static StatusWith<BalancerSettingsType> fromBSON(const BSONObj& obj); + + /** + * Returns whether the balancer is enabled. + */ + bool shouldBalance() const { + return _shouldBalance; + } + + /** + * Returns true if either 'now' is in the balancing window or if no balancing window exists. + */ + bool isTimeInBalancingWindow(const boost::posix_time::ptime& now) const; + + /** + * Returns the secondary throttle options. + */ + const MigrationSecondaryThrottleOptions& getSecondaryThrottle() const { + return _secondaryThrottle; + } + + /** + * Returns whether the balancer should wait for deletions after each completed move. + */ + bool waitForDelete() const { + return _waitForDelete; + } + + +private: + BalancerSettingsType(); + + bool _shouldBalance{true}; + boost::optional<boost::posix_time::ptime> _activeWindowStart; + boost::optional<boost::posix_time::ptime> _activeWindowStop; + + MigrationSecondaryThrottleOptions _secondaryThrottle; + + bool _waitForDelete{false}; +}; + +/** + * Utility class to parse the chunk size settings document, which has the following format: + * + * chunksize: { value: <value in MB between 1 and 1024> } + */ +class ChunkSizeSettingsType { public: + // The key under which this setting is stored on the config server + static const char kKey[]; + // Default value used for the max chunk size if one is not specified in the balancer // configuration static const uint64_t kDefaultMaxChunkSizeBytes; /** - * Primes the balancer configuration with some default values. These settings may change at a - * later time after a call to refresh(). + * Constructs a settings object with the default values. To be used when no chunk size settings + * have been specified. + */ + static ChunkSizeSettingsType createDefault(int maxChunkSizeBytes); + + /** + * Interprets the BSON content as chunk size settings and extracts the respective values. + */ + static StatusWith<ChunkSizeSettingsType> fromBSON(const BSONObj& obj); + + uint64_t getMaxChunkSizeBytes() const { + return _maxChunkSizeBytes; + } + + /** + * Validates that the specified max chunk size value (in bytes) is allowed. + */ + static bool checkMaxChunkSizeValid(uint64_t maxChunkSizeBytes); + +private: + ChunkSizeSettingsType(); + + uint64_t _maxChunkSizeBytes{kDefaultMaxChunkSizeBytes}; +}; + +/** + * Contains settings, which control the behaviour of the balancer. + */ +class BalancerConfiguration { + MONGO_DISALLOW_COPYING(BalancerConfiguration); + +public: + /** + * Primes the balancer configuration with some default values. The effective settings may change + * at a later time after a call to refresh(). + * + * defaultMaxChunkSizeBytes indicates the value to be use for the MaxChunkSize parameter if one + * has not been specified in config.settings. This parameter must have been pre-validated. */ BalancerConfiguration(uint64_t defaultMaxChunkSizeBytes); ~BalancerConfiguration(); @@ -94,11 +201,6 @@ public: */ Status refreshAndCheck(OperationContext* txn); - /** - * Validates that the specified max chunk size value (in bytes) is allowed. - */ - static bool checkMaxChunkSizeValid(uint64_t maxChunkSizeBytes); - private: /** * Reloads the balancer configuration from the settings document. Fails if the settings document @@ -107,37 +209,22 @@ private: Status _refreshBalancerSettings(OperationContext* txn); /** - * If the balancer settings document is missing, these are the defaults, which will be used. - */ - void _useDefaultBalancerSettings(); - - /** * Reloads the chunk sizes configuration from the settings document. Fails if the settings * document cannot be read or if any setting contains invalid value, in which case the offending * value will remain unchanged. */ Status _refreshChunkSizeSettings(OperationContext* txn); - /** - * If the chunk size settings document is missing, these are the defaults, which will be used. - */ - void _useDefaultChunkSizeSettings(); - - // Whether auto-balancing of chunks should happen - AtomicBool _shouldBalance{true}; - - // The latest read balancer settings (used for the balancer window and secondary throttle) and a - // mutex to protect its changes + // The latest read balancer settings and a mutex to protect its swaps mutable stdx::mutex _balancerSettingsMutex; - SettingsType _balancerSettings; - bool _waitForDelete{false}; - MigrationSecondaryThrottleOptions _secondaryThrottle; + BalancerSettingsType _balancerSettings; // Default value for use for the max chunk size if the setting is not present in the balancer // configuration const uint64_t _defaultMaxChunkSizeBytes; - // Max chunk size after which a chunk would be considered jumbo and won't be moved + // Max chunk size after which a chunk would be considered jumbo and won't be moved. This value + // is read on the critical path after each write operation, that's why it is cached. AtomicUInt64 _maxChunkSizeBytes; }; diff --git a/src/mongo/s/balancer/balancer_configuration_test.cpp b/src/mongo/s/balancer/balancer_configuration_test.cpp new file mode 100644 index 00000000000..ffe95dde645 --- /dev/null +++ b/src/mongo/s/balancer/balancer_configuration_test.cpp @@ -0,0 +1,263 @@ +/** + * Copyright (C) 2016 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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 <boost/optional.hpp> +#include <vector> + +#include "mongo/bson/bsonmisc.h" +#include "mongo/client/remote_command_targeter_mock.h" +#include "mongo/db/namespace_string.h" +#include "mongo/db/query/lite_parsed_query.h" +#include "mongo/executor/remote_command_request.h" +#include "mongo/rpc/metadata/repl_set_metadata.h" +#include "mongo/rpc/metadata/server_selection_metadata.h" +#include "mongo/s/balancer/balancer_configuration.h" +#include "mongo/s/catalog/catalog_manager.h" +#include "mongo/s/sharding_test_fixture.h" +#include "mongo/unittest/unittest.h" +#include "mongo/util/net/hostandport.h" + +namespace mongo { +namespace { + +using executor::RemoteCommandRequest; +using std::vector; +using unittest::assertGet; + +const BSONObj kReplSecondaryOkMetadata{[] { + BSONObjBuilder o; + o.appendElements(rpc::ServerSelectionMetadata(true, boost::none).toBSON()); + o.append(rpc::kReplSetMetadataFieldName, 1); + return o.obj(); +}()}; + +class BalancerConfigurationTestFixture : public ShardingTestFixture { +protected: + /** + * Expects a correct find command to be dispatched for the config.settings namespace and returns + * the specified result. If an empty boost::optional is passed, returns an empty results. + */ + void expectSettingsQuery(StringData key, StatusWith<boost::optional<BSONObj>> result) { + onFindCommand([&](const RemoteCommandRequest& request) { + ASSERT_EQUALS(kReplSecondaryOkMetadata, request.metadata); + + const NamespaceString nss(request.dbname, request.cmdObj.firstElement().String()); + ASSERT_EQ(nss.ns(), "config.settings"); + + auto query = + assertGet(LiteParsedQuery::makeFromFindCommand(nss, request.cmdObj, false)); + + ASSERT_EQ(query->ns(), "config.settings"); + ASSERT_EQ(query->getFilter(), BSON("_id" << key)); + + checkReadConcern(request.cmdObj, Timestamp(0, 0), repl::OpTime::kUninitializedTerm); + + if (!result.isOK()) { + return StatusWith<vector<BSONObj>>(result.getStatus()); + } + + if (result.getValue()) { + return StatusWith<vector<BSONObj>>(vector<BSONObj>{*(result.getValue())}); + } + + return StatusWith<vector<BSONObj>>(vector<BSONObj>{}); + }); + } +}; + +TEST_F(BalancerConfigurationTestFixture, NoConfigurationDocuments) { + configTargeter()->setFindHostReturnValue(HostAndPort("TestHost1")); + + BalancerConfiguration config(1024 * 1024ULL); + + auto future = launchAsync([&] { ASSERT_OK(config.refreshAndCheck(operationContext())); }); + + expectSettingsQuery(BalancerSettingsType::kKey, boost::optional<BSONObj>()); + expectSettingsQuery(ChunkSizeSettingsType::kKey, boost::optional<BSONObj>()); + + future.timed_get(kFutureTimeout); + + ASSERT(config.isBalancerActive()); + ASSERT_EQ(MigrationSecondaryThrottleOptions::kDefault, + config.getSecondaryThrottle().getSecondaryThrottle()); + ASSERT_EQ(1024 * 1024ULL, config.getMaxChunkSizeBytes()); +} + +TEST_F(BalancerConfigurationTestFixture, ChunkSizeSettingsDocumentOnly) { + configTargeter()->setFindHostReturnValue(HostAndPort("TestHost1")); + + BalancerConfiguration config(1024 * 1024ULL); + + auto future = launchAsync([&] { ASSERT_OK(config.refreshAndCheck(operationContext())); }); + + expectSettingsQuery(BalancerSettingsType::kKey, boost::optional<BSONObj>()); + expectSettingsQuery(ChunkSizeSettingsType::kKey, boost::optional<BSONObj>(BSON("value" << 3))); + + future.timed_get(kFutureTimeout); + + ASSERT(config.isBalancerActive()); + ASSERT_EQ(MigrationSecondaryThrottleOptions::kDefault, + config.getSecondaryThrottle().getSecondaryThrottle()); + ASSERT_EQ(3 * 1024 * 1024ULL, config.getMaxChunkSizeBytes()); +} + +TEST_F(BalancerConfigurationTestFixture, BalancerSettingsDocumentOnly) { + configTargeter()->setFindHostReturnValue(HostAndPort("TestHost1")); + + BalancerConfiguration config(1024 * 1024ULL); + + auto future = launchAsync([&] { ASSERT_OK(config.refreshAndCheck(operationContext())); }); + + expectSettingsQuery(BalancerSettingsType::kKey, + boost::optional<BSONObj>(BSON("stopped" << true))); + expectSettingsQuery(ChunkSizeSettingsType::kKey, boost::optional<BSONObj>()); + + future.timed_get(kFutureTimeout); + + ASSERT(!config.isBalancerActive()); + ASSERT_EQ(MigrationSecondaryThrottleOptions::kDefault, + config.getSecondaryThrottle().getSecondaryThrottle()); + ASSERT_EQ(1024 * 1024ULL, config.getMaxChunkSizeBytes()); +} + +TEST(BalancerSettingsType, BalancerDisabled) { + BalancerSettingsType settings = + assertGet(BalancerSettingsType::fromBSON(BSON("stopped" << true))); + ASSERT(!settings.shouldBalance()); +} + +TEST(BalancerSettingsType, BalancingWindowStartLessThanStop) { + BalancerSettingsType settings = + assertGet(BalancerSettingsType::fromBSON(BSON("activeWindow" << BSON("start" + << "9:00" + << "stop" + << "19:00")))); + + ASSERT(settings.isTimeInBalancingWindow(boost::posix_time::ptime( + currentDate(), boost::posix_time::hours(9) + boost::posix_time::minutes(0)))); + ASSERT(settings.isTimeInBalancingWindow(boost::posix_time::ptime( + currentDate(), boost::posix_time::hours(10) + boost::posix_time::minutes(30)))); + ASSERT(settings.isTimeInBalancingWindow(boost::posix_time::ptime( + currentDate(), boost::posix_time::hours(19) + boost::posix_time::minutes(0)))); + + ASSERT(!settings.isTimeInBalancingWindow(boost::posix_time::ptime( + currentDate(), boost::posix_time::hours(8) + boost::posix_time::minutes(59)))); + ASSERT(!settings.isTimeInBalancingWindow(boost::posix_time::ptime( + currentDate(), boost::posix_time::hours(19) + boost::posix_time::minutes(1)))); +} + +TEST(BalancerSettingsType, BalancingWindowStopLessThanStart) { + BalancerSettingsType settings = + assertGet(BalancerSettingsType::fromBSON(BSON("activeWindow" << BSON("start" + << "23:00" + << "stop" + << "8:00")))); + + ASSERT(settings.isTimeInBalancingWindow(boost::posix_time::ptime( + currentDate(), boost::posix_time::hours(23) + boost::posix_time::minutes(0)))); + ASSERT(settings.isTimeInBalancingWindow(boost::posix_time::ptime( + currentDate(), boost::posix_time::hours(2) + boost::posix_time::minutes(30)))); + ASSERT(settings.isTimeInBalancingWindow(boost::posix_time::ptime( + currentDate(), boost::posix_time::hours(7) + boost::posix_time::minutes(59)))); + + ASSERT(!settings.isTimeInBalancingWindow(boost::posix_time::ptime( + currentDate(), boost::posix_time::hours(8) + boost::posix_time::minutes(1)))); + ASSERT(!settings.isTimeInBalancingWindow(boost::posix_time::ptime( + currentDate(), boost::posix_time::hours(22) + boost::posix_time::minutes(00)))); +} + +TEST(BalancerSettingsType, InvalidBalancingWindowStartEqualsStop) { + ASSERT_NOT_OK( + BalancerSettingsType::fromBSON(BSON("activeWindow" << BSON("start" + << "00:00" + << "stop" + << "00:00"))).getStatus()); +} + +TEST(BalancerSettingsType, InvalidBalancingWindowTimeFormat) { + ASSERT_NOT_OK(BalancerSettingsType::fromBSON(BSON("activeWindow" << BSON("start" + << "23" + << "stop" + << "6"))).getStatus()); + + ASSERT_NOT_OK(BalancerSettingsType::fromBSON( + BSON("activeWindow" << BSON("start" << 23LL << "stop" + << "6:00"))).getStatus()); + + ASSERT_NOT_OK( + BalancerSettingsType::fromBSON(BSON("activeWindow" << BSON("start" + << "23:00" + << "stop" << 6LL))).getStatus()); +} + +TEST(BalancerSettingsType, InvalidBalancingWindowFormat) { + ASSERT_NOT_OK( + BalancerSettingsType::fromBSON(BSON("activeWindow" << BSON("begin" + << "23:00" + << "stop" + << "6:00"))).getStatus()); + + ASSERT_NOT_OK( + BalancerSettingsType::fromBSON(BSON("activeWindow" << BSON("start" + << "23:00" + << "end" + << "6:00"))).getStatus()); +} + +TEST(ChunkSizeSettingsType, NormalValues) { + ASSERT_EQ( + 1024 * 1024ULL, + assertGet(ChunkSizeSettingsType::fromBSON(BSON("value" << 1))).getMaxChunkSizeBytes()); + ASSERT_EQ( + 10 * 1024 * 1024ULL, + assertGet(ChunkSizeSettingsType::fromBSON(BSON("value" << 10))).getMaxChunkSizeBytes()); + ASSERT_EQ( + 1024 * 1024 * 1024ULL, + assertGet(ChunkSizeSettingsType::fromBSON(BSON("value" << 1024))).getMaxChunkSizeBytes()); +} + +TEST(ChunkSizeSettingsType, BackwardsCompatibilityDueToExtraKeys) { + ASSERT_EQ(1024 * 1024ULL, + assertGet(ChunkSizeSettingsType::fromBSON(BSON("value" << 1 << "SomeFutureKey" + << "SomeFutureValue"))) + .getMaxChunkSizeBytes()); +} + +TEST(ChunkSizeSettingsType, IllegalValues) { + ASSERT_NOT_OK(ChunkSizeSettingsType::fromBSON(BSON("value" << 0)).getStatus()); + ASSERT_NOT_OK(ChunkSizeSettingsType::fromBSON(BSON("value" << -1)).getStatus()); + ASSERT_NOT_OK(ChunkSizeSettingsType::fromBSON(BSON("value" << 1025)).getStatus()); + ASSERT_NOT_OK(ChunkSizeSettingsType::fromBSON(BSON("value" + << "WrongType")).getStatus()); + ASSERT_NOT_OK(ChunkSizeSettingsType::fromBSON(BSON("IllegalKey" << 1)).getStatus()); +} + +} // namespace +} // namespace mongo diff --git a/src/mongo/s/catalog/SConscript b/src/mongo/s/catalog/SConscript index c79fff0d7ab..f2be9260e3b 100644 --- a/src/mongo/s/catalog/SConscript +++ b/src/mongo/s/catalog/SConscript @@ -20,7 +20,6 @@ env.Library( 'type_lockpings.cpp', 'type_locks.cpp', 'type_mongos.cpp', - 'type_settings.cpp', 'type_shard.cpp', 'type_tags.cpp', ], @@ -52,7 +51,6 @@ env.CppUnitTest( 'type_lockpings_test.cpp', 'type_locks_test.cpp', 'type_mongos_test.cpp', - 'type_settings_test.cpp', 'type_shard_test.cpp', 'type_tags_test.cpp', ], diff --git a/src/mongo/s/catalog/replset/catalog_manager_replica_set_shard_collection_test.cpp b/src/mongo/s/catalog/replset/catalog_manager_replica_set_shard_collection_test.cpp index 7971a0ce846..8fc2cf1f984 100644 --- a/src/mongo/s/catalog/replset/catalog_manager_replica_set_shard_collection_test.cpp +++ b/src/mongo/s/catalog/replset/catalog_manager_replica_set_shard_collection_test.cpp @@ -686,7 +686,7 @@ TEST_F(ShardCollectionTest, withInitialData) { ASSERT_EQUALS(keyPattern.toBSON(), request.cmdObj["keyPattern"].Obj()); ASSERT_EQUALS(keyPattern.getKeyPattern().globalMin(), request.cmdObj["min"].Obj()); ASSERT_EQUALS(keyPattern.getKeyPattern().globalMax(), request.cmdObj["max"].Obj()); - ASSERT_EQUALS(BalancerConfiguration::kDefaultMaxChunkSizeBytes, + ASSERT_EQUALS(ChunkSizeSettingsType::kDefaultMaxChunkSizeBytes, static_cast<uint64_t>(request.cmdObj["maxChunkSizeBytes"].numberLong())); ASSERT_EQUALS(0, request.cmdObj["maxSplitPoints"].numberLong()); ASSERT_EQUALS(0, request.cmdObj["maxChunkObjects"].numberLong()); diff --git a/src/mongo/s/catalog/replset/catalog_manager_replica_set_test.cpp b/src/mongo/s/catalog/replset/catalog_manager_replica_set_test.cpp index 45d7fdba010..a998c34e935 100644 --- a/src/mongo/s/catalog/replset/catalog_manager_replica_set_test.cpp +++ b/src/mongo/s/catalog/replset/catalog_manager_replica_set_test.cpp @@ -48,7 +48,6 @@ #include "mongo/s/catalog/type_collection.h" #include "mongo/s/catalog/type_database.h" #include "mongo/s/catalog/type_shard.h" -#include "mongo/s/catalog/type_settings.h" #include "mongo/s/catalog/type_tags.h" #include "mongo/s/chunk_version.h" #include "mongo/s/client/shard_registry.h" @@ -834,102 +833,6 @@ TEST_F(CatalogManagerReplSetTest, RunUserManagementWriteCommandNotMasterRetrySuc future.timed_get(kFutureTimeout); } -TEST_F(CatalogManagerReplSetTest, GetGlobalSettingsBalancerDoc) { - configTargeter()->setFindHostReturnValue(HostAndPort("TestHost1")); - - // sample balancer doc - SettingsType st1; - st1.setKey(SettingsType::BalancerDocKey); - st1.setBalancerStopped(true); - - auto future = launchAsync([this] { - return assertGet( - catalogManager()->getGlobalSettings(operationContext(), SettingsType::BalancerDocKey)); - }); - - onFindCommand([this, st1](const RemoteCommandRequest& request) { - ASSERT_EQUALS(kReplSecondaryOkMetadata, request.metadata); - - const NamespaceString nss(request.dbname, request.cmdObj.firstElement().String()); - ASSERT_EQ(nss.ns(), SettingsType::ConfigNS); - - auto query = assertGet(LiteParsedQuery::makeFromFindCommand(nss, request.cmdObj, false)); - - ASSERT_EQ(query->ns(), SettingsType::ConfigNS); - ASSERT_EQ(query->getFilter(), BSON(SettingsType::key(SettingsType::BalancerDocKey))); - - checkReadConcern(request.cmdObj, Timestamp(0, 0), repl::OpTime::kUninitializedTerm); - - return vector<BSONObj>{st1.toBSON()}; - }); - - const auto& actualBalSettings = - assertGet(SettingsType::fromBSON(future.timed_get(kFutureTimeout))); - ASSERT_EQ(actualBalSettings.toBSON(), st1.toBSON()); -} - -TEST_F(CatalogManagerReplSetTest, GetGlobalSettingsChunkSizeDoc) { - configTargeter()->setFindHostReturnValue(HostAndPort("TestHost1")); - - // sample chunk size doc - SettingsType st1; - st1.setKey(SettingsType::ChunkSizeDocKey); - st1.setChunkSizeMB(80); - - auto future = launchAsync([this] { - return assertGet( - catalogManager()->getGlobalSettings(operationContext(), SettingsType::ChunkSizeDocKey)); - }); - - onFindCommand([this, st1](const RemoteCommandRequest& request) { - ASSERT_EQUALS(kReplSecondaryOkMetadata, request.metadata); - - const NamespaceString nss(request.dbname, request.cmdObj.firstElement().String()); - ASSERT_EQ(nss.ns(), SettingsType::ConfigNS); - - auto query = assertGet(LiteParsedQuery::makeFromFindCommand(nss, request.cmdObj, false)); - - ASSERT_EQ(query->ns(), SettingsType::ConfigNS); - ASSERT_EQ(query->getFilter(), BSON(SettingsType::key(SettingsType::ChunkSizeDocKey))); - - checkReadConcern(request.cmdObj, Timestamp(0, 0), repl::OpTime::kUninitializedTerm); - - return vector<BSONObj>{st1.toBSON()}; - }); - - const auto& actualBalSettings = - assertGet(SettingsType::fromBSON(future.timed_get(kFutureTimeout))); - ASSERT_EQ(actualBalSettings.toBSON(), st1.toBSON()); -} - -TEST_F(CatalogManagerReplSetTest, GetGlobalSettingsNonExistent) { - configTargeter()->setFindHostReturnValue(HostAndPort("TestHost1")); - - auto future = launchAsync([this] { - auto status = - catalogManager()->getGlobalSettings(operationContext(), SettingsType::ChunkSizeDocKey); - ASSERT_EQ(status.getStatus(), ErrorCodes::NoMatchingDocument); - }); - - onFindCommand([this](const RemoteCommandRequest& request) { - ASSERT_EQUALS(kReplSecondaryOkMetadata, request.metadata); - - const NamespaceString nss(request.dbname, request.cmdObj.firstElement().String()); - ASSERT_EQ(nss.ns(), SettingsType::ConfigNS); - - auto query = assertGet(LiteParsedQuery::makeFromFindCommand(nss, request.cmdObj, false)); - - ASSERT_EQ(query->ns(), SettingsType::ConfigNS); - ASSERT_EQ(query->getFilter(), BSON(SettingsType::key(SettingsType::ChunkSizeDocKey))); - - checkReadConcern(request.cmdObj, Timestamp(0, 0), repl::OpTime::kUninitializedTerm); - - return vector<BSONObj>{}; - }); - - future.timed_get(kFutureTimeout); -} - TEST_F(CatalogManagerReplSetTest, GetCollectionsValidResultsNoDb) { configTargeter()->setFindHostReturnValue(HostAndPort("TestHost1")); diff --git a/src/mongo/s/catalog/type_settings.cpp b/src/mongo/s/catalog/type_settings.cpp deleted file mode 100644 index b67fa4af6ec..00000000000 --- a/src/mongo/s/catalog/type_settings.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/** - * Copyright (C) 2012 10gen Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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 - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * 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 GNU Affero General 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. - */ - -#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kSharding - -#include "mongo/platform/basic.h" - -#include "mongo/s/catalog/type_settings.h" - -#include <memory> - -#include "mongo/base/status_with.h" -#include "mongo/bson/bsonobj.h" -#include "mongo/bson/bsonobjbuilder.h" -#include "mongo/bson/util/bson_extract.h" -#include "mongo/stdx/memory.h" -#include "mongo/util/assert_util.h" -#include "mongo/util/log.h" -#include "mongo/util/mongoutils/str.h" -#include "mongo/util/time_support.h" - -namespace mongo { - -const std::string SettingsType::ConfigNS = "config.settings"; -const std::string SettingsType::BalancerDocKey("balancer"); -const std::string SettingsType::ChunkSizeDocKey("chunksize"); - -const BSONField<std::string> SettingsType::key("_id"); -const BSONField<long long> SettingsType::chunkSizeMB("value"); -const BSONField<bool> SettingsType::balancerStopped("stopped"); -const BSONField<BSONObj> SettingsType::balancerActiveWindow("activeWindow"); -const BSONField<bool> SettingsType::deprecated_secondaryThrottle("_secondaryThrottle"); -const BSONField<BSONObj> SettingsType::migrationWriteConcern("_secondaryThrottle"); -const BSONField<bool> SettingsType::waitForDelete("_waitForDelete"); - -StatusWith<SettingsType> SettingsType::fromBSON(const BSONObj& source) { - SettingsType settings; - - { - std::string settingsKey; - Status status = bsonExtractStringField(source, key.name(), &settingsKey); - if (!status.isOK()) - return status; - settings._key = settingsKey; - } - - if (settings._key == ChunkSizeDocKey) { - long long settingsChunkSizeMB; - Status status = bsonExtractIntegerField(source, chunkSizeMB.name(), &settingsChunkSizeMB); - if (!status.isOK()) - return status; - settings._chunkSizeMB = settingsChunkSizeMB; - } else if (settings._key == BalancerDocKey) { - { - bool settingsBalancerStopped; - Status status = bsonExtractBooleanFieldWithDefault( - source, balancerStopped.name(), false, &settingsBalancerStopped); - if (!status.isOK()) - return status; - settings._balancerStopped = settingsBalancerStopped; - } - - { - BSONElement settingsBalancerActiveWindowElem; - Status status = bsonExtractTypedField( - source, balancerActiveWindow.name(), Object, &settingsBalancerActiveWindowElem); - if (status != ErrorCodes::NoSuchKey) { - if (!status.isOK()) - return status; - StatusWith<BoostTimePair> timePairResult = - settings._parseBalancingWindow(settingsBalancerActiveWindowElem.Obj()); - if (!timePairResult.isOK()) - return timePairResult.getStatus(); - settings._balancerActiveWindow = timePairResult.getValue(); - } - } - - { - BSONElement settingsMigrationWriteConcernElem; - Status status = bsonExtractTypedField( - source, migrationWriteConcern.name(), Object, &settingsMigrationWriteConcernElem); - if (status == ErrorCodes::TypeMismatch) { - bool settingsSecondaryThrottle; - status = bsonExtractBooleanFieldWithDefault( - source, deprecated_secondaryThrottle.name(), true, &settingsSecondaryThrottle); - if (!status.isOK()) - return status; - settings._secondaryThrottle = settingsSecondaryThrottle; - } else if (status != ErrorCodes::NoSuchKey) { - if (!status.isOK()) - return status; - settings._migrationWriteConcern = WriteConcernOptions(); - status = - settings._migrationWriteConcern->parse(settingsMigrationWriteConcernElem.Obj()); - if (!status.isOK()) - return status; - } - } - - { - bool settingsWaitForDelete; - Status status = - bsonExtractBooleanField(source, waitForDelete.name(), &settingsWaitForDelete); - if (status != ErrorCodes::NoSuchKey) { - if (!status.isOK()) - return status; - settings._waitForDelete = settingsWaitForDelete; - } - } - } - - return settings; -} - -Status SettingsType::validate() const { - if (!_key.is_initialized() || _key->empty()) { - return Status(ErrorCodes::NoSuchKey, str::stream() << "missing " << key.name() << " field"); - } - - if (_key == ChunkSizeDocKey) { - if (!(getChunkSizeMB() > 0)) { - return Status(ErrorCodes::BadValue, - str::stream() << "chunksize specified in " << chunkSizeMB.name() - << " field must be greater than zero"); - } - } else if (_key == BalancerDocKey) { - if (_secondaryThrottle.is_initialized() && _migrationWriteConcern.is_initialized()) { - return Status(ErrorCodes::BadValue, - str::stream() << "cannot have both secondary throttle and " - << "migration write concern set at the same time"); - } - } else { - return Status(ErrorCodes::UnsupportedFormat, - str::stream() << "unsupported key in " << key.name() << " field"); - } - - return Status::OK(); -} - -BSONObj SettingsType::toBSON() const { - BSONObjBuilder builder; - - if (_key) - builder.append(key(), getKey()); - if (_chunkSizeMB) - builder.append(chunkSizeMB(), getChunkSizeMB()); - if (_balancerStopped) - builder.append(balancerStopped(), getBalancerStopped()); - - // These two are mutually exclusive - if (_secondaryThrottle) - builder.append(deprecated_secondaryThrottle(), *_secondaryThrottle); - if (_migrationWriteConcern) - builder.append(migrationWriteConcern(), _migrationWriteConcern->toBSON()); - - if (_waitForDelete) - builder.append(waitForDelete(), getWaitForDelete()); - - return builder.obj(); -} - -std::string SettingsType::toString() const { - return toBSON().toString(); -} - -StatusWith<BoostTimePair> SettingsType::_parseBalancingWindow(const BSONObj& balancingWindowObj) { - if (balancingWindowObj.isEmpty()) { - return Status(ErrorCodes::BadValue, "'activeWindow' can't be empty"); - } - - // check if both 'start' and 'stop' are present - std::string start = balancingWindowObj.getField("start").str(); - std::string stop = balancingWindowObj.getField("stop").str(); - if (start.empty() || stop.empty()) { - return Status(ErrorCodes::BadValue, - str::stream() << "must specify both start and end of balancing window: " - << balancingWindowObj); - } - - // check that both 'start' and 'stop' are valid time-of-day - boost::posix_time::ptime startTime, stopTime; - if (!toPointInTime(start, &startTime) || !toPointInTime(stop, &stopTime)) { - return Status(ErrorCodes::BadValue, - str::stream() << balancerActiveWindow.name() << " format is " - << " { start: \"hh:mm\" , stop: \"hh:mm\" }"); - } - - return std::make_pair(startTime, stopTime); -} - -bool SettingsType::inBalancingWindow(const boost::posix_time::ptime& now) const { - if (!_balancerActiveWindow.is_initialized()) { - return true; - } - const boost::posix_time::ptime& startTime = _balancerActiveWindow->first; - const boost::posix_time::ptime& stopTime = _balancerActiveWindow->second; - - LOG(1).stream() << "inBalancingWindow: " - << " now: " << now << " startTime: " << startTime << " stopTime: " << stopTime; - - // allow balancing if during the activeWindow - // note that a window may be open during the night - if (stopTime > startTime) { - if ((now >= startTime) && (now <= stopTime)) { - return true; - } - } else if (startTime > stopTime) { - if ((now >= startTime) || (now <= stopTime)) { - return true; - } - } - - return false; -} - -void SettingsType::setKey(const std::string& key) { - invariant(!key.empty()); - _key = key; -} - -void SettingsType::setChunkSizeMB(const long long chunkSizeMB) { - invariant(_key == ChunkSizeDocKey); - invariant(chunkSizeMB > 0); - _chunkSizeMB = chunkSizeMB; -} - -void SettingsType::setBalancerStopped(const bool balancerStopped) { - invariant(_key == BalancerDocKey); - _balancerStopped = balancerStopped; -} - -void SettingsType::setBalancerActiveWindow(const BSONObj& balancerActiveWindow) { - invariant(_key == BalancerDocKey); - StatusWith<BoostTimePair> timePairResult = _parseBalancingWindow(balancerActiveWindow); - invariant(timePairResult.isOK()); - _balancerActiveWindow = timePairResult.getValue(); -} - -void SettingsType::setWaitForDelete(const bool waitForDelete) { - invariant(_key == BalancerDocKey); - _waitForDelete = waitForDelete; -} - -} // namespace mongo diff --git a/src/mongo/s/catalog/type_settings.h b/src/mongo/s/catalog/type_settings.h deleted file mode 100644 index 464bf7efa50..00000000000 --- a/src/mongo/s/catalog/type_settings.h +++ /dev/null @@ -1,192 +0,0 @@ -/** - * Copyright (C) 2012 10gen Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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 - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * 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 GNU Affero General 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/date_time/posix_time/posix_time.hpp> -#include <boost/optional.hpp> -#include <string> -#include <utility> - -#include "mongo/bson/bson_field.h" -#include "mongo/db/write_concern_options.h" - -namespace mongo { - -class BSONObj; -template <typename T> -class StatusWith; - -using BoostTimePair = std::pair<boost::posix_time::ptime, boost::posix_time::ptime>; - -/** - * This class represents the layout and contents of documents contained in the - * config.settings collection. All manipulation of documents coming from that - * collection should be done with this class. - * - * Usage Example: - * - * // Contact the config. 'conn' has been obtained before. - * DBClientBase* conn; - * BSONObj query = QUERY(SettingsType::exampleField(SettingsType::ExampleFieldName)); - * exampleDoc = conn->findOne(SettingsType::ConfigNS, query); - * - * // Process the response. - * StatusWith<SettingsType> exampleResult = SettingsType::fromBSON(exampleDoc); - * if (!exampleResult.isOK()) { - * if (exampleResult.getStatus() == ErrorCodes::NoSuchKey) { - * // exampleDoc has no key set or is empty - * } - * // handle error -- exampleResult.getStatus() - * } - * SettingsType exampleType = exampleResult.getValue(); - */ -class SettingsType { -public: - // Name of the settings collection in the config server. - static const std::string ConfigNS; - - static const std::string BalancerDocKey; - static const std::string ChunkSizeDocKey; - - // Field names and types in the settings collection type. - static const BSONField<std::string> key; - static const BSONField<long long> chunkSizeMB; - static const BSONField<bool> balancerStopped; - static const BSONField<BSONObj> balancerActiveWindow; - static const BSONField<bool> deprecated_secondaryThrottle; - static const BSONField<BSONObj> migrationWriteConcern; - static const BSONField<bool> waitForDelete; - - /** - * Returns OK if all mandatory fields have been set and their corresponding - * values are valid. - */ - Status validate() const; - - /** - * Returns the BSON representation of the entry. - */ - BSONObj toBSON() const; - - /** - * Constructs a new ShardType object from BSON. - * Also does validation of the contents. - */ - static StatusWith<SettingsType> fromBSON(const BSONObj& source); - - /** - * Returns a std::string representation of the current internal state. - */ - std::string toString() const; - - /** - * Returns true if either 'now' is in the balancing window or - * if no balancing window exists. - */ - bool inBalancingWindow(const boost::posix_time::ptime& now) const; - - bool isKeySet() const { - return _key.is_initialized(); - } - const std::string& getKey() const { - return _key.get(); - } - void setKey(const std::string& key); - - bool isChunkSizeMBSet() const { - return _chunkSizeMB.is_initialized(); - } - long long getChunkSizeMB() const { - return _chunkSizeMB.get(); - } - void setChunkSizeMB(const long long chunkSizeMB); - - bool isBalancerStoppedSet() const { - return _balancerStopped.is_initialized(); - } - bool getBalancerStopped() const { - return _balancerStopped.get(); - } - void setBalancerStopped(const bool balancerStopped); - - bool isBalancerActiveWindowSet() const { - return _balancerActiveWindow.is_initialized(); - } - const BoostTimePair& getBalancerActiveWindow() const { - return _balancerActiveWindow.get(); - } - void setBalancerActiveWindow(const BSONObj& balancerActiveWindow); - - bool isWaitForDeleteSet() const { - return _waitForDelete.is_initialized(); - } - bool getWaitForDelete() const { - return _waitForDelete.get(); - } - void setWaitForDelete(const bool waitForDelete); - -private: - /** - * Used to parse balancing 'activeWindow'. - * See '_balancerActiveWindow' member variable doc comments below. - */ - StatusWith<BoostTimePair> _parseBalancingWindow(const BSONObj& balancingWindowObj); - - // Convention: (M)andatory, (O)ptional, (S)pecial rule. - - // (M) key determining the type of options to use - boost::optional<std::string> _key; - - // (S) size of the chunks in our cluster in MB - // Required if key is chunkSize - boost::optional<long long> _chunkSizeMB; - - // (O) is balancer enabled or disabled (default) - // Defaults to false. - boost::optional<bool> _balancerStopped; - - // (O) if present, balancerActiveWindow is an interval during the day - // when the balancer should be active. - // Stored as (<start time>, <stop time>) pair. strftime format is %H:%M - boost::optional<BoostTimePair> _balancerActiveWindow; - - // (O) only migrate chunks as fast as at least one secondary can keep up with - boost::optional<bool> _secondaryThrottle; - - // (O) detailed write concern for *individual* writes during migration. - // From side: deletes during cleanup. - // To side: deletes to clear the incoming range, deletes to undo migration at abort, - // and writes during cloning. - boost::optional<WriteConcernOptions> _migrationWriteConcern; - - // (O) synchronous migration cleanup. - boost::optional<bool> _waitForDelete; -}; - -} // namespace mongo diff --git a/src/mongo/s/catalog/type_settings_test.cpp b/src/mongo/s/catalog/type_settings_test.cpp deleted file mode 100644 index f5abfe88dfe..00000000000 --- a/src/mongo/s/catalog/type_settings_test.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/** - * Copyright (C) 2012 10gen Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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 - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * 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 GNU Affero General 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/s/catalog/type_settings.h" - -#include "mongo/base/status_with.h" -#include "mongo/db/jsobj.h" -#include "mongo/unittest/unittest.h" - -namespace { - -using namespace mongo; - -TEST(SettingsType, MissingKey) { - BSONObj objNoKey = BSONObj(); - StatusWith<SettingsType> result = SettingsType::fromBSON(objNoKey); - ASSERT_FALSE(result.isOK()); - ASSERT_EQUALS(result.getStatus(), ErrorCodes::NoSuchKey); -} - -TEST(SettingsType, ChunkSize) { - BSONObj objChunkSizeZero = - BSON(SettingsType::key(SettingsType::ChunkSizeDocKey) << SettingsType::chunkSizeMB(0)); - StatusWith<SettingsType> result = SettingsType::fromBSON(objChunkSizeZero); - ASSERT(result.isOK()); - SettingsType settings = result.getValue(); - ASSERT_EQUALS(settings.getChunkSizeMB(), 0); - Status validationStatus = settings.validate(); - ASSERT_FALSE(validationStatus.isOK()); - ASSERT_EQUALS(validationStatus, ErrorCodes::BadValue); -} - -TEST(SettingsType, UnsupportedSetting) { - BSONObj objBadSetting = BSON(SettingsType::key("badsetting")); - StatusWith<SettingsType> result = SettingsType::fromBSON(objBadSetting); - ASSERT(result.isOK()); - SettingsType settings = result.getValue(); - Status validationStatus = settings.validate(); - ASSERT_FALSE(validationStatus.isOK()); - ASSERT_EQUALS(validationStatus, ErrorCodes::UnsupportedFormat); -} - -TEST(SettingsType, InvalidBalancerWindow) { - BSONObj objBalancerBadKeys = BSON(SettingsType::key(SettingsType::BalancerDocKey) - << SettingsType::balancerActiveWindow(BSON("begin" - << "23:00" - << "end" - << "6:00"))); - StatusWith<SettingsType> result = SettingsType::fromBSON(objBalancerBadKeys); - ASSERT_FALSE(result.isOK()); - - BSONObj objBalancerBadTimes = BSON(SettingsType::key(SettingsType::BalancerDocKey) - << SettingsType::balancerActiveWindow(BSON("start" - << "23" - << "stop" - << "6"))); - result = SettingsType::fromBSON(objBalancerBadTimes); - ASSERT_FALSE(result.isOK()); -} - -TEST(SettingsType, ValidValues) { - BSONObj objChunkSize = - BSON(SettingsType::key(SettingsType::ChunkSizeDocKey) << SettingsType::chunkSizeMB(1)); - StatusWith<SettingsType> result = SettingsType::fromBSON(objChunkSize); - SettingsType settings = result.getValue(); - ASSERT(result.isOK()); - Status validationStatus = settings.validate(); - ASSERT(validationStatus.isOK()); - ASSERT_EQUALS(settings.getKey(), SettingsType::ChunkSizeDocKey); - ASSERT_EQUALS(settings.getChunkSizeMB(), 1); - - BSONObj objBalancer = BSON(SettingsType::key(SettingsType::BalancerDocKey) - << SettingsType::balancerStopped(true) - << SettingsType::balancerActiveWindow(BSON("start" - << "23:00" - << "stop" - << "6:00"))); - result = SettingsType::fromBSON(objBalancer); - settings = result.getValue(); - ASSERT(result.isOK()); - validationStatus = settings.validate(); - ASSERT(validationStatus.isOK()); - ASSERT_EQUALS(settings.getKey(), SettingsType::BalancerDocKey); - ASSERT_EQUALS(settings.getBalancerStopped(), true); -} - -TEST(SettingsType, BadType) { - BSONObj badTypeObj = BSON(SettingsType::key() << 0); - StatusWith<SettingsType> result = SettingsType::fromBSON(badTypeObj); - ASSERT_FALSE(result.isOK()); -} - -TEST(SettingsType, BalancingWindow) { - // T0 < T1 < now < T2 < T3 and Error - const std::string T0 = "9:00"; - const std::string T1 = "11:00"; - boost::posix_time::ptime now(currentDate(), - boost::posix_time::hours(13) + boost::posix_time::minutes(48)); - const std::string T2 = "17:00"; - const std::string T3 = "21:30"; - const std::string E = "28:35"; - - // closed in the past - BSONObj w1 = BSON(SettingsType::key(SettingsType::BalancerDocKey) - << SettingsType::balancerActiveWindow(BSON("start" << T0 << "stop" << T1))); - StatusWith<SettingsType> result = SettingsType::fromBSON(w1); - ASSERT(result.isOK()); - ASSERT_FALSE(result.getValue().inBalancingWindow(now)); - - // not opened until the future - BSONObj w2 = BSON(SettingsType::key(SettingsType::BalancerDocKey) - << SettingsType::balancerActiveWindow(BSON("start" << T2 << "stop" << T3))); - result = SettingsType::fromBSON(w2); - ASSERT(result.isOK()); - ASSERT_FALSE(result.getValue().inBalancingWindow(now)); - - // open now - BSONObj w3 = BSON(SettingsType::key(SettingsType::BalancerDocKey) - << SettingsType::balancerActiveWindow(BSON("start" << T1 << "stop" << T2))); - result = SettingsType::fromBSON(w3); - ASSERT(result.isOK()); - ASSERT(result.getValue().inBalancingWindow(now)); - - // open since last day - BSONObj w4 = BSON(SettingsType::key(SettingsType::BalancerDocKey) - << SettingsType::balancerActiveWindow(BSON("start" << T3 << "stop" << T2))); - result = SettingsType::fromBSON(w4); - ASSERT(result.isOK()); - ASSERT(result.getValue().inBalancingWindow(now)); - - // bad input should not stop the balancer - - // empty window - BSONObj w5 = BSON(SettingsType::key(SettingsType::BalancerDocKey)); - result = SettingsType::fromBSON(w5); - ASSERT(result.isOK()); - ASSERT(result.getValue().inBalancingWindow(now)); - - // missing stop - BSONObj w6 = BSON(SettingsType::key(SettingsType::BalancerDocKey) - << SettingsType::balancerActiveWindow(BSON("start" << 1))); - result = SettingsType::fromBSON(w6); - ASSERT_FALSE(result.isOK()); - - // missing start - BSONObj w7 = BSON(SettingsType::key(SettingsType::BalancerDocKey) - << SettingsType::balancerActiveWindow(BSON("stop" << 1))); - result = SettingsType::fromBSON(w7); - ASSERT_FALSE(result.isOK()); - - // active window marker missing - BSONObj w8 = BSON(SettingsType::key(SettingsType::BalancerDocKey) - << "wrongMarker" << BSON("start" << 1 << "stop" << 1)); - result = SettingsType::fromBSON(w8); - ASSERT(result.isOK()); - ASSERT(result.getValue().inBalancingWindow(now)); - - // garbage in window - BSONObj w9 = BSON(SettingsType::balancerActiveWindow(BSON("start" << T3 << "stop" << E))); - result = SettingsType::fromBSON(w9); - ASSERT_FALSE(result.isOK()); -} - -} // unnamed namespace diff --git a/src/mongo/s/mongos_options.cpp b/src/mongo/s/mongos_options.cpp index 96de91add0e..f0718d12f41 100644 --- a/src/mongo/s/mongos_options.cpp +++ b/src/mongo/s/mongos_options.cpp @@ -217,7 +217,7 @@ Status storeMongosOptions(const moe::Environment& params, const std::vector<std: const uint64_t maxChunkSizeBytes = maxChunkSizeMB * 1024 * 1024; - if (!BalancerConfiguration::checkMaxChunkSizeValid(maxChunkSizeBytes)) { + if (!ChunkSizeSettingsType::checkMaxChunkSizeValid(maxChunkSizeBytes)) { return Status(ErrorCodes::BadValue, "MaxChunkSize invalid"); } diff --git a/src/mongo/s/mongos_options.h b/src/mongo/s/mongos_options.h index e8eb1306b9c..b14c688e6a9 100644 --- a/src/mongo/s/mongos_options.h +++ b/src/mongo/s/mongos_options.h @@ -49,7 +49,7 @@ struct MongosGlobalParams { ConnectionString configdbs; // The max chunk size after which a chunk will be considered jumbo - uint64_t maxChunkSizeBytes{BalancerConfiguration::kDefaultMaxChunkSizeBytes}; + uint64_t maxChunkSizeBytes{ChunkSizeSettingsType::kDefaultMaxChunkSizeBytes}; // Whether auto-splitting is enabled bool shouldAutoSplit{true}; diff --git a/src/mongo/s/sharding_initialization.cpp b/src/mongo/s/sharding_initialization.cpp index 17646ac1adb..1be0d0ae45d 100644 --- a/src/mongo/s/sharding_initialization.cpp +++ b/src/mongo/s/sharding_initialization.cpp @@ -212,7 +212,7 @@ Status initializeGlobalShardingStateForMongos(OperationContext* txn, Status initializeGlobalShardingStateForMongod(OperationContext* txn, const ConnectionString& configCS) { return initializeGlobalShardingState( - txn, configCS, BalancerConfiguration::kDefaultMaxChunkSizeBytes, false); + txn, configCS, ChunkSizeSettingsType::kDefaultMaxChunkSizeBytes, false); } } // namespace mongo diff --git a/src/mongo/s/sharding_test_fixture.cpp b/src/mongo/s/sharding_test_fixture.cpp index 886478cc03d..737a996e113 100644 --- a/src/mongo/s/sharding_test_fixture.cpp +++ b/src/mongo/s/sharding_test_fixture.cpp @@ -142,7 +142,7 @@ void ShardingTestFixture::setUp() { stdx::make_unique<CatalogCache>(), std::move(shardRegistry), stdx::make_unique<ClusterCursorManager>(_service->getPreciseClockSource()), - stdx::make_unique<BalancerConfiguration>(BalancerConfiguration::kDefaultMaxChunkSizeBytes), + stdx::make_unique<BalancerConfiguration>(ChunkSizeSettingsType::kDefaultMaxChunkSizeBytes), std::move(executorPool), _mockNetwork); } |