summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/s/chunk_version.cpp95
-rw-r--r--src/mongo/s/chunk_version.h8
-rw-r--r--src/mongo/s/chunk_version_test.cpp65
3 files changed, 107 insertions, 61 deletions
diff --git a/src/mongo/s/chunk_version.cpp b/src/mongo/s/chunk_version.cpp
index fd71e7270f8..0de0352f3b9 100644
--- a/src/mongo/s/chunk_version.cpp
+++ b/src/mongo/s/chunk_version.cpp
@@ -56,28 +56,26 @@ StatusWith<ChunkVersion> ChunkVersion::fromBSON(const BSONObj& obj) {
if (!it.more())
return {ErrorCodes::BadValue, "Unexpected empty version array"};
- ChunkVersion version;
-
- // Expect the major and minor versions
+ // Expect the major and minor versions (must be present)
+ uint64_t combined;
{
BSONElement tsPart = it.next();
if (tsPart.type() != bsonTimestamp)
return {ErrorCodes::TypeMismatch,
str::stream() << "Invalid type " << tsPart.type()
<< " for version major and minor part."};
-
- version._combined = tsPart.timestamp().asULL();
+ combined = tsPart.timestamp().asULL();
}
- // Expect the epoch OID
+ // Expect the epoch OID (must be present)
+ boost::optional<OID> epoch;
{
BSONElement epochPart = it.next();
if (epochPart.type() != jstOID)
return {ErrorCodes::TypeMismatch,
str::stream() << "Invalid type " << epochPart.type()
<< " for version epoch part."};
-
- version._epoch = epochPart.OID();
+ epoch = epochPart.OID();
}
BSONElement nextElem = it.next();
@@ -90,33 +88,37 @@ StatusWith<ChunkVersion> ChunkVersion::fromBSON(const BSONObj& obj) {
}
// Check for timestamp
+ boost::optional<Timestamp> timestamp;
if (nextElem.type() == bsonTimestamp) {
- version._timestamp = nextElem.timestamp();
- } else if (nextElem.eoo() && version.is50IgnoredOrUnsharded()) {
+ timestamp = nextElem.timestamp();
+ } else if (nextElem.eoo() && (epoch == UNSHARDED().epoch() || epoch == IGNORED().epoch())) {
// In 5.0 binaries, the timestamp is not present in UNSHARDED and IGNORED versions
- version._timestamp =
- (version.epoch() == UNSHARDED().epoch()) ? Timestamp() : Timestamp::max();
+ timestamp =
+ (epoch == UNSHARDED().epoch() ? UNSHARDED().getTimestamp() : IGNORED().getTimestamp());
} else {
return {ErrorCodes::TypeMismatch,
str::stream() << "Invalid type " << nextElem.type()
<< " for version timestamp part."};
}
+ ChunkVersion version;
+ version._combined = combined;
+ version._epoch = *epoch;
+ version._timestamp = *timestamp;
return version;
}
StatusWith<ChunkVersion> ChunkVersion::parseLegacyWithField(const BSONObj& obj, StringData field) {
- auto versionElem = obj[field];
- if (versionElem.eoo())
- return {ErrorCodes::NoSuchKey,
- str::stream() << "Expected field " << field << " not found."};
-
- ChunkVersion version;
-
- // Expect the major and minor
+ // Expect the major and minor (must always exist)
+ uint64_t combined;
{
+ auto versionElem = obj[field];
+ if (versionElem.eoo())
+ return {ErrorCodes::NoSuchKey,
+ str::stream() << "Expected field " << field << " not found."};
+
if (versionElem.type() == bsonTimestamp || versionElem.type() == Date) {
- version._combined = versionElem._numberLong();
+ combined = versionElem._numberLong();
} else {
return {ErrorCodes::TypeMismatch,
str::stream() << "Invalid type " << versionElem.type()
@@ -124,14 +126,16 @@ StatusWith<ChunkVersion> ChunkVersion::parseLegacyWithField(const BSONObj& obj,
}
}
- bool fullVersion = false;
// Expect the epoch OID
+ //
+ // TODO: Confirm whether the epoch can still be missing in upgrade chains that started from
+ // pre-2.4 versions anymore (after FCV 4.4 -> 5.0 upgrade) ?
+ boost::optional<OID> epoch;
{
const auto epochField = field + "Epoch";
auto epochElem = obj[epochField];
if (epochElem.type() == jstOID) {
- version._epoch = epochElem.OID();
- fullVersion = true;
+ epoch = epochElem.OID();
} else if (!epochElem.eoo()) {
return {ErrorCodes::TypeMismatch,
str::stream() << "Invalid type " << epochElem.type()
@@ -139,27 +143,42 @@ StatusWith<ChunkVersion> ChunkVersion::parseLegacyWithField(const BSONObj& obj,
}
}
- // Expect the timestamp
+ // Expect the timestamp (can be missing only in the case of pre-5.0 UNSHARDED and IGNORED
+ // versions)
+ boost::optional<Timestamp> timestamp;
{
const auto timestampField = field + "Timestamp";
auto timestampElem = obj[timestampField];
- if (fullVersion) {
- if (timestampElem.type() == bsonTimestamp) {
- version._timestamp = timestampElem.timestamp();
- } else if (timestampElem.eoo() && version.is50IgnoredOrUnsharded()) {
- // In 5.0 binaries, the timestamp is not present in UNSHARDED and IGNORED versions
- version._timestamp =
- (version.epoch() == UNSHARDED().epoch()) ? Timestamp() : Timestamp::max();
- } else {
- return {ErrorCodes::TypeMismatch,
- str::stream() << "Invalid type " << timestampElem.type()
- << " for version timestamp part."};
- }
+ if (timestampElem.type() == bsonTimestamp) {
+ timestamp = timestampElem.timestamp();
+ } else if (!timestampElem.eoo()) {
+ return {ErrorCodes::TypeMismatch,
+ str::stream() << "Invalid type " << timestampElem.type()
+ << " for version timestamp part."};
+ }
+ }
+
+ if (epoch && timestamp) {
+ // Expected situation
+ } else if (epoch && !timestamp) {
+ if (epoch == UNSHARDED().epoch() || epoch == IGNORED().epoch()) {
+ // In 5.0 binaries, the timestamp is not present in UNSHARDED and IGNORED versions
+ timestamp = (epoch == UNSHARDED().epoch() ? UNSHARDED().getTimestamp()
+ : IGNORED().getTimestamp());
} else {
- invariant(timestampElem.eoo());
+ uasserted(6278300, "Timestamp must be present if epoch exists.");
}
+ } else if (!epoch && timestamp) {
+ uasserted(6278301, "Epoch must be present if timestamp exists.");
+ } else {
+ // Can happen in upgrade chains that started from pre-2.4 versions or in the case of
+ // persistence for ShardCollectionType
}
+ ChunkVersion version;
+ version._combined = combined;
+ version._epoch = epoch.value_or(OID());
+ version._timestamp = timestamp.value_or(Timestamp());
return version;
}
diff --git a/src/mongo/s/chunk_version.h b/src/mongo/s/chunk_version.h
index 96ea3864b35..be1c25f6094 100644
--- a/src/mongo/s/chunk_version.h
+++ b/src/mongo/s/chunk_version.h
@@ -123,14 +123,6 @@ public:
version.getTimestamp() == IGNORED().getTimestamp();
}
- /**
- * Needed for parsing IGNORED and UNSHARDED from 5.0 that didn't include a timestamp. Should be
- * removed after 6.0 is last-lts.
- */
- bool is50IgnoredOrUnsharded() {
- return _combined == 0 && (_epoch == UNSHARDED().epoch() || _epoch == IGNORED().epoch());
- }
-
void incMajor() {
uassert(
31180,
diff --git a/src/mongo/s/chunk_version_test.cpp b/src/mongo/s/chunk_version_test.cpp
index 624ff4ae81f..e3095e0186f 100644
--- a/src/mongo/s/chunk_version_test.cpp
+++ b/src/mongo/s/chunk_version_test.cpp
@@ -104,27 +104,60 @@ TEST(ChunkVersionParsing, FromBSONMissingMajorAndMinor) {
ErrorCodes::TypeMismatch);
}
-TEST(ChunkVersionParsing, FromBSONLegacy) {
+TEST(ChunkVersionParsing, FromBSONLegacy_WithTimestamp_WithEpoch) {
const OID oid = OID::gen();
- const Timestamp timestamp(42);
ChunkVersion chunkVersionComplete = assertGet(ChunkVersion::parseLegacyWithField(
BSON("lastmod" << Timestamp(Seconds(2), 3) << "lastmodEpoch" << oid << "lastmodTimestamp"
- << timestamp),
+ << Timestamp(42)),
"lastmod"));
-
- ASSERT(chunkVersionComplete.epoch().isSet());
+ ASSERT_EQ(Timestamp(42), chunkVersionComplete.getTimestamp());
ASSERT_EQ(oid, chunkVersionComplete.epoch());
ASSERT_EQ(2u, chunkVersionComplete.majorVersion());
ASSERT_EQ(3u, chunkVersionComplete.minorVersion());
}
-TEST(ChunkVersionParsing, FromBSONLegacyEpochAndTimestampOptional) {
- ChunkVersion chunkVersionNoEpoch = assertGet(
- ChunkVersion::parseLegacyWithField(BSON("lastmod" << Timestamp(Seconds(3), 4)), "lastmod"));
+TEST(ChunkVersionParsing, FromBSONLegacy_NoTimestamp_WithUnshardedEpoch) {
+ ChunkVersion chunkVersion = assertGet(ChunkVersion::parseLegacyWithField(
+ BSON("lastmod" << Timestamp() << "lastmodEpoch" << ChunkVersion::UNSHARDED().epoch()),
+ "lastmod"));
+ ASSERT_EQ(ChunkVersion::UNSHARDED().getTimestamp(), chunkVersion.getTimestamp());
+ ASSERT_EQ(ChunkVersion::UNSHARDED().epoch(), chunkVersion.epoch());
+ ASSERT_EQ(0u, chunkVersion.majorVersion());
+ ASSERT_EQ(0u, chunkVersion.minorVersion());
+}
+
+TEST(ChunkVersionParsing, FromBSONLegacy_NoTimestamp_WithIgnoredEpoch) {
+ ChunkVersion chunkVersion = assertGet(ChunkVersion::parseLegacyWithField(
+ BSON("lastmod" << Timestamp() << "lastmodEpoch" << ChunkVersion::IGNORED().epoch()),
+ "lastmod"));
+ ASSERT_EQ(ChunkVersion::IGNORED().getTimestamp(), chunkVersion.getTimestamp());
+ ASSERT_EQ(ChunkVersion::IGNORED().epoch(), chunkVersion.epoch());
+ ASSERT_EQ(0u, chunkVersion.majorVersion());
+ ASSERT_EQ(0u, chunkVersion.minorVersion());
+}
+
+TEST(ChunkVersionParsing, FromBSONLegacy_NoTimestamp_WithShardedEpoch_Throws) {
+ ASSERT_THROWS(uassertStatusOK(ChunkVersion::parseLegacyWithField(
+ BSON("lastmod" << Timestamp(Seconds(3), 4) << "lastmodEpoch" << OID::gen()),
+ "lastmod")),
+ DBException);
+}
- ASSERT(!chunkVersionNoEpoch.epoch().isSet());
- ASSERT_EQ(3u, chunkVersionNoEpoch.majorVersion());
- ASSERT_EQ(4u, chunkVersionNoEpoch.minorVersion());
+TEST(ChunkVersionParsing, FromBSONLegacy_WithTimestamp_NoEpoch_Throws) {
+ ASSERT_THROWS(
+ uassertStatusOK(ChunkVersion::parseLegacyWithField(
+ BSON("lastmod" << Timestamp(Seconds(3), 4) << "lastmodTimestamp" << Timestamp(42)),
+ "lastmod")),
+ DBException);
+}
+
+TEST(ChunkVersionParsing, FromBSONLegacy_NoTimestamp_NoEpoch) {
+ ChunkVersion chunkVersion = assertGet(
+ ChunkVersion::parseLegacyWithField(BSON("lastmod" << Timestamp(Seconds(3), 4)), "lastmod"));
+ ASSERT_EQ(Timestamp(), chunkVersion.getTimestamp());
+ ASSERT(!chunkVersion.epoch().isSet());
+ ASSERT_EQ(3u, chunkVersion.majorVersion());
+ ASSERT_EQ(4u, chunkVersion.minorVersion());
}
TEST(ChunkVersionComparison, EqualityOperators) {
@@ -161,19 +194,20 @@ TEST(ChunkVersionComparison, OlderThan) {
}
TEST(ChunkVersionConstruction, CreateWithLargeValues) {
- const auto minorVersion = std::numeric_limits<uint32_t>::max();
- const uint32_t majorVersion = 1 << 24;
+ const uint32_t majorVersion = std::numeric_limits<uint32_t>::max();
+ const uint32_t minorVersion = std::numeric_limits<uint32_t>::max();
const auto epoch = OID::gen();
ChunkVersion version(majorVersion, minorVersion, epoch, Timestamp(1, 1));
ASSERT_EQ(majorVersion, version.majorVersion());
ASSERT_EQ(minorVersion, version.minorVersion());
ASSERT_EQ(epoch, version.epoch());
+ ASSERT_EQ(Timestamp(1, 1), version.getTimestamp());
}
TEST(ChunkVersionManipulation, ThrowsErrorIfOverflowIsAttemptedForMajorVersion) {
- const uint32_t minorVersion = 0;
const uint32_t majorVersion = std::numeric_limits<uint32_t>::max();
+ const uint32_t minorVersion = 0;
const auto epoch = OID::gen();
ChunkVersion version(majorVersion, minorVersion, epoch, Timestamp(1, 1));
@@ -185,8 +219,8 @@ TEST(ChunkVersionManipulation, ThrowsErrorIfOverflowIsAttemptedForMajorVersion)
}
TEST(ChunkVersionManipulation, ThrowsErrorIfOverflowIsAttemptedForMinorVersion) {
- const uint32_t minorVersion = std::numeric_limits<uint32_t>::max();
const uint32_t majorVersion = 0;
+ const uint32_t minorVersion = std::numeric_limits<uint32_t>::max();
const auto epoch = OID::gen();
ChunkVersion version(majorVersion, minorVersion, epoch, Timestamp(1, 1));
@@ -196,5 +230,6 @@ TEST(ChunkVersionManipulation, ThrowsErrorIfOverflowIsAttemptedForMinorVersion)
ASSERT_THROWS_CODE(version.incMinor(), DBException, 31181);
}
+
} // namespace
} // namespace mongo