summaryrefslogtreecommitdiff
path: root/src/mongo/s/catalog
diff options
context:
space:
mode:
authorDaniel Alabi <alabidan@gmail.com>2015-04-20 00:28:50 -0400
committerDaniel Alabi <alabidan@gmail.com>2015-04-30 10:22:52 -0400
commit30c9fa8ef46c1c6a0951a0d8ab724991e5cc04e2 (patch)
tree4da2d1012ac53da70c171aa0a291c01fb60b9a66 /src/mongo/s/catalog
parentc269033226874638626b6c028bf0f81fa0af2006 (diff)
downloadmongo-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/mongo/s/catalog')
-rw-r--r--src/mongo/s/catalog/catalog_manager.h4
-rw-r--r--src/mongo/s/catalog/legacy/catalog_manager_legacy.cpp26
-rw-r--r--src/mongo/s/catalog/legacy/config_upgrade.cpp22
-rw-r--r--src/mongo/s/catalog/type_settings.cpp387
-rw-r--r--src/mongo/s/catalog/type_settings.h287
-rw-r--r--src/mongo/s/catalog/type_settings_test.cpp225
6 files changed, 466 insertions, 485 deletions
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