diff options
author | Jamie Heppenstall <jamie.heppenstall@mongodb.com> | 2019-06-17 14:42:54 -0400 |
---|---|---|
committer | Jamie Heppenstall <jamie.heppenstall@mongodb.com> | 2019-07-12 16:59:06 -0400 |
commit | 6e02a4d34bd972e6755bb5f71a5b26f69fe2cfb0 (patch) | |
tree | 1a68aa771c357aceb88686004b4f2e730afa17d2 /src/mongo/s | |
parent | 75e47b701d34c9ef7beb0482121ac8b62d2d4991 (diff) | |
download | mongo-6e02a4d34bd972e6755bb5f71a5b26f69fe2cfb0.tar.gz |
SERVER-41658 Convert ShardCollectionType into an IDL type
Diffstat (limited to 'src/mongo/s')
-rw-r--r-- | src/mongo/s/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/s/catalog/type_shard_collection.cpp | 218 | ||||
-rw-r--r-- | src/mongo/s/catalog/type_shard_collection.h | 188 | ||||
-rw-r--r-- | src/mongo/s/catalog/type_shard_collection.idl | 113 | ||||
-rw-r--r-- | src/mongo/s/catalog/type_shard_collection_test.cpp | 155 | ||||
-rw-r--r-- | src/mongo/s/chunk_version.cpp | 4 | ||||
-rw-r--r-- | src/mongo/s/chunk_version.h | 16 | ||||
-rw-r--r-- | src/mongo/s/chunk_version.idl | 8 | ||||
-rw-r--r-- | src/mongo/s/shard_key_pattern_test.cpp | 2 |
9 files changed, 255 insertions, 452 deletions
diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript index aac39f39858..8a8b8c6692d 100644 --- a/src/mongo/s/SConscript +++ b/src/mongo/s/SConscript @@ -174,6 +174,7 @@ env.Library( 'stale_exception.cpp', 'would_change_owning_shard_exception.cpp', env.Idlc('catalog/type_chunk_base.idl')[0], + env.Idlc('catalog/type_shard_collection.idl')[0], env.Idlc('chunk_version.idl')[0], env.Idlc('database_version.idl')[0], env.Idlc('request_types/clone_catalog_data.idl')[0], @@ -285,7 +286,6 @@ env.Library( ], ) - # This library contains sharding functionality used by both mongod and mongos env.Library( target='grid', @@ -446,6 +446,7 @@ env.CppUnitTest( '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/query/query_test_service_context', '$BUILD_DIR/mongo/db/service_context_test_fixture', + '$BUILD_DIR/mongo/db/s/sharding_runtime_d', '$BUILD_DIR/mongo/dbtests/mocklib', '$BUILD_DIR/mongo/s/catalog/sharding_catalog_client_mock', '$BUILD_DIR/mongo/util/net/network', diff --git a/src/mongo/s/catalog/type_shard_collection.cpp b/src/mongo/s/catalog/type_shard_collection.cpp index 354eec5262f..bc8b3576ee1 100644 --- a/src/mongo/s/catalog/type_shard_collection.cpp +++ b/src/mongo/s/catalog/type_shard_collection.cpp @@ -27,214 +27,46 @@ * it in the license file. */ -#include "mongo/platform/basic.h" - #include "mongo/s/catalog/type_shard_collection.h" -#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/s/catalog/type_collection.h" -#include "mongo/util/assert_util.h" - namespace mongo { -const BSONField<std::string> ShardCollectionType::ns("_id"); -const BSONField<UUID> ShardCollectionType::uuid("uuid"); -const BSONField<OID> ShardCollectionType::epoch("epoch"); -const BSONField<BSONObj> ShardCollectionType::keyPattern("key"); -const BSONField<BSONObj> ShardCollectionType::defaultCollation("defaultCollation"); -const BSONField<bool> ShardCollectionType::unique("unique"); -const BSONField<bool> ShardCollectionType::refreshing("refreshing"); -const BSONField<Date_t> ShardCollectionType::lastRefreshedCollectionVersion( - "lastRefreshedCollectionVersion"); -const BSONField<int> ShardCollectionType::enterCriticalSectionCounter( - "enterCriticalSectionCounter"); - -ShardCollectionType::ShardCollectionType(NamespaceString nss, - boost::optional<UUID> uuid, - OID epoch, - const KeyPattern& keyPattern, - const BSONObj& defaultCollation, - bool unique) - : _nss(std::move(nss)), - _uuid(uuid), - _epoch(std::move(epoch)), - _keyPattern(keyPattern.toBSON()), - _defaultCollation(defaultCollation.getOwned()), - _unique(unique) {} +ShardCollectionType::ShardCollectionType(const BSONObj& obj) { + ShardCollectionTypeBase::parseProtected(IDLParserErrorContext("ShardCollectionType"), obj); -StatusWith<ShardCollectionType> ShardCollectionType::fromBSON(const BSONObj& source) { + uassert(ErrorCodes::ShardKeyNotFound, + str::stream() << "Empty shard key. Failed to parse: " << obj.toString(), + !getKeyPattern().toBSON().isEmpty()); - NamespaceString nss; - { - std::string ns; - Status status = bsonExtractStringField(source, ShardCollectionType::ns.name(), &ns); - if (!status.isOK()) { - return status; - } - nss = NamespaceString{ns}; - } - - boost::optional<UUID> uuid; - { - BSONElement uuidElem; - Status status = bsonExtractTypedField( - source, ShardCollectionType::uuid.name(), BSONType::BinData, &uuidElem); - if (status.isOK()) { - auto uuidWith = UUID::parse(uuidElem); - if (!uuidWith.isOK()) - return uuidWith.getStatus(); - uuid = uuidWith.getValue(); - } else if (status == ErrorCodes::NoSuchKey) { - // The field is not set, which is okay. - } else { - return status; - } - } - - OID epoch; - { - BSONElement oidElem; - Status status = bsonExtractTypedField( - source, ShardCollectionType::epoch.name(), BSONType::jstOID, &oidElem); - if (!status.isOK()) - return status; - epoch = oidElem.OID(); - } - - BSONElement collKeyPattern; - Status status = bsonExtractTypedField( - source, ShardCollectionType::keyPattern.name(), Object, &collKeyPattern); - if (!status.isOK()) { - return status; - } - BSONObj obj = collKeyPattern.Obj(); - if (obj.isEmpty()) { - return Status(ErrorCodes::ShardKeyNotFound, - str::stream() << "Empty shard key. Failed to parse: " << source.toString()); - } - KeyPattern pattern(obj.getOwned()); - - BSONObj collation; - { - BSONElement defaultCollation; - Status status = bsonExtractTypedField( - source, ShardCollectionType::defaultCollation.name(), Object, &defaultCollation); - if (status.isOK()) { - BSONObj obj = defaultCollation.Obj(); - if (obj.isEmpty()) { - return Status(ErrorCodes::BadValue, "empty defaultCollation"); - } - - collation = obj.getOwned(); - } else if (status != ErrorCodes::NoSuchKey) { - return status; - } - } - - bool unique; - { - Status status = - bsonExtractBooleanField(source, ShardCollectionType::unique.name(), &unique); - if (!status.isOK()) { - return status; - } - } - - ShardCollectionType shardCollectionType( - std::move(nss), uuid, std::move(epoch), pattern, collation, unique); - - // Below are optional fields. - - { - bool refreshing; - Status status = - bsonExtractBooleanField(source, ShardCollectionType::refreshing.name(), &refreshing); - if (status.isOK()) { - shardCollectionType.setRefreshing(refreshing); - } else if (status == ErrorCodes::NoSuchKey) { - // The field is not set yet, which is okay. - } else { - return status; - } + // Last refreshed collection version is stored as a timestamp in the BSON representation of + // shard collection type for legacy reasons. We therefore explicitly convert this timestamp, if + // it exists, into a chunk version. + if (getLastRefreshedCollectionVersion()) { + ChunkVersion version = *getLastRefreshedCollectionVersion(); + setLastRefreshedCollectionVersion( + ChunkVersion(version.majorVersion(), version.minorVersion(), getEpoch())); } +} - { - if (!source[lastRefreshedCollectionVersion.name()].eoo()) { - auto statusWithLastRefreshedCollectionVersion = - ChunkVersion::parseLegacyWithField(source, lastRefreshedCollectionVersion()); - if (!statusWithLastRefreshedCollectionVersion.isOK()) { - return statusWithLastRefreshedCollectionVersion.getStatus(); - } - auto version = std::move(statusWithLastRefreshedCollectionVersion.getValue()); - shardCollectionType.setLastRefreshedCollectionVersion( - ChunkVersion(version.majorVersion(), version.minorVersion(), epoch)); - } +StatusWith<ShardCollectionType> ShardCollectionType::fromBSON(const BSONObj& obj) { + try { + return ShardCollectionType(obj); + } catch (const DBException& ex) { + return ex.toStatus(); } - - return shardCollectionType; } BSONObj ShardCollectionType::toBSON() const { - BSONObjBuilder builder; + BSONObj obj = ShardCollectionTypeBase::toBSON(); - builder.append(ns.name(), _nss.ns()); - if (_uuid) { - _uuid->appendToBuilder(&builder, uuid.name()); + // Default collation is not included in the BSON representation of shard collection type, and + // thus persisted to disk, if it is empty. We therefore explicitly remove default collation from + // obj, if it is empty. + if (getDefaultCollation().isEmpty()) { + obj = obj.removeField(kDefaultCollationFieldName); } - builder.append(epoch.name(), _epoch); - builder.append(keyPattern.name(), _keyPattern.toBSON()); - - if (!_defaultCollation.isEmpty()) { - builder.append(defaultCollation.name(), _defaultCollation); - } - - builder.append(unique.name(), _unique); - - if (_refreshing) { - builder.append(refreshing.name(), _refreshing.get()); - } - if (_lastRefreshedCollectionVersion) { - builder.appendTimestamp(lastRefreshedCollectionVersion.name(), - _lastRefreshedCollectionVersion->toLong()); - } - - return builder.obj(); -} - -std::string ShardCollectionType::toString() const { - return toBSON().toString(); -} - -void ShardCollectionType::setUUID(UUID uuid) { - _uuid = uuid; -} - -void ShardCollectionType::setNss(NamespaceString nss) { - invariant(nss.isValid()); - _nss = std::move(nss); -} - -void ShardCollectionType::setEpoch(OID epoch) { - invariant(epoch.isSet()); - _epoch = std::move(epoch); -} - -void ShardCollectionType::setKeyPattern(const KeyPattern& keyPattern) { - invariant(!keyPattern.toBSON().isEmpty()); - _keyPattern = keyPattern; -} - -bool ShardCollectionType::getRefreshing() const { - invariant(_refreshing); - return _refreshing.get(); -} -const ChunkVersion& ShardCollectionType::getLastRefreshedCollectionVersion() const { - invariant(_lastRefreshedCollectionVersion); - return _lastRefreshedCollectionVersion.get(); + return obj; } } // namespace mongo diff --git a/src/mongo/s/catalog/type_shard_collection.h b/src/mongo/s/catalog/type_shard_collection.h index 42d54052f07..95ec62e7775 100644 --- a/src/mongo/s/catalog/type_shard_collection.h +++ b/src/mongo/s/catalog/type_shard_collection.h @@ -29,158 +29,58 @@ #pragma once -#include <boost/optional.hpp> -#include <string> - -#include "mongo/db/jsobj.h" -#include "mongo/db/keypattern.h" -#include "mongo/db/namespace_string.h" -#include "mongo/s/chunk_version.h" -#include "mongo/util/uuid.h" +#include "mongo/s/catalog/type_shard_collection_gen.h" namespace mongo { -class CollectionType; -class Status; -template <typename T> -class StatusWith; - -/** - * This class represents the layout and contents of documents contained in the shard server's - * config.collections collection. All manipulation of documents coming from that collection should - * be done with this class. - * - * Expected shard server config.collections collection format: - * { - * "_id" : "foo.bar", - * "uuid" : UUID, // optional in 3.6 - * "epoch" : ObjectId("58b6fd76132358839e409e47"), // will remove when UUID becomes available - * "key" : { - * "_id" : 1 - * }, - * "defaultCollation" : { - * "locale" : "fr_CA" - * }, - * "unique" : false, - * "refreshing" : true, // optional - * "lastRefreshedCollectionVersion" : Timestamp(1, 0), // optional - * "enterCriticalSectionCounter" : 4 // optional - * } - * - * enterCriticalSectionCounter is currently just an OpObserver signal, thus otherwise ignored here. - */ -class ShardCollectionType { +class ShardCollectionType : private ShardCollectionTypeBase { public: - static const BSONField<std::string> ns; // "_id" - static const BSONField<UUID> uuid; - static const BSONField<OID> epoch; - static const BSONField<BSONObj> keyPattern; - static const BSONField<BSONObj> defaultCollation; - static const BSONField<bool> unique; - static const BSONField<bool> refreshing; - static const BSONField<Date_t> lastRefreshedCollectionVersion; - static const BSONField<int> enterCriticalSectionCounter; - - ShardCollectionType(NamespaceString nss, - boost::optional<UUID> uuid, - OID epoch, - const KeyPattern& keyPattern, - const BSONObj& defaultCollation, - bool unique); - - /** - * Constructs a new ShardCollectionType object from BSON. Also does validation of the contents. - */ - static StatusWith<ShardCollectionType> fromBSON(const BSONObj& source); - - /** - * Returns the BSON representation of this shard collection type object. - */ - BSONObj toBSON() const; - - /** - * Returns a std::string representation of the current internal state. - */ - std::string toString() const; - - const NamespaceString& getNss() const { - return _nss; + // Make field names accessible. + using ShardCollectionTypeBase::kDefaultCollationFieldName; + using ShardCollectionTypeBase::kEnterCriticalSectionCounterFieldName; + using ShardCollectionTypeBase::kEpochFieldName; + using ShardCollectionTypeBase::kKeyPatternFieldName; + using ShardCollectionTypeBase::kLastRefreshedCollectionVersionFieldName; + using ShardCollectionTypeBase::kNssFieldName; + using ShardCollectionTypeBase::kRefreshingFieldName; + using ShardCollectionTypeBase::kUniqueFieldName; + using ShardCollectionTypeBase::kUuidFieldName; + + // Make getters and setters accessible. + using ShardCollectionTypeBase::getNss; + using ShardCollectionTypeBase::setNss; + using ShardCollectionTypeBase::getUuid; + using ShardCollectionTypeBase::setUuid; + using ShardCollectionTypeBase::getEpoch; + using ShardCollectionTypeBase::setEpoch; + using ShardCollectionTypeBase::getKeyPattern; + using ShardCollectionTypeBase::setKeyPattern; + using ShardCollectionTypeBase::getDefaultCollation; + using ShardCollectionTypeBase::setDefaultCollation; + using ShardCollectionTypeBase::getUnique; + using ShardCollectionTypeBase::setUnique; + using ShardCollectionTypeBase::getRefreshing; + using ShardCollectionTypeBase::setRefreshing; + using ShardCollectionTypeBase::getLastRefreshedCollectionVersion; + using ShardCollectionTypeBase::setLastRefreshedCollectionVersion; + using ShardCollectionTypeBase::getEnterCriticalSectionCounter; + using ShardCollectionTypeBase::setEnterCriticalSectionCounter; + + ShardCollectionType() : ShardCollectionTypeBase() {} + + ShardCollectionType(NamespaceString nss, OID epoch, KeyPattern keyPattern, bool unique) + : ShardCollectionTypeBase(std::move(nss), std::move(epoch), std::move(keyPattern), unique) { } - void setNss(NamespaceString nss); - const boost::optional<UUID> getUUID() const { - return _uuid; - } - void setUUID(UUID uuid); + explicit ShardCollectionType(const BSONObj& obj); - const OID& getEpoch() const { - return _epoch; - } - void setEpoch(OID epoch); + // A wrapper around the IDL generated 'ShardCollectionTypeBase::parse' to ensure backwards + // compatibility. + static StatusWith<ShardCollectionType> fromBSON(const BSONObj& obj); - const KeyPattern& getKeyPattern() const { - return _keyPattern; - } - void setKeyPattern(const KeyPattern& keyPattern); - - const BSONObj& getDefaultCollation() const { - return _defaultCollation; - } - void setDefaultCollation(const BSONObj& collation) { - _defaultCollation = collation.getOwned(); - } - - bool getUnique() const { - return _unique; - } - void setUnique(bool unique) { - _unique = unique; - } - - bool hasRefreshing() const { - return _refreshing.is_initialized(); - } - bool getRefreshing() const; - void setRefreshing(bool refreshing) { - _refreshing = refreshing; - } - - bool hasLastRefreshedCollectionVersion() const { - return _lastRefreshedCollectionVersion.is_initialized(); - } - const ChunkVersion& getLastRefreshedCollectionVersion() const; - void setLastRefreshedCollectionVersion(const ChunkVersion& version) { - _lastRefreshedCollectionVersion = version; - } - -private: - // The full namespace (with the database prefix). - NamespaceString _nss; - - // The UUID of the collection, if known. - boost::optional<UUID> _uuid; - - // Uniquely identifies this instance of the collection, in case of drop/create. - OID _epoch; - - // Sharding key. If collection is dropped, this is no longer required. - KeyPattern _keyPattern; - - // Optional collection default collation. If empty, implies simple collation. - BSONObj _defaultCollation; - - // Uniqueness of the sharding key. - bool _unique; - - // Refresh fields set by primaries and used by shard secondaries to safely refresh chunk - // metadata. '_refreshing' indicates whether the chunks collection is currently being updated, - // which means read results won't provide a complete view of the chunk metadata. - // '_lastRefreshedCollectionVersion' indicates the collection version of the last complete chunk - // metadata refresh, and is used to indicate a refresh occurred if the value is different than - // when the caller last checked -- because 'refreshing' will be false both before and after a - // refresh occurs. - boost::optional<bool> _refreshing{boost::none}; - boost::optional<ChunkVersion> _lastRefreshedCollectionVersion{boost::none}; + // A wrapper around the IDL generated 'ShardCollectionTypeBase::toBSON' to ensure backwards + // compatibility. + BSONObj toBSON() const; }; } // namespace mongo diff --git a/src/mongo/s/catalog/type_shard_collection.idl b/src/mongo/s/catalog/type_shard_collection.idl new file mode 100644 index 00000000000..fadbebe7d4d --- /dev/null +++ b/src/mongo/s/catalog/type_shard_collection.idl @@ -0,0 +1,113 @@ +# Copyright (C) 2019-present MongoDB, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the Server Side Public License, version 1, +# as published by MongoDB, Inc. +# +# 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 +# Server Side Public License for more details. +# +# You should have received a copy of the Server Side Public License +# along with this program. If not, see +# <http://www.mongodb.com/licensing/server-side-public-license>. +# +# 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 Server Side 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. +# + +# ShardCollectionType type +# +# This type represents the layout and contents of documents contained in the shard server's +# config.collections collection. All manipulation of documents coming from that collection should +# be done with this class. +# +# Expected shard server config.collections collection format: +# { +# "_id" : "foo.bar", +# "uuid" : UUID, // optional in 3.6 +# "epoch" : ObjectId("58b6fd76132358839e409e47"), // will remove when UUID becomes available +# "key" : { +# "_id" : 1 +# }, +# "defaultCollation" : { +# "locale" : "fr_CA" +# }, +# "unique" : false, +# "refreshing" : true, // optional +# "lastRefreshedCollectionVersion" : Timestamp(1, 0), // optional +# "enterCriticalSectionCounter" : 4 // optional +# } +# + +global: + cpp_namespace: "mongo" + +imports: + - "mongo/idl/basic_types.idl" + - "mongo/db/keypattern.idl" + - "mongo/s/chunk_version.idl" + +structs: + ShardCollectionTypeBase: + description: "Represents the layout and contents of documents contained in the shard + server's config.collections collection." + fields: + _id: + cpp_name: nss + type: namespacestring + description: "The full namespace (with the database prefix)." + optional: false + uuid: + type: uuid + description: "The UUID of the collection, if known." + optional: true + epoch: + type: objectid + description: "Uniquely identifies this instance of the collection, in case of + drop/create." + optional: false + key: + cpp_name: keyPattern + type: KeyPattern + description: "Sharding key. If collection is dropped, this is no longer required." + optional: false + defaultCollation: + type: object_owned + default: BSONObj() + description: "Optional collection default collation. If empty, implies simple + collation." + optional: false + unique: + type: bool + description: "Uniqueness of the sharding key." + optional: false + refreshing: + type: bool + description: "Set by primaries and used by shard secondaries to safely refresh chunk + metadata. Indicates whether the chunks collection is current being + updated, which means read results won't provide a complete view of the + chunk metadata." + optional: true + lastRefreshedCollectionVersion: + type: ChunkVersionLegacy + description: "Set by primaries and used by shard secondaries to safely refresh chunk + metadata. Indicates the collection version of the last complete chunk + metadata refresh, and is used to indicate if a refresh occurred if the + value is different than when the caller last checked -- because + 'refreshing' will be false both before and after a refresh occurs." + optional: true + enterCriticalSectionCounter: + type: int + description: "Currently just an OpObserver signal, thus otherwise ignored." + optional: true diff --git a/src/mongo/s/catalog/type_shard_collection_test.cpp b/src/mongo/s/catalog/type_shard_collection_test.cpp index 85e3f5636cb..bde4643c186 100644 --- a/src/mongo/s/catalog/type_shard_collection_test.cpp +++ b/src/mongo/s/catalog/type_shard_collection_test.cpp @@ -29,11 +29,10 @@ #include "mongo/platform/basic.h" +#include "mongo/db/s/shard_metadata_util.h" #include "mongo/s/catalog/type_shard_collection.h" -#include "mongo/base/status_with.h" #include "mongo/bson/oid.h" -#include "mongo/s/catalog/type_collection.h" #include "mongo/unittest/unittest.h" #include "mongo/util/time_support.h" @@ -47,133 +46,63 @@ const BSONObj kKeyPattern = BSON("a" << 1); const BSONObj kDefaultCollation = BSON("locale" << "fr_CA"); -TEST(ShardCollectionType, ToFromBSON) { - const OID epoch = OID::gen(); - const UUID uuid = UUID::gen(); - const ChunkVersion lastRefreshedCollectionVersion(2, 0, epoch); - +TEST(ShardCollectionType, FromBSONEmptyShardKeyFails) { BSONObjBuilder builder; - builder.append(ShardCollectionType::ns.name(), kNss.ns()); - uuid.appendToBuilder(&builder, ShardCollectionType::uuid.name()); - builder.append(ShardCollectionType::epoch(), epoch); - builder.append(ShardCollectionType::keyPattern.name(), kKeyPattern); - builder.append(ShardCollectionType::defaultCollation(), kDefaultCollation); - builder.append(ShardCollectionType::unique(), true); - builder.append(ShardCollectionType::refreshing(), false); - builder.appendTimestamp(ShardCollectionType::lastRefreshedCollectionVersion(), - lastRefreshedCollectionVersion.toLong()); - BSONObj obj = builder.obj(); - - ShardCollectionType shardCollectionType = assertGet(ShardCollectionType::fromBSON(obj)); - - ASSERT_EQUALS(shardCollectionType.getNss(), kNss); - ASSERT(shardCollectionType.getUUID()); - ASSERT_EQUALS(*shardCollectionType.getUUID(), uuid); - ASSERT_EQUALS(shardCollectionType.getEpoch(), epoch); - ASSERT_BSONOBJ_EQ(shardCollectionType.getKeyPattern().toBSON(), kKeyPattern); - ASSERT_BSONOBJ_EQ(shardCollectionType.getDefaultCollation(), kDefaultCollation); - ASSERT_EQUALS(shardCollectionType.getUnique(), true); - ASSERT_EQUALS(shardCollectionType.getRefreshing(), false); - ASSERT_EQUALS(shardCollectionType.getLastRefreshedCollectionVersion(), - lastRefreshedCollectionVersion); - - ASSERT_BSONOBJ_EQ(obj, shardCollectionType.toBSON()); + builder.append(ShardCollectionType::kNssFieldName, kNss.ns()); + builder.append(ShardCollectionType::kEpochFieldName, OID::gen()); + builder.append(ShardCollectionType::kKeyPatternFieldName, BSONObj()); + builder.append(ShardCollectionType::kUniqueFieldName, true); + + StatusWith<ShardCollectionType> status = ShardCollectionType::fromBSON(builder.obj()); + ASSERT_EQ(status.getStatus().code(), ErrorCodes::ShardKeyNotFound); } -TEST(ShardCollectionType, ToFromShardBSONWithoutOptionals) { - const OID epoch = OID::gen(); +TEST(ShardCollectionType, FromBSONEpochMatchesLastRefreshedCollectionVersionWhenBSONTimestamp) { + OID epoch = OID::gen(); BSONObjBuilder builder; - builder.append(ShardCollectionType::ns.name(), kNss.ns()); - builder.append(ShardCollectionType::epoch(), epoch); - builder.append(ShardCollectionType::keyPattern.name(), kKeyPattern); - builder.append(ShardCollectionType::defaultCollation(), kDefaultCollation); - builder.append(ShardCollectionType::unique(), true); - BSONObj obj = builder.obj(); - - ShardCollectionType shardCollectionType = assertGet(ShardCollectionType::fromBSON(obj)); - - ASSERT_EQUALS(shardCollectionType.getNss(), kNss); - ASSERT_EQUALS(shardCollectionType.getEpoch(), epoch); - ASSERT_BSONOBJ_EQ(shardCollectionType.getKeyPattern().toBSON(), kKeyPattern); - ASSERT_BSONOBJ_EQ(shardCollectionType.getDefaultCollation(), kDefaultCollation); - ASSERT_EQUALS(shardCollectionType.getUnique(), true); - ASSERT_FALSE(shardCollectionType.hasRefreshing()); - ASSERT_FALSE(shardCollectionType.hasLastRefreshedCollectionVersion()); - - ASSERT_BSONOBJ_EQ(obj, shardCollectionType.toBSON()); -} + builder.append(ShardCollectionType::kNssFieldName, kNss.ns()); + builder.append(ShardCollectionType::kEpochFieldName, epoch); + builder.append(ShardCollectionType::kKeyPatternFieldName, kKeyPattern); + builder.append(ShardCollectionType::kUniqueFieldName, true); + builder.append(ShardCollectionType::kLastRefreshedCollectionVersionFieldName, Timestamp()); -TEST(ShardCollectionType, FromEmptyBSON) { - StatusWith<ShardCollectionType> status = ShardCollectionType::fromBSON(BSONObj()); - ASSERT_FALSE(status.isOK()); -} + ShardCollectionType shardCollType = assertGet(ShardCollectionType::fromBSON(builder.obj())); -TEST(ShardCollectionType, FromBSONNoUUIDIsOK) { - BSONObjBuilder builder; - builder.append(ShardCollectionType::ns.name(), kNss.ns()); - builder.append(ShardCollectionType::epoch(), OID::gen()); - builder.append(ShardCollectionType::keyPattern(), kKeyPattern); - builder.append(ShardCollectionType::unique(), true); - StatusWith<ShardCollectionType> status = ShardCollectionType::fromBSON(builder.obj()); - ASSERT_OK(status.getStatus()); - ASSERT_FALSE(status.getValue().getUUID()); + ASSERT_EQ(epoch, shardCollType.getLastRefreshedCollectionVersion()->epoch()); } -TEST(ShardCollectionType, FromBSONNoNSFails) { - BSONObjBuilder builder; - UUID::gen().appendToBuilder(&builder, ShardCollectionType::uuid.name()); - builder.append(ShardCollectionType::epoch(), OID::gen()); - builder.append(ShardCollectionType::keyPattern(), kKeyPattern); - builder.append(ShardCollectionType::unique(), true); - StatusWith<ShardCollectionType> status = ShardCollectionType::fromBSON(builder.obj()); - ASSERT_EQUALS(status.getStatus().code(), ErrorCodes::NoSuchKey); - ASSERT_STRING_CONTAINS(status.getStatus().reason(), ShardCollectionType::ns()); -} +TEST(ShardCollectionType, FromBSONEpochMatchesLastRefreshedCollectionVersionWhenDate) { + OID epoch = OID::gen(); -TEST(ShardCollectionType, FromBSONNoEpochFails) { BSONObjBuilder builder; - builder.append(ShardCollectionType::ns.name(), kNss.ns()); - UUID::gen().appendToBuilder(&builder, ShardCollectionType::uuid.name()); - builder.append(ShardCollectionType::keyPattern(), kKeyPattern); - builder.append(ShardCollectionType::unique(), true); - StatusWith<ShardCollectionType> status = ShardCollectionType::fromBSON(builder.obj()); - ASSERT_EQUALS(status.getStatus().code(), ErrorCodes::NoSuchKey); - ASSERT_STRING_CONTAINS(status.getStatus().reason(), ShardCollectionType::epoch()); -} + builder.append(ShardCollectionType::kNssFieldName, kNss.ns()); + builder.append(ShardCollectionType::kEpochFieldName, epoch); + builder.append(ShardCollectionType::kKeyPatternFieldName, kKeyPattern); + builder.append(ShardCollectionType::kUniqueFieldName, true); + builder.append(ShardCollectionType::kLastRefreshedCollectionVersionFieldName, Date_t()); -TEST(ShardCollectionType, FromBSONNoShardKeyFails) { - BSONObjBuilder builder; - builder.append(ShardCollectionType::ns.name(), kNss.ns()); - UUID::gen().appendToBuilder(&builder, ShardCollectionType::uuid.name()); - builder.append(ShardCollectionType::epoch(), OID::gen()); - builder.append(ShardCollectionType::unique(), true); - StatusWith<ShardCollectionType> status = ShardCollectionType::fromBSON(builder.obj()); - ASSERT_EQUALS(status.getStatus().code(), ErrorCodes::NoSuchKey); - ASSERT_STRING_CONTAINS(status.getStatus().reason(), ShardCollectionType::keyPattern()); -} + ShardCollectionType shardCollType = assertGet(ShardCollectionType::fromBSON(builder.obj())); -TEST(ShardCollectionType, FromBSONNoUniqueFails) { - BSONObjBuilder builder; - builder.append(ShardCollectionType::ns.name(), kNss.ns()); - UUID::gen().appendToBuilder(&builder, ShardCollectionType::uuid.name()); - builder.append(ShardCollectionType::epoch(), OID::gen()); - builder.append(ShardCollectionType::keyPattern.name(), kKeyPattern); - builder.append(ShardCollectionType::defaultCollation(), kDefaultCollation); - StatusWith<ShardCollectionType> status = ShardCollectionType::fromBSON(builder.obj()); - ASSERT_EQUALS(status.getStatus().code(), ErrorCodes::NoSuchKey); - ASSERT_STRING_CONTAINS(status.getStatus().reason(), ShardCollectionType::unique()); + ASSERT_EQ(epoch, shardCollType.getLastRefreshedCollectionVersion()->epoch()); } -TEST(ShardCollectionType, FromBSONNoDefaultCollationIsOK) { - BSONObjBuilder builder; - builder.append(ShardCollectionType::ns.name(), kNss.ns()); - UUID::gen().appendToBuilder(&builder, ShardCollectionType::uuid.name()); - builder.append(ShardCollectionType::epoch(), OID::gen()); - builder.append(ShardCollectionType::keyPattern.name(), kKeyPattern); - builder.append(ShardCollectionType::unique(), true); +TEST(ShardCollectionType, ToBSONEmptyDefaultCollationNotIncluded) { + ShardCollectionType shardCollType; + shardCollType.setNss(kNss); + shardCollType.setEpoch(OID::gen()); + shardCollType.setKeyPattern(kKeyPattern); + shardCollType.setUnique(true); + + shardCollType.setDefaultCollation(BSONObj()); + BSONObj obj = shardCollType.toBSON(); + + ASSERT_FALSE(obj.hasField(ShardCollectionType::kDefaultCollationFieldName)); + + shardCollType.setDefaultCollation(kDefaultCollation); + obj = shardCollType.toBSON(); - assertGet(ShardCollectionType::fromBSON(builder.obj())); + ASSERT_TRUE(obj.hasField(ShardCollectionType::kDefaultCollationFieldName)); } } // namespace diff --git a/src/mongo/s/chunk_version.cpp b/src/mongo/s/chunk_version.cpp index 62edcdb6404..3f25d474a4a 100644 --- a/src/mongo/s/chunk_version.cpp +++ b/src/mongo/s/chunk_version.cpp @@ -131,6 +131,10 @@ BSONObj ChunkVersion::toBSON() const { return b.arr(); } +void ChunkVersion::legacyToBSON(StringData field, BSONObjBuilder* out) const { + out->appendTimestamp(field, this->toLong()); +} + std::string ChunkVersion::toString() const { return str::stream() << majorVersion() << "|" << minorVersion() << "||" << _epoch; } diff --git a/src/mongo/s/chunk_version.h b/src/mongo/s/chunk_version.h index a0d7f09c1db..a77272d8f03 100644 --- a/src/mongo/s/chunk_version.h +++ b/src/mongo/s/chunk_version.h @@ -86,6 +86,16 @@ public: } /** + * NOTE: This format should not be used. Use fromBSONThrowing instead. + * + * A throwing version of 'parseLegacyWithField' to resolve a compatibility issue with the + * ShardCollectionType IDL type. + */ + static ChunkVersion legacyFromBSONThrowing(const BSONElement& element) { + return uassertStatusOK(parseLegacyWithField(element.wrap(), element.fieldNameStringData())); + } + + /** * NOTE: This format is being phased out. Use parseWithField instead. * * Parses the BSON formatted by appendLegacyWithField. If the field is missing, returns @@ -218,6 +228,12 @@ public: void appendLegacyWithField(BSONObjBuilder* out, StringData field) const; BSONObj toBSON() const; + + /** + * NOTE: This format serializes chunk version as a timestamp (without the epoch) for + * legacy reasons. + */ + void legacyToBSON(StringData field, BSONObjBuilder* builder) const; std::string toString() const; private: diff --git a/src/mongo/s/chunk_version.idl b/src/mongo/s/chunk_version.idl index 887767855ae..ee33893468b 100644 --- a/src/mongo/s/chunk_version.idl +++ b/src/mongo/s/chunk_version.idl @@ -40,3 +40,11 @@ types: cpp_type: ChunkVersion serializer: ChunkVersion::toBSON deserializer: ChunkVersion::fromBSONThrowing + + ChunkVersionLegacy: + bson_serialization_type: any + description: An object representing a chunk version for a collection. Ignores the epoch + component in the chunk version for legacy reasons. + cpp_type: ChunkVersion + serializer: ChunkVersion::legacyToBSON + deserializer: ChunkVersion::legacyFromBSONThrowing diff --git a/src/mongo/s/shard_key_pattern_test.cpp b/src/mongo/s/shard_key_pattern_test.cpp index 15a38fcee11..808ccfbe419 100644 --- a/src/mongo/s/shard_key_pattern_test.cpp +++ b/src/mongo/s/shard_key_pattern_test.cpp @@ -48,7 +48,7 @@ TEST(ShardKeyPattern, SingleFieldShardKeyPatternsValidityCheck) { ShardKeyPattern(BSON("a" << "hashed")); - ASSERT_THROWS(ShardKeyPattern({}), DBException); + ASSERT_THROWS(ShardKeyPattern(BSONObj()), DBException); ASSERT_THROWS(ShardKeyPattern(BSON("a" << -1)), DBException); ASSERT_THROWS(ShardKeyPattern(BSON("a" << -1.0)), DBException); ASSERT_THROWS(ShardKeyPattern(BSON("a" |