diff options
author | Daniel Alabi <alabidan@gmail.com> | 2015-04-20 00:28:50 -0400 |
---|---|---|
committer | Daniel Alabi <alabidan@gmail.com> | 2015-04-30 10:22:52 -0400 |
commit | 30c9fa8ef46c1c6a0951a0d8ab724991e5cc04e2 (patch) | |
tree | 4da2d1012ac53da70c171aa0a291c01fb60b9a66 /src | |
parent | c269033226874638626b6c028bf0f81fa0af2006 (diff) | |
download | mongo-30c9fa8ef46c1c6a0951a0d8ab724991e5cc04e2.tar.gz |
SERVER-18156 SettingsType should do all validation and default value determination.
Removed s/balancing_window_test.cpp.
Added balancing window tests to type_settings_test.
Moved balancing window checking from Grid to SettingsType.
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/dbtests/config_upgrade_tests.cpp | 5 | ||||
-rw-r--r-- | src/mongo/s/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/s/balance.cpp | 28 | ||||
-rw-r--r-- | src/mongo/s/balancing_window_test.cpp | 94 | ||||
-rw-r--r-- | src/mongo/s/catalog/catalog_manager.h | 4 | ||||
-rw-r--r-- | src/mongo/s/catalog/legacy/catalog_manager_legacy.cpp | 26 | ||||
-rw-r--r-- | src/mongo/s/catalog/legacy/config_upgrade.cpp | 22 | ||||
-rw-r--r-- | src/mongo/s/catalog/type_settings.cpp | 387 | ||||
-rw-r--r-- | src/mongo/s/catalog/type_settings.h | 287 | ||||
-rw-r--r-- | src/mongo/s/catalog/type_settings_test.cpp | 225 | ||||
-rw-r--r-- | src/mongo/s/chunk.cpp | 7 | ||||
-rw-r--r-- | src/mongo/s/config.cpp | 40 | ||||
-rw-r--r-- | src/mongo/s/grid.cpp | 52 | ||||
-rw-r--r-- | src/mongo/s/grid.h | 10 |
14 files changed, 505 insertions, 683 deletions
diff --git a/src/mongo/dbtests/config_upgrade_tests.cpp b/src/mongo/dbtests/config_upgrade_tests.cpp index 17ad4a25331..e76760d283f 100644 --- a/src/mongo/dbtests/config_upgrade_tests.cpp +++ b/src/mongo/dbtests/config_upgrade_tests.cpp @@ -57,8 +57,9 @@ namespace mongo { // DBDirectClient DBDirectClient client(&_txn); client.update(SettingsType::ConfigNS, - BSON(SettingsType::key("balancer")), - BSON(SettingsType::key("balancer") << SettingsType::balancerStopped(true)), + BSON(SettingsType::key(SettingsType::BalancerDocKey)), + BSON(SettingsType::key(SettingsType::BalancerDocKey) << + SettingsType::balancerStopped(true)), true, false); } diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript index 1301e20fe3e..975b034e617 100644 --- a/src/mongo/s/SConscript +++ b/src/mongo/s/SConscript @@ -295,7 +295,6 @@ env.CppUnitTest( target='mongoscore_test', source=[ 'balancer_policy_tests.cpp', - 'balancing_window_test.cpp', 'shard_key_pattern_test.cpp', ], LIBDEPS=[ diff --git a/src/mongo/s/balance.cpp b/src/mongo/s/balance.cpp index f0dd06271da..7dc7ad80e6b 100644 --- a/src/mongo/s/balance.cpp +++ b/src/mongo/s/balance.cpp @@ -100,15 +100,15 @@ namespace mongo { // chunks moves. auto balSettingsResult = grid.catalogManager()->getGlobalSettings(SettingsType::BalancerDocKey); - if (!balSettingsResult.isOK()) { + const bool isBalSettingsAbsent = balSettingsResult.getStatus() == ErrorCodes::NoSuchKey; + if (!balSettingsResult.isOK() && !isBalSettingsAbsent) { warning() << balSettingsResult.getStatus(); return movedCount; } const SettingsType& balancerConfig = balSettingsResult.getValue(); - if ((balancerConfig.isKeySet() && // balancer config doc exists - !grid.shouldBalance(balancerConfig)) || - MONGO_FAIL_POINT(skipBalanceRound)) { + if ((!isBalSettingsAbsent && !grid.shouldBalance(balancerConfig)) || + MONGO_FAIL_POINT(skipBalanceRound)) { LOG(1) << "Stopping balancing round early as balancing was disabled"; return movedCount; } @@ -540,16 +540,17 @@ namespace mongo { auto balSettingsResult = grid.catalogManager()->getGlobalSettings(SettingsType::BalancerDocKey); - if (!balSettingsResult.isOK()) { + const bool isBalSettingsAbsent = + balSettingsResult.getStatus() == ErrorCodes::NoSuchKey; + if (!balSettingsResult.isOK() && !isBalSettingsAbsent) { warning() << balSettingsResult.getStatus(); return; } const SettingsType& balancerConfig = balSettingsResult.getValue(); // now make sure we should even be running - if ((balancerConfig.isKeySet() && // balancer config doc exists - !grid.shouldBalance(balancerConfig)) || - MONGO_FAIL_POINT(skipBalanceRound)) { + if ((!isBalSettingsAbsent && !grid.shouldBalance(balancerConfig)) || + MONGO_FAIL_POINT(skipBalanceRound)) { LOG(1) << "skipping balancing round because balancing is disabled" << endl; @@ -584,16 +585,9 @@ namespace mongo { const bool waitForDelete = (balancerConfig.isWaitForDeleteSet() ? balancerConfig.getWaitForDelete() : false); - scoped_ptr<WriteConcernOptions> writeConcern; + std::unique_ptr<WriteConcernOptions> writeConcern; if (balancerConfig.isKeySet()) { // if balancer doc exists. - StatusWith<WriteConcernOptions*> extractStatus = - balancerConfig.extractWriteConcern(); - if (extractStatus.isOK()) { - writeConcern.reset(extractStatus.getValue()); - } - else { - warning() << extractStatus.getStatus().toString(); - } + writeConcern = std::move(balancerConfig.getWriteConcern()); } LOG(1) << "*** start balancing round. " diff --git a/src/mongo/s/balancing_window_test.cpp b/src/mongo/s/balancing_window_test.cpp deleted file mode 100644 index f2390a6527a..00000000000 --- a/src/mongo/s/balancing_window_test.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* Copyright 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::kDefault - -#include "mongo/s/catalog/type_settings.h" -#include "mongo/s/grid.h" -#include "mongo/unittest/unittest.h" -#include "mongo/util/log.h" - -namespace mongo { - - using std::string; - -namespace { - - TEST(BalancingWindow, Test) { - // T0 < T1 < now < T2 < T3 and Error - const string T0 = "9:00"; - const string T1 = "11:00"; - boost::posix_time::ptime now(currentDate(), - boost::posix_time::hours(13) + - boost::posix_time::minutes(48)); - const string T2 = "17:00"; - const string T3 = "21:30"; - const string E = "28:35"; - - // closed in the past - BSONObj w1 = BSON(SettingsType::balancerActiveWindow(BSON("start" << T0 << "stop" << T1))); - - // not opened until the future - BSONObj w2 = BSON(SettingsType::balancerActiveWindow(BSON("start" << T2 << "stop" << T3))); - - // open now - BSONObj w3 = BSON(SettingsType::balancerActiveWindow(BSON("start" << T1 << "stop" << T2))); - - // open since last day - BSONObj w4 = BSON(SettingsType::balancerActiveWindow(BSON("start" << T3 << "stop" << T2))); - - ASSERT(!Grid::_inBalancingWindow(w1, now)); - ASSERT(!Grid::_inBalancingWindow(w2, now)); - ASSERT(Grid::_inBalancingWindow(w3, now)); - ASSERT(Grid::_inBalancingWindow(w4, now)); - - // bad input should not stop the balancer - - // empty window - BSONObj w5; - - // missing stop - BSONObj w6 = BSON(SettingsType::balancerActiveWindow(BSON("start" << 1))); - - // missing start - BSONObj w7 = BSON(SettingsType::balancerActiveWindow(BSON("stop" << 1))); - - // active window marker missing - BSONObj w8 = BSON("wrongMarker" << 1 << "start" << 1 << "stop" << 1); - - // garbage in window - BSONObj w9 = BSON(SettingsType::balancerActiveWindow(BSON("start" << T3 << "stop" << E))); - - ASSERT(Grid::_inBalancingWindow(w5, now)); - ASSERT(Grid::_inBalancingWindow(w6, now)); - ASSERT(Grid::_inBalancingWindow(w7, now)); - ASSERT(Grid::_inBalancingWindow(w8, now)); - ASSERT(Grid::_inBalancingWindow(w9, now)); - } - -} // namespace -} // namespace mongo diff --git a/src/mongo/s/catalog/catalog_manager.h b/src/mongo/s/catalog/catalog_manager.h index 4c368cc1f24..21d2bc5de2d 100644 --- a/src/mongo/s/catalog/catalog_manager.h +++ b/src/mongo/s/catalog/catalog_manager.h @@ -277,9 +277,7 @@ namespace mongo { * Returns global settings for a certain key. * @param key: key for SettingsType::ConfigNS document. * - * NOTE: If no document with such a key exists, an empty SettingsType object will - * will be returned. It is up to the caller to check if the SettingsType - * is non-empty (via the keySet() method on the SettingsType). + * Returns NoSuchKey if no SettingsType::ConfigNS document with such key exists. */ virtual StatusWith<SettingsType> getGlobalSettings(const std::string& key) = 0; diff --git a/src/mongo/s/catalog/legacy/catalog_manager_legacy.cpp b/src/mongo/s/catalog/legacy/catalog_manager_legacy.cpp index 5af37bb70b7..e5e693ffb3c 100644 --- a/src/mongo/s/catalog/legacy/catalog_manager_legacy.cpp +++ b/src/mongo/s/catalog/legacy/catalog_manager_legacy.cpp @@ -1095,29 +1095,29 @@ namespace { } StatusWith<SettingsType> CatalogManagerLegacy::getGlobalSettings(const string& key) { - SettingsType settingsType; - try { ScopedDbConnection conn(_configServerConnectionString, 30); BSONObj settingsDoc = conn->findOne(SettingsType::ConfigNS, BSON(SettingsType::key(key))); + StatusWith<SettingsType> settingsResult = SettingsType::fromBSON(settingsDoc); + conn.done(); - string errMsg; - if (!settingsType.parseBSON(settingsDoc, &errMsg)) { - conn.done(); - return Status(ErrorCodes::UnsupportedFormat, - str::stream() << "error parsing config.settings document: " - << errMsg); + if (!settingsResult.isOK()) { + return settingsResult.getStatus(); } - conn.done(); + const SettingsType& settings = settingsResult.getValue(); + Status validationStatus = settings.validate(); + if (!validationStatus.isOK()) { + return validationStatus; + } + + return settingsResult; } catch (const DBException& ex) { return Status(ErrorCodes::OperationFailed, - str::stream() << "unable to successfully obtain config.settings document: " - << causedBy(ex)); + str::stream() << "unable to successfully obtain " + << "config.settings document: " << causedBy(ex)); } - - return settingsType; } void CatalogManagerLegacy::getDatabasesForShard(const string& shardName, diff --git a/src/mongo/s/catalog/legacy/config_upgrade.cpp b/src/mongo/s/catalog/legacy/config_upgrade.cpp index d4f34ae64de..505e2c6e799 100644 --- a/src/mongo/s/catalog/legacy/config_upgrade.cpp +++ b/src/mongo/s/catalog/legacy/config_upgrade.cpp @@ -303,22 +303,14 @@ namespace mongo { } // Returns true if we can confirm the balancer is stopped - bool _isBalancerStopped(const ConnectionString& configLoc, string* errMsg) { - - // Get the balancer information - BSONObj balancerDoc; - try { - ScopedDbConnection conn(configLoc, 30); - balancerDoc = conn->findOne(SettingsType::ConfigNS, - BSON(SettingsType::key("balancer"))); - conn.done(); - } - catch (const DBException& e) { - *errMsg = e.toString(); + bool _isBalancerStopped() { + auto balSettingsResult = + grid.catalogManager()->getGlobalSettings(SettingsType::BalancerDocKey); + if (!balSettingsResult.isOK()) { return false; } - - return balancerDoc[SettingsType::balancerStopped()].trueValue(); + SettingsType balSettings = balSettingsResult.getValue(); + return balSettings.getBalancerStopped(); } // Checks that all config servers are online @@ -479,7 +471,7 @@ namespace mongo { // Check whether or not the balancer is online, if it is online we will not upgrade // (but we will initialize the config server) - if (!isEmptyVersion && !_isBalancerStopped(configLoc, errMsg)) { + if (!isEmptyVersion && !_isBalancerStopped()) { *errMsg = stream() << "balancer must be stopped for config upgrade" << causedBy(errMsg); diff --git a/src/mongo/s/catalog/type_settings.cpp b/src/mongo/s/catalog/type_settings.cpp index 1544c9ce056..ebe757bb1e9 100644 --- a/src/mongo/s/catalog/type_settings.cpp +++ b/src/mongo/s/catalog/type_settings.cpp @@ -25,241 +25,282 @@ * 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 "mongo/db/field_parser.h" -#include "mongo/db/write_concern_options.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 { - using std::auto_ptr; - using std::string; - - using mongoutils::str::stream; - 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<int> SettingsType::chunksize("value"); + const BSONField<long long> SettingsType::chunkSize("value"); const BSONField<bool> SettingsType::balancerStopped("stopped"); const BSONField<BSONObj> SettingsType::balancerActiveWindow("activeWindow"); - const BSONField<bool> SettingsType::deprecated_secondaryThrottle("_secondaryThrottle", true); + const BSONField<bool> SettingsType::deprecated_secondaryThrottle("_secondaryThrottle"); const BSONField<BSONObj> SettingsType::migrationWriteConcern("_secondaryThrottle"); const BSONField<bool> SettingsType::waitForDelete("_waitForDelete"); - SettingsType::SettingsType() { - clear(); - } + StatusWith<SettingsType> SettingsType::fromBSON(const BSONObj& source) { + SettingsType settings; - SettingsType::~SettingsType() { - } - - bool SettingsType::isValid(std::string* errMsg) const { - std::string dummy; - if (errMsg == NULL) { - errMsg = &dummy; + { + std::string settingsKey; + Status status = bsonExtractStringField(source, key.name(), &settingsKey); + if (!status.isOK()) return status; + settings._key = settingsKey; } - // All the mandatory fields must be present. - if (!_isKeySet) { - *errMsg = stream() << "missing " << key.name() << " field"; - return false; + if (settings._key == ChunkSizeDocKey) { + long long settingsChunkSize; + Status status = bsonExtractIntegerField(source, + chunkSize.name(), + &settingsChunkSize); + if (!status.isOK()) return status; + settings._chunkSize = settingsChunkSize; } + else if (settings._key == BalancerDocKey) { + { + bool settingsBalancerStopped; + Status status = bsonExtractBooleanFieldWithDefault(source, + balancerStopped.name(), + false, + &settingsBalancerStopped); + if (!status.isOK()) return status; + settings._balancerStopped = settingsBalancerStopped; + } - if (_key == ChunkSizeDocKey) { - if (_isChunksizeSet) { - if (!(_chunksize > 0)) { - *errMsg = stream() << "chunksize specified in " << chunksize.name() << - " field must be greater than zero"; - return false; + { + 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(); } - } else { - *errMsg = stream() << "chunksize must be specified in " << chunksize.name() << - " field for chunksize setting"; - return false; } - return true; - } - else if (_key == BalancerDocKey) { - if (_balancerActiveWindow.nFields() != 0) { - // check if both 'start' and 'stop' are present - const std::string start = _balancerActiveWindow["start"].str(); - const std::string stop = _balancerActiveWindow["stop"].str(); - if ( start.empty() || stop.empty() ) { - *errMsg = stream() << balancerActiveWindow.name() << - " format is { start: \"hh:mm\" , stop: \"hh:mm\" }"; - return false; - } - // check that both 'start' and 'stop' are valid time-of-day - boost::posix_time::ptime startTime, stopTime; - if ( !toPointInTime( start , &startTime ) || !toPointInTime( stop , &stopTime ) ) { - *errMsg = stream() << balancerActiveWindow.name() << - " format is { start: \"hh:mm\" , stop: \"hh:mm\" }"; - return false; + { + 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; + } + } - if (_isSecondaryThrottleSet && _isMigrationWriteConcernSet) { - *errMsg = stream() << "cannot have both secondary throttle and migration " - << "write concern set at the same time"; - return false; + { + bool settingsWaitForDelete; + Status status = bsonExtractBooleanField(source, + waitForDelete.name(), + &settingsWaitForDelete); + if (status != ErrorCodes::NoSuchKey) { + if (!status.isOK()) return status; + settings._waitForDelete = settingsWaitForDelete; } } - return true; + } + + 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 (!(getChunkSize() > 0)) { + return Status(ErrorCodes::BadValue, + str::stream() << "chunksize specified in " << chunkSize.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 { - *errMsg = stream() << "unsupported key in " << key.name() << " field"; - return false; + return Status(ErrorCodes::UnsupportedFormat, + str::stream() << "unsupported key in " << key.name() << " field"); } + + return Status::OK(); } BSONObj SettingsType::toBSON() const { BSONObjBuilder builder; - if (_isKeySet) builder.append(key(), _key); - if (_isChunksizeSet) builder.append(chunksize(), _chunksize); - if (_isBalancerStoppedSet) builder.append(balancerStopped(), _balancerStopped); - if (_isBalancerActiveWindowSet) { - builder.append(balancerActiveWindow(), _balancerActiveWindow); + if (_key) builder.append(key(), getKey()); + if (_chunkSize) builder.append(chunkSize(), getChunkSize()); + if (_balancerStopped) builder.append(balancerStopped(), getBalancerStopped()); + if (_secondaryThrottle) { + builder.append(deprecated_secondaryThrottle(), getSecondaryThrottle()); } - if (_isSecondaryThrottleSet) { - builder.append(deprecated_secondaryThrottle(), _secondaryThrottle); + if (_migrationWriteConcern) { + builder.append(migrationWriteConcern(), getMigrationWriteConcern().toBSON()); } + if (_waitForDelete) builder.append(waitForDelete(), getWaitForDelete()); - if (_isMigrationWriteConcernSet) { - builder.append(migrationWriteConcern(), _migrationWriteConcern); - } return builder.obj(); } - bool SettingsType::parseBSON(const BSONObj& source, string* errMsg) { - clear(); - - std::string dummy; - if (!errMsg) errMsg = &dummy; - - FieldParser::FieldState fieldState; - fieldState = FieldParser::extract(source, key, &_key, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isKeySet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, chunksize, &_chunksize, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isChunksizeSet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, balancerStopped, &_balancerStopped, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isBalancerStoppedSet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, balancerActiveWindow, - &_balancerActiveWindow, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isBalancerActiveWindowSet = fieldState == FieldParser::FIELD_SET; - - fieldState = FieldParser::extract(source, - migrationWriteConcern, - &_migrationWriteConcern, - errMsg); - _isMigrationWriteConcernSet = fieldState == FieldParser::FIELD_SET; - - if (fieldState == FieldParser::FIELD_INVALID) { - fieldState = FieldParser::extract(source, deprecated_secondaryThrottle, - &_secondaryThrottle, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - errMsg->clear(); // Note: extract method doesn't clear errMsg. - _isSecondaryThrottleSet = fieldState == FieldParser::FIELD_SET; - } - - fieldState = FieldParser::extract(source, waitForDelete, &_waitForDelete, errMsg); - if (fieldState == FieldParser::FIELD_INVALID) return false; - _isWaitForDeleteSet = fieldState == FieldParser::FIELD_SET; - - return true; + std::string SettingsType::toString() const { + return toBSON().toString(); } - void SettingsType::clear() { - - _key.clear(); - _isKeySet = false; - - _chunksize = 0; - _isChunksizeSet = false; - - _balancerStopped = false; - _isBalancerStoppedSet = false; - - _balancerActiveWindow = BSONObj(); - _isBalancerActiveWindowSet = false; - - _secondaryThrottle = false; - _isSecondaryThrottleSet = false; - - _migrationWriteConcern = BSONObj(); - _isMigrationWriteConcernSet= false; + std::unique_ptr<WriteConcernOptions> SettingsType::getWriteConcern() const { + dassert(_key.is_initialized()); + dassert(_key == BalancerDocKey); - _waitForDelete = false; - _isWaitForDeleteSet = false; + if (isSecondaryThrottleSet() && !getSecondaryThrottle()) { + return stdx::make_unique<WriteConcernOptions>(1, WriteConcernOptions::NONE, 0); + } + else if (!isMigrationWriteConcernSet()) { + // Default setting. + return nullptr; + } + else { + return stdx::make_unique<WriteConcernOptions>(getMigrationWriteConcern()); + } } - void SettingsType::cloneTo(SettingsType* other) const { - other->clear(); - - other->_key = _key; - other->_isKeySet = _isKeySet; - - other->_chunksize = _chunksize; - other->_isChunksizeSet = _isChunksizeSet; + StatusWith<BoostTimePair> SettingsType::_parseBalancingWindow(const BSONObj& balancingWindowObj) { + if (balancingWindowObj.isEmpty()) { + return Status(ErrorCodes::BadValue, + "'activeWindow' can't be empty"); + } - other->_balancerStopped = _balancerStopped; - other->_isBalancerStoppedSet = _isBalancerStoppedSet; + // 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); + } - other->_balancerActiveWindow = _balancerActiveWindow.copy(); - other->_isBalancerActiveWindowSet = _isBalancerActiveWindowSet; + // 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\" }"); + } - other->_secondaryThrottle = _secondaryThrottle; - other->_isSecondaryThrottleSet = _isSecondaryThrottleSet; + return std::make_pair(startTime, stopTime); + } - other->_migrationWriteConcern = _migrationWriteConcern.copy(); - other->_isMigrationWriteConcernSet = _isMigrationWriteConcernSet; + 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; + } + } - other->_waitForDelete = _waitForDelete; - other->_isWaitForDeleteSet = _isWaitForDeleteSet; + return false; } - std::string SettingsType::toString() const { - return toBSON().toString(); + void SettingsType::setKey(const std::string& key) { + invariant(!key.empty()); + _key = key; } - StatusWith<WriteConcernOptions*> SettingsType::extractWriteConcern() const { - dassert(_isKeySet); - dassert(_key == BalancerDocKey); - - const bool isSecondaryThrottle = getSecondaryThrottle(); - if (!isSecondaryThrottle) { - return(StatusWith<WriteConcernOptions*>( - new WriteConcernOptions(1, WriteConcernOptions::NONE, 0))); - } + void SettingsType::setChunkSize(const long long chunkSize) { + invariant(_key == ChunkSizeDocKey); + invariant(chunkSize > 0); + _chunkSize = chunkSize; + } - const BSONObj migrationWOption(isMigrationWriteConcernSet() ? - getMigrationWriteConcern() : BSONObj()); + void SettingsType::setBalancerStopped(const bool balancerStopped) { + invariant(_key == BalancerDocKey); + _balancerStopped = balancerStopped; + } - if (migrationWOption.isEmpty()) { - // Default setting. - return StatusWith<WriteConcernOptions*>(NULL); - } + void SettingsType::setBalancerActiveWindow(const BSONObj& balancerActiveWindow) { + invariant(_key == BalancerDocKey); + StatusWith<BoostTimePair> timePairResult = _parseBalancingWindow(balancerActiveWindow); + invariant(timePairResult.isOK()); + _balancerActiveWindow = timePairResult.getValue(); + } - auto_ptr<WriteConcernOptions> writeConcern(new WriteConcernOptions()); - Status status = writeConcern->parse(migrationWOption); + void SettingsType::setSecondaryThrottle(const bool secondaryThrottle) { + invariant(_key == BalancerDocKey); + _secondaryThrottle = secondaryThrottle; + } - if (!status.isOK()) { - return StatusWith<WriteConcernOptions*>(status); - } + void SettingsType::setMigrationWriteConcern(const BSONObj& migrationWCBSONObj) { + invariant(_key == BalancerDocKey); + invariant(!migrationWCBSONObj.isEmpty()); + Status status = _migrationWriteConcern->parse(migrationWCBSONObj); + invariant(status.isOK()); + } - return StatusWith<WriteConcernOptions*>(writeConcern.release()); + 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 index 11bd68c584e..c14cdd2c1c0 100644 --- a/src/mongo/s/catalog/type_settings.h +++ b/src/mongo/s/catalog/type_settings.h @@ -28,16 +28,21 @@ #pragma once +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/optional.hpp> #include <string> +#include <utility> -#include "mongo/base/disallow_copying.h" -#include "mongo/base/status_with.h" -#include "mongo/base/string_data.h" -#include "mongo/db/jsobj.h" +#include "mongo/bson/bson_field.h" +#include "mongo/db/write_concern_options.h" namespace mongo { struct WriteConcernOptions; + 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 @@ -48,55 +53,42 @@ namespace mongo { * * // Contact the config. 'conn' has been obtained before. * DBClientBase* conn; - * BSONObj query = QUERY(SettingsType::exampleField("exampleFieldName")); + * BSONObj query = QUERY(SettingsType::exampleField(SettingsType::ExampleFieldName)); * exampleDoc = conn->findOne(SettingsType::ConfigNS, query); * * // Process the response. - * SettingsType exampleType; - * std::string errMsg; - * if (!exampleType.parseBSON(exampleDoc, &errMsg) || !exampleType.isValid(&errMsg)) { - * // Can't use 'exampleType'. Take action. + * 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() * } - * // use 'exampleType' - * - * It is the responsibility of the caller to make sure that this object has - * the right "key" when calling the getter and serialize methods. The key is - * used for identifying the type of document this object represents (for example, - * balancer settings or chunk size settings document). + * SettingsType exampleType = exampleResult.getValue(); */ class SettingsType { public: - // - // schema declarations - // - // 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<int> chunksize; + static const BSONField<long long> chunkSize; 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; - // - // settings type methods - // - - SettingsType(); - ~SettingsType(); - /** - * Returns true if all the mandatory fields are present and have valid - * representations. Otherwise returns false and fills in the optional 'errMsg' string. + * Returns OK if all mandatory fields have been set and their corresponding + * values are valid. */ - bool isValid(std::string* errMsg) const; + Status validate() const; /** * Returns the BSON representation of the entry. @@ -104,218 +96,95 @@ namespace mongo { BSONObj toBSON() const; /** - * Clears and populates the internal state using the 'source' BSON object if the - * latter contains valid values. Otherwise sets errMsg and returns false. + * Constructs a new ShardType object from BSON. + * Also does validation of the contents. */ - bool parseBSON(const BSONObj& source, std::string* errMsg); + static StatusWith<SettingsType> fromBSON(const BSONObj& source); /** - * Clears the internal state. + * Returns a std::string representation of the current internal state. */ - void clear(); + std::string toString() const; /** - * Copies all the fields present in 'this' to 'other'. + * Returns the write concern to use for balancing. + * Uses *deprecated* secondary throttle if migration write concern is not set. */ - void cloneTo(SettingsType* other) const; + std::unique_ptr<WriteConcernOptions> getWriteConcern() const; /** - * Returns a std::string representation of the current internal state. + * Returns true if either 'now' is in the balancing window or + * if no balancing window exists. */ - std::string toString() const; - - // - // individual field accessors - // - - // Mandatory Fields - void setKey(StringData key) { - _key = key.toString(); - _isKeySet = true; - } - - void unsetKey() { _isKeySet = false; } + bool inBalancingWindow(const boost::posix_time::ptime& now) const; - bool isKeySet() const { return _isKeySet; } + bool isKeySet() const { return _key.is_initialized(); } + const std::string& getKey() const { return _key.get(); } + void setKey(const std::string& key); - // Calling get*() methods when the member is not set results in undefined behavior - const std::string getKey() const { - dassert(_isKeySet); - return _key; - } - - // Optional Fields - void setChunksize(int chunksize) { - _chunksize = chunksize; - _isChunksizeSet = true; - } - - void unsetChunksize() { _isChunksizeSet = false; } - - bool isChunksizeSet() const { - return _isChunksizeSet || chunksize.hasDefault(); - } - - // Calling get*() methods when the member is not set and has no default results in undefined - // behavior - int getChunksize() const { - dassert(_key == ChunkSizeDocKey); - if (_isChunksizeSet) { - return _chunksize; - } else { - dassert(chunksize.hasDefault()); - return chunksize.getDefault(); - } - } - void setBalancerStopped(bool balancerStopped) { - _balancerStopped = balancerStopped; - _isBalancerStoppedSet = true; - } + bool isChunkSizeSet() const { return _chunkSize.is_initialized(); } + long long getChunkSize() const { return _chunkSize.get(); } + void setChunkSize(const long long chunkSize); - void unsetBalancerStopped() { _isBalancerStoppedSet = false; } + bool isBalancerStoppedSet() const { return _balancerStopped.is_initialized(); } + bool getBalancerStopped() const { return _balancerStopped.get(); } + void setBalancerStopped(const bool balancerStopped); - bool isBalancerStoppedSet() const { - return _isBalancerStoppedSet || balancerStopped.hasDefault(); - } + bool isBalancerActiveWindowSet() const { return _balancerActiveWindow.is_initialized(); } + const BoostTimePair& getBalancerActiveWindow() const { return _balancerActiveWindow.get(); } + void setBalancerActiveWindow(const BSONObj& balancerActiveWindow); - // Calling get*() methods when the member is not set and has no default results in undefined - // behavior - bool getBalancerStopped() const { - dassert(_key == BalancerDocKey); - if (_isBalancerStoppedSet) { - return _balancerStopped; - } else { - dassert(balancerStopped.hasDefault()); - return balancerStopped.getDefault(); - } - } - void setBalancerActiveWindow(BSONObj& balancerActiveWindow) { - _balancerActiveWindow = balancerActiveWindow.getOwned(); - _isBalancerActiveWindowSet = true; - } - - void unsetBalancerActiveWindow() { _isBalancerActiveWindowSet = false; } + bool isSecondaryThrottleSet() const { return _secondaryThrottle.is_initialized(); } + bool getSecondaryThrottle() const { return _secondaryThrottle.get(); } + void setSecondaryThrottle(const bool secondaryThrottle); - bool isBalancerActiveWindowSet() const { - return _isBalancerActiveWindowSet || balancerActiveWindow.hasDefault(); + bool isMigrationWriteConcernSet() const { return _migrationWriteConcern.is_initialized(); } + const WriteConcernOptions& getMigrationWriteConcern() const { + return _migrationWriteConcern.get(); } + void setMigrationWriteConcern(const BSONObj& migrationWriteConcern); - // Calling get*() methods when the member is not set and has no default results in undefined - // behavior - BSONObj getBalancerActiveWindow() const { - dassert(_key == BalancerDocKey); - if (_isBalancerActiveWindowSet) { - return _balancerActiveWindow; - } else { - dassert(balancerActiveWindow.hasDefault()); - return balancerActiveWindow.getDefault(); - } - } + bool isWaitForDeleteSet() const { return _waitForDelete.is_initialized(); } + bool getWaitForDelete() const { return _waitForDelete.get(); } + void setWaitForDelete(const bool waitForDelete); - void setSecondaryThrottle(bool secondaryThrottle) { - _secondaryThrottle = secondaryThrottle; - _isSecondaryThrottleSet = true; - } - - void unsetSecondaryThrottle() { _isSecondaryThrottleSet = false; } - - bool isSecondaryThrottleSet() const { - return _isSecondaryThrottleSet || deprecated_secondaryThrottle.hasDefault(); - } - - // Calling get*() methods when the member is not set and has no default results in undefined - // behavior - bool getSecondaryThrottle() const { - dassert(_key == BalancerDocKey); - if (_isSecondaryThrottleSet) { - return _secondaryThrottle; - } else { - dassert(deprecated_secondaryThrottle.hasDefault()); - return deprecated_secondaryThrottle.getDefault(); - } - } - - void setMigrationWriteConcern(const BSONObj& writeConcern) { - _migrationWriteConcern = writeConcern; - _isMigrationWriteConcernSet = true; - } - - void unsetMigrationWriteConcern() { - _isMigrationWriteConcernSet = false; - } - - bool isMigrationWriteConcernSet() const { - return _isMigrationWriteConcernSet; - } - - // Calling get*() methods when the member is not set and has no default results in undefined - // behavior - BSONObj getMigrationWriteConcern() const { - dassert(_key == BalancerDocKey); - dassert (_isMigrationWriteConcernSet); - return _migrationWriteConcern; - } - - void setWaitForDelete(bool waitForDelete) { - _waitForDelete = waitForDelete; - _isWaitForDeleteSet = true; - } - - void unsetWaitForDelete() { - _isWaitForDeleteSet = false; - } - - bool isWaitForDeleteSet() const { - return _isWaitForDeleteSet; - } - - // Calling get*() methods when the member is not set and has no default results in undefined - // behavior - bool getWaitForDelete() const { - dassert(_key == BalancerDocKey); - dassert (_isWaitForDeleteSet); - return _waitForDelete; - } - - // Helper methods + private: /** - * Extract the write concern settings from this settings. This is only valid when - * key is "balancer". Returns NULL if secondary throttle is true but write - * concern is not specified. + * Used to parse balancing 'activeWindow'. + * See '_balancerActiveWindow' member variable doc comments below. */ - StatusWith<WriteConcernOptions*> extractWriteConcern() const; + StatusWith<BoostTimePair> _parseBalancingWindow(const BSONObj& balancingWindowObj); - private: // Convention: (M)andatory, (O)ptional, (S)pecial rule. - std::string _key; // (M) key determining the type of options to use - bool _isKeySet; - // === chunksize options === - int _chunksize; // (O) size of the chunks in our cluster - bool _isChunksizeSet; - // === balancer options === - bool _balancerStopped; // (O) balancer enabled/disabled - bool _isBalancerStoppedSet; - BSONObj _balancerActiveWindow; // (O) if present, activeWindow is an interval - bool _isBalancerActiveWindowSet; // during the day when the balancer should - // be active. - // Format: { start: "08:00" , stop: - // "19:30" }, strftime format is %H:%M + // (M) key determining the type of options to use + boost::optional<std::string> _key; + + // (S) size of the chunks in our cluster + // Required if key is chunkSize + boost::optional<long long> _chunkSize; + + // (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; - bool _secondaryThrottle; // (O) only migrate chunks as fast as at least - bool _isSecondaryThrottleSet; // one secondary can keep up with + // (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. - BSONObj _migrationWriteConcern; - bool _isMigrationWriteConcernSet; + boost::optional<WriteConcernOptions> _migrationWriteConcern; - bool _waitForDelete; // (O) synchronous migration cleanup. - bool _isWaitForDeleteSet; + // (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 index ca8755b6baf..16d1c84e49f 100644 --- a/src/mongo/s/catalog/type_settings_test.cpp +++ b/src/mongo/s/catalog/type_settings_test.cpp @@ -26,111 +26,192 @@ * 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 std::string; - using mongo::BSONObj; - using mongo::SettingsType; + using namespace mongo; - TEST(Validity, MissingFields) { - SettingsType settings; + TEST(SettingsType, MissingKey) { BSONObj objNoKey = BSONObj(); - string errMsg; - ASSERT(settings.parseBSON(objNoKey, &errMsg)); - ASSERT_EQUALS(errMsg, ""); - ASSERT_FALSE(settings.isValid(NULL)); - - BSONObj objChunksizeNoValue = BSON(SettingsType::key("chunksize")); - ASSERT(settings.parseBSON(objChunksizeNoValue, &errMsg)); - ASSERT_EQUALS(errMsg, ""); - ASSERT_FALSE(settings.isValid(NULL)); + 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::chunkSize(0)); + StatusWith<SettingsType> result = SettingsType::fromBSON(objChunkSizeZero); + ASSERT(result.isOK()); + SettingsType settings = result.getValue(); + ASSERT_EQUALS(settings.getChunkSize(), 0); + Status validationStatus = settings.validate(); + ASSERT_FALSE(validationStatus.isOK()); + ASSERT_EQUALS(validationStatus, ErrorCodes::BadValue); } - TEST(Validity, UnsupportedSetting) { - SettingsType settings; + TEST(SettingsType, UnsupportedSetting) { BSONObj objBadSetting = BSON(SettingsType::key("badsetting")); - string errMsg; - ASSERT(settings.parseBSON(objBadSetting, &errMsg)); - ASSERT_EQUALS(errMsg, ""); - ASSERT_FALSE(settings.isValid(NULL)); + 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(Validity, InvalidBalancerWindow) { - SettingsType settings; - BSONObj objBalancerBadKeys = BSON(SettingsType::key("balancer") << + TEST(SettingsType, InvalidBalancerWindow) { + BSONObj objBalancerBadKeys = BSON(SettingsType::key(SettingsType::BalancerDocKey) << SettingsType::balancerActiveWindow(BSON("begin" << "23:00" << "end" << "6:00" ))); - string errMsg; - ASSERT(settings.parseBSON(objBalancerBadKeys, &errMsg)); - ASSERT_EQUALS(errMsg, ""); - ASSERT_FALSE(settings.isValid(NULL)); - BSONObj objBalancerBadTimes = BSON(SettingsType::key("balancer") << + StatusWith<SettingsType> result = SettingsType::fromBSON(objBalancerBadKeys); + ASSERT_FALSE(result.isOK()); + + BSONObj objBalancerBadTimes = BSON(SettingsType::key(SettingsType::BalancerDocKey) << SettingsType::balancerActiveWindow(BSON("start" << "23" << "stop" << "6" ))); - ASSERT(settings.parseBSON(objBalancerBadTimes, &errMsg)); - ASSERT_EQUALS(errMsg, ""); - ASSERT_FALSE(settings.isValid(NULL)); + result = SettingsType::fromBSON(objBalancerBadTimes); + ASSERT_FALSE(result.isOK()); } - TEST(Validity, Valid) { - SettingsType settings; - BSONObj objChunksize = BSON(SettingsType::key("chunksize") << - SettingsType::chunksize(1)); - string errMsg; - ASSERT(settings.parseBSON(objChunksize, &errMsg)); - ASSERT_EQUALS(errMsg, ""); - ASSERT_TRUE(settings.isValid(NULL)); - ASSERT_EQUALS(settings.getKey(), "chunksize"); - ASSERT_EQUALS(settings.getChunksize(), 1); - - BSONObj objBalancer = BSON(SettingsType::key("balancer") << + TEST(SettingsType, ValidValues) { + BSONObj objChunkSize = BSON(SettingsType::key(SettingsType::ChunkSizeDocKey) << + SettingsType::chunkSize(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.getChunkSize(), 1); + + BSONObj objBalancer = BSON(SettingsType::key(SettingsType::BalancerDocKey) << SettingsType::balancerStopped(true) << SettingsType::balancerActiveWindow(BSON("start" << "23:00" << "stop" << "6:00" )) << SettingsType::migrationWriteConcern(BSON("w" << 2))); - ASSERT(settings.parseBSON(objBalancer, &errMsg)); - ASSERT_EQUALS(errMsg, ""); - ASSERT_TRUE(settings.isValid(NULL)); - ASSERT_EQUALS(settings.getKey(), "balancer"); + 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); - ASSERT_EQUALS(settings.getBalancerActiveWindow(), BSON("start" << "23:00" << - "stop" << "6:00" )); - ASSERT(settings.getSecondaryThrottle()); - ASSERT_EQUALS(0, settings.getMigrationWriteConcern().woCompare(BSON("w" << 2))); + + WriteConcernOptions wc; + wc.parse(BSON("w" << 2)); + ASSERT_EQUALS(settings.getMigrationWriteConcern().toBSON(), wc.toBSON()); } - TEST(Validity, ValidWithDeprecatedThrottle) { - SettingsType settings; - BSONObj objChunksize = BSON(SettingsType::key("chunksize") << - SettingsType::chunksize(1)); - string errMsg; - ASSERT(settings.parseBSON(objChunksize, &errMsg)); - ASSERT_EQUALS(errMsg, ""); - ASSERT_TRUE(settings.isValid(NULL)); - ASSERT_EQUALS(settings.getKey(), "chunksize"); - ASSERT_EQUALS(settings.getChunksize(), 1); - - BSONObj objBalancer = BSON(SettingsType::key("balancer") << + TEST(SettingsType, ValidWithDeprecatedThrottle) { + BSONObj objChunkSize = BSON(SettingsType::key(SettingsType::ChunkSizeDocKey) << + SettingsType::chunkSize(1)); + StatusWith<SettingsType> result = SettingsType::fromBSON(objChunkSize); + ASSERT(result.isOK()); + SettingsType settings = result.getValue(); + ASSERT_EQUALS(settings.getKey(), SettingsType::ChunkSizeDocKey); + ASSERT_EQUALS(settings.getChunkSize(), 1); + + BSONObj objBalancer = BSON(SettingsType::key(SettingsType::BalancerDocKey) << SettingsType::deprecated_secondaryThrottle(true)); - ASSERT(settings.parseBSON(objBalancer, &errMsg)); - ASSERT_EQUALS(errMsg, ""); - ASSERT_TRUE(settings.isValid(NULL)); - ASSERT_EQUALS(settings.getKey(), "balancer"); + result = SettingsType::fromBSON(objBalancer); + ASSERT_EQUALS(result.getStatus(), Status::OK()); + settings = result.getValue(); + ASSERT_EQUALS(settings.getKey(), SettingsType::BalancerDocKey); ASSERT(settings.getSecondaryThrottle()); } - TEST(Validity, BadType) { - SettingsType settings; - BSONObj obj = BSON(SettingsType::key() << 0); - string errMsg; - ASSERT((!settings.parseBSON(obj, &errMsg)) && (errMsg != "")); + 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()); + ASSERT(result.getValue().inBalancingWindow(now)); + + // missing start + BSONObj w7 = BSON(SettingsType::key(SettingsType::BalancerDocKey) << + SettingsType::balancerActiveWindow(BSON("stop" << 1))); + result = SettingsType::fromBSON(w7); + ASSERT_FALSE(result.isOK()); + ASSERT(result.getValue().inBalancingWindow(now)); + + // 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()); + ASSERT(result.getValue().inBalancingWindow(now)); + } } // unnamed namespace diff --git a/src/mongo/s/chunk.cpp b/src/mongo/s/chunk.cpp index be18e3089a0..496e25bda29 100644 --- a/src/mongo/s/chunk.cpp +++ b/src/mongo/s/chunk.cpp @@ -730,12 +730,7 @@ namespace { return; } SettingsType chunkSizeSettings = chunkSizeSettingsResult.getValue(); - string errMsg; - if (!chunkSizeSettings.isValid(&errMsg)) { - log() << errMsg; - return; - } - int csize = chunkSizeSettings.getChunksize(); + int csize = chunkSizeSettings.getChunkSize(); LOG(1) << "Refreshing MaxChunkSize: " << csize << "MB"; diff --git a/src/mongo/s/config.cpp b/src/mongo/s/config.cpp index fa72bf41572..39d9ad3cf8c 100644 --- a/src/mongo/s/config.cpp +++ b/src/mongo/s/config.cpp @@ -779,35 +779,41 @@ namespace mongo { set<string> got; try { - ScopedDbConnection conn(_primary.getConnString(), 30.0); auto_ptr<DBClientCursor> cursor = conn->query(SettingsType::ConfigNS, BSONObj()); verify(cursor.get()); + while (cursor->more()) { + StatusWith<SettingsType> settingsResult = + SettingsType::fromBSON(cursor->nextSafe()); + if (!settingsResult.isOK()) { + warning() << settingsResult.getStatus(); + continue; + } + SettingsType settings = settingsResult.getValue(); + string key = settings.getKey(); + got.insert(key); - BSONObj o = cursor->nextSafe(); - string name = o[SettingsType::key()].valuestrsafe(); - got.insert( name ); - if ( name == "chunksize" ) { - int csize = o[SettingsType::chunksize()].numberInt(); + if (key == SettingsType::ChunkSizeDocKey) { + int csize = settings.getChunkSize(); // validate chunksize before proceeding - if ( csize == 0 ) { + if (csize == 0) { // setting was not modified; mark as such - got.erase(name); + got.erase(key); log() << "warning: invalid chunksize (" << csize << ") ignored" << endl; } else { LOG(1) << "MaxChunkSize: " << csize << endl; - if ( !Chunk::setMaxChunkSizeSizeMB( csize ) ) { + if (!Chunk::setMaxChunkSizeSizeMB(csize)) { warning() << "invalid chunksize: " << csize << endl; } } } - else if ( name == "balancer" ) { + else if (key == SettingsType::BalancerDocKey) { // ones we ignore here } else { - log() << "warning: unknown setting [" << name << "]" << endl; + log() << "warning: unknown setting [" << key << "]"; } } @@ -817,13 +823,13 @@ namespace mongo { warning() << "couldn't load settings on config db" << causedBy(ex); } - if ( ! got.count( "chunksize" ) ) { + if (!got.count(SettingsType::ChunkSizeDocKey)) { const int chunkSize = Chunk::MaxChunkSize / (1024 * 1024); - Status result = grid.catalogManager()->insert( - SettingsType::ConfigNS, - BSON(SettingsType::key("chunksize") - << SettingsType::chunksize(chunkSize)), - NULL); + Status result = + grid.catalogManager()->insert(SettingsType::ConfigNS, + BSON(SettingsType::key(SettingsType::ChunkSizeDocKey) + << SettingsType::chunkSize(chunkSize)), + NULL); if (!result.isOK()) { warning() << "couldn't set chunkSize on config db" << causedBy(result); } diff --git a/src/mongo/s/grid.cpp b/src/mongo/s/grid.cpp index a9860fda1fa..b4ce68962e3 100644 --- a/src/mongo/s/grid.cpp +++ b/src/mongo/s/grid.cpp @@ -100,7 +100,7 @@ namespace mongo { if (balancerSettings.isBalancerActiveWindowSet()) { boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - return _inBalancingWindow(balancerSettings.getBalancerActiveWindow(), now); + return balancerSettings.inBalancingWindow(now); } return true; @@ -123,55 +123,5 @@ namespace mongo { return shouldBalance(balSettings); } - bool Grid::_inBalancingWindow( const BSONObj& balancerDoc , const boost::posix_time::ptime& now ) { - // check the 'activeWindow' marker - // if present, it is an interval during the day when the balancer should be active - // { start: "08:00" , stop: "19:30" }, strftime format is %H:%M - BSONElement windowElem = balancerDoc[SettingsType::balancerActiveWindow()]; - if ( windowElem.eoo() ) { - return true; - } - - // check if both 'start' and 'stop' are present - if ( ! windowElem.isABSONObj() ) { - warning() << "'activeWindow' format is { start: \"hh:mm\" , stop: ... }" << balancerDoc << endl; - return true; - } - BSONObj intervalDoc = windowElem.Obj(); - const string start = intervalDoc["start"].str(); - const string stop = intervalDoc["stop"].str(); - if ( start.empty() || stop.empty() ) { - warning() << "must specify both start and end of balancing window: " << intervalDoc << endl; - return true; - } - - // check that both 'start' and 'stop' are valid time-of-day - boost::posix_time::ptime startTime, stopTime; - if ( ! toPointInTime( start , &startTime ) || ! toPointInTime( stop , &stopTime ) ) { - warning() << "cannot parse active window (use hh:mm 24hs format): " << intervalDoc << endl; - return true; - } - - 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; - } - Grid grid; } diff --git a/src/mongo/s/grid.h b/src/mongo/s/grid.h index d70db0ac01d..eb344a34392 100644 --- a/src/mongo/s/grid.h +++ b/src/mongo/s/grid.h @@ -28,7 +28,6 @@ #pragma once -#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/shared_ptr.hpp> #include <string> #include <vector> @@ -88,15 +87,6 @@ namespace mongo { CatalogManager* catalogManager() const { return _catalogManager.get(); } CatalogCache* catalogCache() const { return _catalogCache.get(); } - // exposed methods below are for testing only - - /** - * @param balancerDoc bson that may contain a window of time for the balancer to work - * format { ... , activeWindow: { start: "8:30" , stop: "19:00" } , ... } - * @return true if there is no window of time specified for the balancer or it we're currently in it - */ - static bool _inBalancingWindow(const BSONObj& balancerDoc, const boost::posix_time::ptime& now); - private: std::unique_ptr<CatalogManager> _catalogManager; std::unique_ptr<CatalogCache> _catalogCache; |