summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaloian Manassiev <kaloian.manassiev@mongodb.com>2016-11-14 14:13:03 -0500
committerKaloian Manassiev <kaloian.manassiev@mongodb.com>2016-11-15 10:08:45 -0500
commit21821549cdeef84173293c46725695fc3a7834b0 (patch)
treea6d68779f04e83e289d036418d20087522e09d1b
parent19449d2883f2a97b622102935a63606d950573ad (diff)
downloadmongo-21821549cdeef84173293c46725695fc3a7834b0.tar.gz
SERVER-26927 Rename maxStalenessMS to maxStalenessSeconds and support doubles
-rw-r--r--src/mongo/bson/util/bson_extract.cpp15
-rw-r--r--src/mongo/bson/util/bson_extract.h12
-rw-r--r--src/mongo/client/read_preference.cpp70
-rw-r--r--src/mongo/client/read_preference_test.cpp109
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