diff options
author | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2016-11-14 14:13:03 -0500 |
---|---|---|
committer | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2016-11-15 10:08:45 -0500 |
commit | 21821549cdeef84173293c46725695fc3a7834b0 (patch) | |
tree | a6d68779f04e83e289d036418d20087522e09d1b | |
parent | 19449d2883f2a97b622102935a63606d950573ad (diff) | |
download | mongo-21821549cdeef84173293c46725695fc3a7834b0.tar.gz |
SERVER-26927 Rename maxStalenessMS to maxStalenessSeconds and support doubles
-rw-r--r-- | src/mongo/bson/util/bson_extract.cpp | 15 | ||||
-rw-r--r-- | src/mongo/bson/util/bson_extract.h | 12 | ||||
-rw-r--r-- | src/mongo/client/read_preference.cpp | 70 | ||||
-rw-r--r-- | src/mongo/client/read_preference_test.cpp | 109 |
4 files changed, 140 insertions, 66 deletions
diff --git a/src/mongo/bson/util/bson_extract.cpp b/src/mongo/bson/util/bson_extract.cpp index b3ff37ca1b2..84c9cc92de6 100644 --- a/src/mongo/bson/util/bson_extract.cpp +++ b/src/mongo/bson/util/bson_extract.cpp @@ -170,6 +170,21 @@ Status bsonExtractIntegerField(const BSONObj& object, StringData fieldName, long return Status::OK(); } +Status bsonExtractDoubleField(const BSONObj& object, StringData fieldName, double* out) { + BSONElement value; + Status status = bsonExtractField(object, fieldName, &value); + if (!status.isOK()) + return status; + if (!value.isNumber()) { + return Status(ErrorCodes::TypeMismatch, + mongoutils::str::stream() << "Expected field \"" << fieldName + << "\" to have numeric type, but found " + << typeName(value.type())); + } + *out = value.numberDouble(); + return Status::OK(); +} + Status bsonExtractIntegerFieldWithDefault(const BSONObj& object, StringData fieldName, long long defaultValue, diff --git a/src/mongo/bson/util/bson_extract.h b/src/mongo/bson/util/bson_extract.h index a2189a4d57b..cedc84cd01f 100644 --- a/src/mongo/bson/util/bson_extract.h +++ b/src/mongo/bson/util/bson_extract.h @@ -86,6 +86,18 @@ Status bsonExtractBooleanField(const BSONObj& object, StringData fieldName, bool Status bsonExtractIntegerField(const BSONObj& object, StringData fieldName, long long* out); /** + * Finds an element named "fieldName" in "object" that represents a double-precision floating point + * value. + * + * Returns Status::OK() and sets *out to the element's double floating point value representation on + * success. Returns ErrorCodes::NoSuchKey if there are no matches for "fieldName". Returns + * ErrorCodes::TypeMismatch if the value of the matching element is not of a numeric type. Returns + * ErrorCodes::BadValue if the value does not have an exact floating point number representation. + * For return values other than Status::OK(), the resulting value of "*out" is undefined. + */ +Status bsonExtractDoubleField(const BSONObj& object, StringData fieldName, double* out); + +/** * Finds a string-typed element named "fieldName" in "object" and stores its value in "out". * * Returns Status::OK() and sets *out to the found element's std::string value on success. Returns diff --git a/src/mongo/client/read_preference.cpp b/src/mongo/client/read_preference.cpp index 5df64eab171..ea16f3365ad 100644 --- a/src/mongo/client/read_preference.cpp +++ b/src/mongo/client/read_preference.cpp @@ -46,7 +46,7 @@ namespace { const char kModeFieldName[] = "mode"; const char kTagsFieldName[] = "tags"; -const char kMaxStalenessMSFieldName[] = "maxStalenessMS"; +const char kMaxStalenessSecondsFieldName[] = "maxStalenessSeconds"; const char kPrimaryOnly[] = "primary"; const char kPrimaryPreferred[] = "primaryPreferred"; @@ -54,6 +54,9 @@ const char kSecondaryOnly[] = "secondary"; const char kSecondaryPreferred[] = "secondaryPreferred"; const char kNearest[] = "nearest"; +// Avoid overflow errors when converting from seconds to milliseconds +const auto kMaximalMaxStalenessSecondsValue(durationCount<Seconds>(Milliseconds::max())); + StringData readPreferenceName(ReadPreference pref) { switch (pref) { case ReadPreference::PrimaryOnly: @@ -182,39 +185,56 @@ StatusWith<ReadPreferenceSetting> ReadPreferenceSetting::fromBSON(const BSONObj& return tagExtractStatus; } - long long maxStalenessMSValue; - auto maxStalenessMSExtractStatus = bsonExtractIntegerFieldWithDefault( - readPrefObj, kMaxStalenessMSFieldName, 0, &maxStalenessMSValue); + double maxStalenessSecondsValue; + + Status maxStalenessSecondsExtractStatus = bsonExtractDoubleField( + readPrefObj, kMaxStalenessSecondsFieldName, &maxStalenessSecondsValue); + + if (maxStalenessSecondsExtractStatus == ErrorCodes::NoSuchKey) { + return ReadPreferenceSetting(mode, tags); + } else if (!maxStalenessSecondsExtractStatus.isOK()) { + return maxStalenessSecondsExtractStatus; + } - if (!maxStalenessMSExtractStatus.isOK()) { - return maxStalenessMSExtractStatus; + if (maxStalenessSecondsValue < 0.0) { + return {ErrorCodes::BadValue, + str::stream() << kMaxStalenessSecondsFieldName << " value can not be negative"}; } - if (maxStalenessMSValue && maxStalenessMSValue < 0) { - return Status(ErrorCodes::BadValue, - str::stream() << kMaxStalenessMSFieldName - << " must be a non negative integer"); + if (maxStalenessSecondsValue > kMaximalMaxStalenessSecondsValue) { + return {ErrorCodes::MaxStalenessOutOfRange, + str::stream() << kMaxStalenessSecondsFieldName << " value can not exceed " + << kMaximalMaxStalenessSecondsValue}; } - if (maxStalenessMSValue && maxStalenessMSValue >= Milliseconds::max().count()) { - return Status(ErrorCodes::BadValue, - str::stream() << kMaxStalenessMSFieldName << " value can not exceed " - << Milliseconds::max().count()); + if (maxStalenessSecondsValue == 0.0) { + return ReadPreferenceSetting(mode, tags); } - if (maxStalenessMSValue && maxStalenessMSValue < kMinimalMaxStalenessValue.count()) { - return Status(ErrorCodes::MaxStalenessOutOfRange, - str::stream() << kMaxStalenessMSFieldName << " value can not be less than " - << kMinimalMaxStalenessValue.count()); + // Use a lambda to do the double seconds to integer milliseconds conversion in order to + // encapsulate the usage of helper variables + const Milliseconds requestedMaxStalenessMS = [maxStalenessSecondsValue] { + double integerPart; + const double fractionalPart = std::modf(maxStalenessSecondsValue, &integerPart); + + return Seconds(static_cast<long long>(integerPart)) + + Milliseconds(static_cast<long long>(fractionalPart * + durationCount<Milliseconds>(Seconds(1)))); + }(); + + if (requestedMaxStalenessMS < kMinimalMaxStalenessValue) { + return {ErrorCodes::MaxStalenessOutOfRange, + str::stream() << kMaxStalenessSecondsFieldName << " value can not be less than " + << kMinimalMaxStalenessValue}; } - if ((mode == ReadPreference::PrimaryOnly) && maxStalenessMSValue) { - return Status(ErrorCodes::BadValue, - str::stream() << kMaxStalenessMSFieldName - << " can not be set for the primary mode"); + if (mode == ReadPreference::PrimaryOnly) { + return {ErrorCodes::BadValue, + str::stream() << kMaxStalenessSecondsFieldName + << " can not be set for the primary mode"}; } - return ReadPreferenceSetting(mode, tags, Milliseconds(maxStalenessMSValue)); + return ReadPreferenceSetting(mode, tags, requestedMaxStalenessMS); } BSONObj ReadPreferenceSetting::toBSON() const { @@ -224,7 +244,9 @@ BSONObj ReadPreferenceSetting::toBSON() const { bob.append(kTagsFieldName, tags.getTagBSON()); } if (maxStalenessMS.count() > 0) { - bob.append(kMaxStalenessMSFieldName, maxStalenessMS.count()); + bob.append(kMaxStalenessSecondsFieldName, + static_cast<double>(maxStalenessMS.count()) / + durationCount<Milliseconds>(Seconds(1))); } return bob.obj(); } diff --git a/src/mongo/client/read_preference_test.cpp b/src/mongo/client/read_preference_test.cpp index 46563b78a7f..e080d059d08 100644 --- a/src/mongo/client/read_preference_test.cpp +++ b/src/mongo/client/read_preference_test.cpp @@ -32,17 +32,20 @@ #include "mongo/unittest/unittest.h" #include "mongo/util/duration.h" +namespace mongo { namespace { -using namespace mongo; +using unittest::assertGet; -static const Milliseconds minMaxStaleness = ReadPreferenceSetting::kMinimalMaxStalenessValue; +const Seconds minMaxStalenessSeconds( + durationCount<Seconds>(ReadPreferenceSetting::kMinimalMaxStalenessValue)); void checkParse(const BSONObj& rpsObj, const ReadPreferenceSetting& expected) { - const auto swRps = ReadPreferenceSetting::fromBSON(rpsObj); - ASSERT_OK(swRps.getStatus()); - const auto rps = swRps.getValue(); - ASSERT_TRUE(rps.equals(expected)); + const auto rps = assertGet(ReadPreferenceSetting::fromBSON(rpsObj)); + if (!rps.equals(expected)) { + FAIL(str::stream() << "Expected " << expected.toString() << " does not match actual " + << rps.toString()); + } } TEST(ReadPreferenceSetting, ParseValid) { @@ -68,27 +71,39 @@ TEST(ReadPreferenceSetting, ParseValid) { << "ny"))))); checkParse(BSON("mode" << "secondary" - << "maxStalenessMS" - << minMaxStaleness.count()), - ReadPreferenceSetting(ReadPreference::SecondaryOnly, minMaxStaleness)); + << "maxStalenessSeconds" + << minMaxStalenessSeconds.count()), + ReadPreferenceSetting(ReadPreference::SecondaryOnly, minMaxStalenessSeconds)); checkParse(BSON("mode" << "secondary" - << "maxStalenessMS" + << "maxStalenessSeconds" << 0), ReadPreferenceSetting(ReadPreference::SecondaryOnly, Milliseconds(0))); checkParse(BSON("mode" << "secondary" + << "maxStalenessSeconds" + << 61LL), + ReadPreferenceSetting(ReadPreference::SecondaryOnly, Milliseconds(61000))); + + checkParse(BSON("mode" + << "secondary" + << "maxStalenessSeconds" + << 63.46), + ReadPreferenceSetting(ReadPreference::SecondaryOnly, Milliseconds(63460))); + + checkParse(BSON("mode" + << "secondary" << "tags" << BSON_ARRAY(BSON("dc" << "ny")) - << "maxStalenessMS" - << minMaxStaleness.count()), + << "maxStalenessSeconds" + << minMaxStalenessSeconds.count()), ReadPreferenceSetting(ReadPreference::SecondaryOnly, TagSet(BSON_ARRAY(BSON("dc" << "ny"))), - minMaxStaleness)); + minMaxStalenessSeconds)); } void checkParseFails(const BSONObj& rpsObj) { @@ -107,14 +122,14 @@ TEST(ReadPreferenceSetting, NonEquality) { << "ca") << BSON("foo" << "bar"))); - auto rps = ReadPreferenceSetting(ReadPreference::Nearest, tagSet, minMaxStaleness); + auto rps = ReadPreferenceSetting(ReadPreference::Nearest, tagSet, minMaxStalenessSeconds); - auto unexpected1 = - ReadPreferenceSetting(ReadPreference::Nearest, TagSet::primaryOnly(), minMaxStaleness); + auto unexpected1 = ReadPreferenceSetting( + ReadPreference::Nearest, TagSet::primaryOnly(), minMaxStalenessSeconds); ASSERT_FALSE(rps.equals(unexpected1)); auto unexpected2 = ReadPreferenceSetting( - ReadPreference::Nearest, tagSet, Milliseconds(minMaxStaleness.count() + 1)); + ReadPreference::Nearest, tagSet, Seconds(minMaxStalenessSeconds.count() + 1)); ASSERT_FALSE(rps.equals(unexpected2)); } @@ -139,42 +154,44 @@ TEST(ReadPreferenceSetting, ParseInvalid) { << "tags" << "bad")); - // maxStalenessMS is negative - checkParseFails(BSON("mode" - << "secondary" - << "maxStalenessMS" - << -1)); + // maxStalenessSeconds is negative + checkParseFailsWithError(BSON("mode" + << "secondary" + << "maxStalenessSeconds" + << -1), + ErrorCodes::BadValue); - // maxStalenessMS is NaN - checkParseFails(BSON("mode" - << "secondary" - << "maxStalenessMS" - << "ONE")); + // maxStalenessSeconds is NaN + checkParseFailsWithError(BSON("mode" + << "secondary" + << "maxStalenessSeconds" + << "ONE"), + ErrorCodes::TypeMismatch); - // maxStalenessMS and primary + // maxStalenessSeconds and primary checkParseFails(BSON("mode" << "primary" - << "maxStalenessMS" - << minMaxStaleness.count())); + << "maxStalenessSeconds" + << minMaxStalenessSeconds.count())); - // maxStalenessMS is less than min + // maxStalenessSeconds is less than min checkParseFailsWithError(BSON("mode" << "primary" - << "maxStalenessMS" - << Milliseconds(minMaxStaleness.count() - 1).count()), + << "maxStalenessSeconds" + << minMaxStalenessSeconds.count() - 1), ErrorCodes::MaxStalenessOutOfRange); - // maxStalenessMS is greater than max - checkParseFails(BSON("mode" - << "secondary" - << "maxStalenessMS" - << Milliseconds::max().count())); + // maxStalenessSeconds is greater than max type value for milliseconds + checkParseFailsWithError(BSON("mode" + << "secondary" + << "maxStalenessSeconds" + << Milliseconds::max().count()), + ErrorCodes::MaxStalenessOutOfRange); } void checkRoundtrip(const ReadPreferenceSetting& rps) { - auto parsed = ReadPreferenceSetting::fromBSON(rps.toBSON()); - ASSERT_OK(parsed.getStatus()); - ASSERT_TRUE(parsed.getValue().equals(rps)); + auto parsed = assertGet(ReadPreferenceSetting::fromBSON(rps.toBSON())); + ASSERT_TRUE(parsed.equals(rps)); } TEST(ReadPreferenceSetting, Roundtrip) { @@ -198,7 +215,15 @@ TEST(ReadPreferenceSetting, Roundtrip) { << "ca") << BSON("foo" << "bar"))), - minMaxStaleness)); + minMaxStalenessSeconds)); + + checkRoundtrip(ReadPreferenceSetting(ReadPreference::Nearest, + TagSet(BSON_ARRAY(BSON("dc" + << "ca") + << BSON("foo" + << "bar"))), + Milliseconds(63246))); } } // namespace +} // namespace mongo |