diff options
| author | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2016-09-30 11:58:59 -0400 |
|---|---|---|
| committer | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2016-10-05 07:52:13 -0400 |
| commit | 92bdb7b398f29bcaef88da18c7a2df03f07e77ca (patch) | |
| tree | 8fc3857f6c0f324529056df42dfcf00883eb3cff /src/mongo/s/balancer_configuration.cpp | |
| parent | e8345ff602fc528dbc33944b21bd344cbea5e3e5 (diff) | |
| download | mongo-92bdb7b398f29bcaef88da18c7a2df03f07e77ca.tar.gz | |
SERVER-26411 Move balancer sources under mongo/db/s
Diffstat (limited to 'src/mongo/s/balancer_configuration.cpp')
| -rw-r--r-- | src/mongo/s/balancer_configuration.cpp | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/src/mongo/s/balancer_configuration.cpp b/src/mongo/s/balancer_configuration.cpp new file mode 100644 index 00000000000..79098a2ec4a --- /dev/null +++ b/src/mongo/s/balancer_configuration.cpp @@ -0,0 +1,410 @@ +/** + * 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. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kSharding + +#include "mongo/platform/basic.h" + +#include "mongo/s/balancer_configuration.h" + +#include <algorithm> + +#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/sharding_catalog_client.h" +#include "mongo/s/grid.h" +#include "mongo/util/log.h" +#include "mongo/util/mongoutils/str.h" + +namespace mongo { +namespace { + +const char kValue[] = "value"; +const char kEnabled[] = "enabled"; +const char kStopped[] = "stopped"; +const char kMode[] = "mode"; +const char kActiveWindow[] = "activeWindow"; +const char kWaitForDelete[] = "_waitForDelete"; + +const NamespaceString kSettingsNamespace("config", "settings"); + +} // namespace + +const char BalancerSettingsType::kKey[] = "balancer"; +const char* BalancerSettingsType::kBalancerModes[] = {"full", "autoSplitOnly", "off"}; + +const char ChunkSizeSettingsType::kKey[] = "chunksize"; +const uint64_t ChunkSizeSettingsType::kDefaultMaxChunkSizeBytes{64 * 1024 * 1024}; + +const char AutoSplitSettingsType::kKey[] = "autosplit"; + +BalancerConfiguration::BalancerConfiguration() + : _balancerSettings(BalancerSettingsType::createDefault()), + _maxChunkSizeBytes(ChunkSizeSettingsType::kDefaultMaxChunkSizeBytes), + _shouldAutoSplit(true) {} + +BalancerConfiguration::~BalancerConfiguration() = default; + +BalancerSettingsType::BalancerMode BalancerConfiguration::getBalancerMode() const { + stdx::lock_guard<stdx::mutex> lk(_balancerSettingsMutex); + return _balancerSettings.getMode(); +} + +Status BalancerConfiguration::setBalancerMode(OperationContext* txn, + BalancerSettingsType::BalancerMode mode) { + auto updateStatus = Grid::get(txn)->catalogClient(txn)->updateConfigDocument( + txn, + kSettingsNamespace.ns(), + BSON("_id" << BalancerSettingsType::kKey), + BSON("$set" << BSON(kStopped << (mode == BalancerSettingsType::kOff) << kMode + << BalancerSettingsType::kBalancerModes[mode])), + true, + ShardingCatalogClient::kMajorityWriteConcern); + + Status refreshStatus = refreshAndCheck(txn); + if (!refreshStatus.isOK()) { + return refreshStatus; + } + + if (!updateStatus.isOK() && (getBalancerMode() != mode)) { + return {updateStatus.getStatus().code(), + str::stream() << "Failed to update balancer configuration due to " + << updateStatus.getStatus().reason()}; + } + + return Status::OK(); +} + +bool BalancerConfiguration::shouldBalance() const { + stdx::lock_guard<stdx::mutex> lk(_balancerSettingsMutex); + if (_balancerSettings.getMode() == BalancerSettingsType::kOff || + _balancerSettings.getMode() == BalancerSettingsType::kAutoSplitOnly) { + return false; + } + + return _balancerSettings.isTimeInBalancingWindow(boost::posix_time::second_clock::local_time()); +} + +bool BalancerConfiguration::shouldBalanceForAutoSplit() const { + stdx::lock_guard<stdx::mutex> lk(_balancerSettingsMutex); + if (_balancerSettings.getMode() == BalancerSettingsType::kOff) { + return false; + } + + return _balancerSettings.isTimeInBalancingWindow(boost::posix_time::second_clock::local_time()); +} + +MigrationSecondaryThrottleOptions BalancerConfiguration::getSecondaryThrottle() const { + stdx::lock_guard<stdx::mutex> lk(_balancerSettingsMutex); + return _balancerSettings.getSecondaryThrottle(); +} + +bool BalancerConfiguration::waitForDelete() const { + stdx::lock_guard<stdx::mutex> lk(_balancerSettingsMutex); + return _balancerSettings.waitForDelete(); +} + +Status BalancerConfiguration::refreshAndCheck(OperationContext* txn) { + // Balancer configuration + Status balancerSettingsStatus = _refreshBalancerSettings(txn); + if (!balancerSettingsStatus.isOK()) { + return {balancerSettingsStatus.code(), + str::stream() << "Failed to refresh the balancer settings due to " + << balancerSettingsStatus.toString()}; + } + + // Chunk size settings + Status chunkSizeStatus = _refreshChunkSizeSettings(txn); + if (!chunkSizeStatus.isOK()) { + return {chunkSizeStatus.code(), + str::stream() << "Failed to refresh the chunk sizes settings due to " + << chunkSizeStatus.toString()}; + } + + // AutoSplit settings + Status autoSplitStatus = _refreshAutoSplitSettings(txn); + if (!autoSplitStatus.isOK()) { + return {autoSplitStatus.code(), + str::stream() << "Failed to refresh the autoSplit settings due to " + << autoSplitStatus.toString()}; + } + + return Status::OK(); +} + +Status BalancerConfiguration::_refreshBalancerSettings(OperationContext* txn) { + BalancerSettingsType settings = BalancerSettingsType::createDefault(); + + auto settingsObjStatus = + Grid::get(txn)->catalogClient(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(); + } + + stdx::lock_guard<stdx::mutex> lk(_balancerSettingsMutex); + _balancerSettings = std::move(settings); + + return Status::OK(); +} + +Status BalancerConfiguration::_refreshChunkSizeSettings(OperationContext* txn) { + ChunkSizeSettingsType settings = ChunkSizeSettingsType::createDefault(); + + auto settingsObjStatus = + grid.catalogClient(txn)->getGlobalSettings(txn, ChunkSizeSettingsType::kKey); + if (settingsObjStatus.isOK()) { + auto settingsStatus = ChunkSizeSettingsType::fromBSON(settingsObjStatus.getValue()); + if (!settingsStatus.isOK()) { + return settingsStatus.getStatus(); + } + + settings = std::move(settingsStatus.getValue()); + } else if (settingsObjStatus != ErrorCodes::NoMatchingDocument) { + return settingsObjStatus.getStatus(); + } + + if (settings.getMaxChunkSizeBytes() != getMaxChunkSizeBytes()) { + log() << "MaxChunkSize changing from " << getMaxChunkSizeBytes() / (1024 * 1024) << "MB" + << " to " << settings.getMaxChunkSizeBytes() / (1024 * 1024) << "MB"; + + _maxChunkSizeBytes.store(settings.getMaxChunkSizeBytes()); + } + + return Status::OK(); +} + +Status BalancerConfiguration::_refreshAutoSplitSettings(OperationContext* txn) { + AutoSplitSettingsType settings = AutoSplitSettingsType::createDefault(); + + auto settingsObjStatus = + grid.catalogClient(txn)->getGlobalSettings(txn, AutoSplitSettingsType::kKey); + if (settingsObjStatus.isOK()) { + auto settingsStatus = AutoSplitSettingsType::fromBSON(settingsObjStatus.getValue()); + if (!settingsStatus.isOK()) { + return settingsStatus.getStatus(); + } + + settings = std::move(settingsStatus.getValue()); + } else if (settingsObjStatus != ErrorCodes::NoMatchingDocument) { + return settingsObjStatus.getStatus(); + } + + if (settings.getShouldAutoSplit() != getShouldAutoSplit()) { + log() << "ShouldAutoSplit changing from " << getShouldAutoSplit() << " to " + << settings.getShouldAutoSplit(); + + _shouldAutoSplit.store(settings.getShouldAutoSplit()); + } + + return Status::OK(); +} + +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; + if (stopped) { + settings._mode = kOff; + } else { + std::string modeStr; + status = bsonExtractStringFieldWithDefault(obj, kMode, kBalancerModes[kFull], &modeStr); + if (!status.isOK()) + return status; + auto it = std::find(std::begin(kBalancerModes), std::end(kBalancerModes), modeStr); + if (it == std::end(kBalancerModes)) { + return Status(ErrorCodes::BadValue, "Invalid balancer mode"); + } + + settings._mode = static_cast<BalancerMode>(it - std::begin(kBalancerModes)); + } + } + + { + 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; + } + } + + { + 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; + } + + return settings; +} + +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; + } + } else { + MONGO_UNREACHABLE; + } + + return false; +} + +ChunkSizeSettingsType::ChunkSizeSettingsType() = default; + +ChunkSizeSettingsType ChunkSizeSettingsType::createDefault() { + return ChunkSizeSettingsType(); +} + +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() << maxChunkSizeMB << " is not a valid value for " << kKey}; + } + + ChunkSizeSettingsType settings; + settings._maxChunkSizeBytes = maxChunkSizeBytes; + + return settings; +} + +bool ChunkSizeSettingsType::checkMaxChunkSizeValid(uint64_t maxChunkSizeBytes) { + if (maxChunkSizeBytes >= (1024 * 1024) && maxChunkSizeBytes <= (1024 * 1024 * 1024)) { + return true; + } + + return false; +} + +AutoSplitSettingsType::AutoSplitSettingsType() = default; + +AutoSplitSettingsType AutoSplitSettingsType::createDefault() { + return AutoSplitSettingsType(); +} + +StatusWith<AutoSplitSettingsType> AutoSplitSettingsType::fromBSON(const BSONObj& obj) { + bool shouldAutoSplit; + Status status = bsonExtractBooleanField(obj, kEnabled, &shouldAutoSplit); + if (!status.isOK()) + return status; + + AutoSplitSettingsType settings; + settings._shouldAutoSplit = shouldAutoSplit; + + return settings; +} + +} // namespace mongo |
