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 | |
parent | 75e47b701d34c9ef7beb0482121ac8b62d2d4991 (diff) | |
download | mongo-6e02a4d34bd972e6755bb5f71a5b26f69fe2cfb0.tar.gz |
SERVER-41658 Convert ShardCollectionType into an IDL type
-rw-r--r-- | src/mongo/bson/bsonobj.cpp | 4 | ||||
-rw-r--r-- | src/mongo/bson/bsonobj.h | 3 | ||||
-rw-r--r-- | src/mongo/db/keypattern.h | 15 | ||||
-rw-r--r-- | src/mongo/db/keypattern.idl | 42 | ||||
-rw-r--r-- | src/mongo/db/s/migration_source_manager.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/s/shard_metadata_util.cpp | 31 | ||||
-rw-r--r-- | src/mongo/db/s/shard_metadata_util_test.cpp | 44 | ||||
-rw-r--r-- | src/mongo/db/s/shard_server_catalog_cache_loader.cpp | 30 | ||||
-rw-r--r-- | src/mongo/db/s/shard_server_op_observer.cpp | 8 | ||||
-rw-r--r-- | src/mongo/idl/basic_types.idl | 8 | ||||
-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 | ||||
-rw-r--r-- | src/mongo/util/uuid.h | 1 |
20 files changed, 387 insertions, 518 deletions
diff --git a/src/mongo/bson/bsonobj.cpp b/src/mongo/bson/bsonobj.cpp index 57bbd59969b..cecc98f299c 100644 --- a/src/mongo/bson/bsonobj.cpp +++ b/src/mongo/bson/bsonobj.cpp @@ -110,6 +110,10 @@ BSONObj BSONObj::getOwned() const { return copy(); } +BSONObj BSONObj::getOwned(const BSONObj& obj) { + return obj.getOwned(); +} + std::string BSONObj::jsonString(JsonStringFormat format, int pretty, bool isArray) const { std::stringstream s; BSONObj::jsonStringStream(format, pretty, isArray, s); diff --git a/src/mongo/bson/bsonobj.h b/src/mongo/bson/bsonobj.h index 68354bb462b..9cefccb7d78 100644 --- a/src/mongo/bson/bsonobj.h +++ b/src/mongo/bson/bsonobj.h @@ -240,6 +240,9 @@ public: */ BSONObj getOwned() const; + /** Returns an owned copy of the given BSON object. */ + static BSONObj getOwned(const BSONObj& obj); + /** @return a new full (and owned) copy of the object. */ BSONObj copy() const; diff --git a/src/mongo/db/keypattern.h b/src/mongo/db/keypattern.h index d87bc8632c5..ac31301b7f2 100644 --- a/src/mongo/db/keypattern.h +++ b/src/mongo/db/keypattern.h @@ -29,6 +29,7 @@ #pragma once +#include "mongo/base/status.h" #include "mongo/base/string_data.h" #include "mongo/bson/util/builder.h" #include "mongo/db/jsobj.h" @@ -68,10 +69,20 @@ public: static bool isHashedKeyPattern(const BSONObj& pattern); /** - * Constructs a new key pattern based on a BSON document + * Constructs a new key pattern based on a BSON document. + * Used as an interface to the IDL parser. + */ + static KeyPattern fromBSON(const BSONObj& pattern) { + return KeyPattern(pattern.getOwned()); + } + + /** + * Constructs a new key pattern based on a BSON document. */ KeyPattern(const BSONObj& pattern); + explicit KeyPattern() = default; + /** * Returns a BSON representation of this KeyPattern. */ @@ -80,7 +91,7 @@ public: } /** - * Returns a string representation of this KeyPattern + * Returns a string representation of this KeyPattern. */ std::string toString() const { return str::stream() << *this; diff --git a/src/mongo/db/keypattern.idl b/src/mongo/db/keypattern.idl new file mode 100644 index 00000000000..158c742faab --- /dev/null +++ b/src/mongo/db/keypattern.idl @@ -0,0 +1,42 @@ +# 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. +# + +# KeyPattern IDL type definition + +global: + cpp_namespace: "mongo" + cpp_includes: + - "mongo/db/keypattern.h" + +types: + KeyPattern: + bson_serialization_type: object + description: An expression describing a transformation of a document into a document key. + cpp_type: KeyPattern + serializer: KeyPattern::toBSON + deserializer: KeyPattern::fromBSON diff --git a/src/mongo/db/s/migration_source_manager.cpp b/src/mongo/db/s/migration_source_manager.cpp index 4e9fb2fd0ff..ccd90364e31 100644 --- a/src/mongo/db/s/migration_source_manager.cpp +++ b/src/mongo/db/s/migration_source_manager.cpp @@ -342,12 +342,12 @@ Status MigrationSourceManager::enterCriticalSection(OperationContext* opCtx) { // time inclusive of the migration config commit update from accessing secondary data. // Note: this write must occur after the critSec flag is set, to ensure the secondary refresh // will stall behind the flag. - Status signalStatus = - updateShardCollectionsEntry(opCtx, - BSON(ShardCollectionType::ns() << getNss().ns()), - BSONObj(), - BSON(ShardCollectionType::enterCriticalSectionCounter() << 1), - false /*upsert*/); + Status signalStatus = updateShardCollectionsEntry( + opCtx, + BSON(ShardCollectionType::kNssFieldName << getNss().ns()), + BSONObj(), + BSON(ShardCollectionType::kEnterCriticalSectionCounterFieldName << 1), + false /*upsert*/); if (!signalStatus.isOK()) { return { ErrorCodes::OperationFailed, diff --git a/src/mongo/db/s/shard_metadata_util.cpp b/src/mongo/db/s/shard_metadata_util.cpp index 59b6a5562f3..627f955fd67 100644 --- a/src/mongo/db/s/shard_metadata_util.cpp +++ b/src/mongo/db/s/shard_metadata_util.cpp @@ -94,12 +94,12 @@ Status unsetPersistedRefreshFlags(OperationContext* opCtx, const ChunkVersion& refreshedVersion) { // Set 'refreshing' to false and update the last refreshed collection version. BSONObjBuilder updateBuilder; - updateBuilder.append(ShardCollectionType::refreshing(), false); - updateBuilder.appendTimestamp(ShardCollectionType::lastRefreshedCollectionVersion(), + updateBuilder.append(ShardCollectionType::kRefreshingFieldName, false); + updateBuilder.appendTimestamp(ShardCollectionType::kLastRefreshedCollectionVersionFieldName, refreshedVersion.toLong()); return updateShardCollectionsEntry(opCtx, - BSON(ShardCollectionType::ns() << nss.ns()), + BSON(ShardCollectionType::kNssFieldName << nss.ns()), updateBuilder.obj(), BSONObj(), false /*upsert*/); @@ -114,14 +114,14 @@ StatusWith<RefreshState> getPersistedRefreshFlags(OperationContext* opCtx, ShardCollectionType entry = statusWithCollectionEntry.getValue(); // Ensure the results have not been incorrectly set somehow. - if (entry.hasRefreshing()) { + if (entry.getRefreshing()) { // If 'refreshing' is present and false, a refresh must have occurred (otherwise the field // would never have been added to the document) and there should always be a refresh // version. - invariant(entry.getRefreshing() ? true : entry.hasLastRefreshedCollectionVersion()); + invariant(*entry.getRefreshing() ? true : !!entry.getLastRefreshedCollectionVersion()); } else { // If 'refreshing' is not present, no refresh version should exist. - invariant(!entry.hasLastRefreshedCollectionVersion()); + invariant(!entry.getLastRefreshedCollectionVersion()); } return RefreshState{entry.getEpoch(), @@ -129,16 +129,16 @@ StatusWith<RefreshState> getPersistedRefreshFlags(OperationContext* opCtx, // refresh has started, but no chunks have ever yet been applied, around // which these flags are set. So default to refreshing true because the // chunk metadata is being updated and is not yet ready to be read. - entry.hasRefreshing() ? entry.getRefreshing() : true, - entry.hasLastRefreshedCollectionVersion() - ? entry.getLastRefreshedCollectionVersion() + entry.getRefreshing() ? *entry.getRefreshing() : true, + entry.getLastRefreshedCollectionVersion() + ? *entry.getLastRefreshedCollectionVersion() : ChunkVersion(0, 0, entry.getEpoch())}; } StatusWith<ShardCollectionType> readShardCollectionsEntry(OperationContext* opCtx, const NamespaceString& nss) { - Query fullQuery(BSON(ShardCollectionType::ns() << nss.ns())); + Query fullQuery(BSON(ShardCollectionType::kNssFieldName << nss.ns())); try { DBDirectClient client(opCtx); @@ -158,12 +158,7 @@ StatusWith<ShardCollectionType> readShardCollectionsEntry(OperationContext* opCt } BSONObj document = cursor->nextSafe(); - auto statusWithCollectionEntry = ShardCollectionType::fromBSON(document); - if (!statusWithCollectionEntry.isOK()) { - return statusWithCollectionEntry.getStatus(); - } - - return statusWithCollectionEntry.getValue(); + return ShardCollectionType::fromBSON(document); } catch (const DBException& ex) { return ex.toStatus(str::stream() << "Failed to read the '" << nss.ns() << "' entry locally from config.collections"); @@ -212,7 +207,7 @@ Status updateShardCollectionsEntry(OperationContext* opCtx, if (upsert) { // If upserting, this should be an update from the config server that does not have shard // refresh / migration inc signal information. - invariant(!update.hasField(ShardCollectionType::lastRefreshedCollectionVersion())); + invariant(!update.hasField(ShardCollectionType::kLastRefreshedCollectionVersionFieldName)); invariant(inc.isEmpty()); } @@ -411,7 +406,7 @@ Status dropChunksAndDeleteCollectionsEntry(OperationContext* opCtx, const Namesp write_ops::Delete deleteOp(NamespaceString::kShardConfigCollectionsNamespace); deleteOp.setDeletes({[&] { write_ops::DeleteOpEntry entry; - entry.setQ(BSON(ShardCollectionType::ns << nss.ns())); + entry.setQ(BSON(ShardCollectionType::kNssFieldName << nss.ns())); entry.setMulti(true); return entry; }()}); diff --git a/src/mongo/db/s/shard_metadata_util_test.cpp b/src/mongo/db/s/shard_metadata_util_test.cpp index 3776b6e89e9..86bf071f3ac 100644 --- a/src/mongo/db/s/shard_metadata_util_test.cpp +++ b/src/mongo/db/s/shard_metadata_util_test.cpp @@ -44,11 +44,12 @@ namespace mongo { namespace { +using namespace shardmetadatautil; + using std::string; using std::unique_ptr; using std::vector; using unittest::assertGet; -using namespace shardmetadatautil; const NamespaceString kNss = NamespaceString("test.foo"); const NamespaceString kChunkMetadataNss = NamespaceString("config.cache.chunks.test.foo"); @@ -61,22 +62,23 @@ struct ShardMetadataUtilTest : public ShardServerTestFixture { */ ShardCollectionType setUpCollection() { BSONObjBuilder builder; - builder.append(ShardCollectionType::ns(), kNss.ns()); - uuid.appendToBuilder(&builder, ShardCollectionType::uuid()); - builder.append(ShardCollectionType::epoch(), maxCollVersion.epoch()); - builder.append(ShardCollectionType::keyPattern(), keyPattern.toBSON()); - builder.append(ShardCollectionType::defaultCollation(), defaultCollation); - builder.append(ShardCollectionType::unique(), kUnique); + builder.append(ShardCollectionType::kNssFieldName, kNss.ns()); + builder.append(ShardCollectionType::kEpochFieldName, maxCollVersion.epoch()); + builder.append(ShardCollectionType::kKeyPatternFieldName, keyPattern.toBSON()); + builder.append(ShardCollectionType::kDefaultCollationFieldName, defaultCollation); + builder.append(ShardCollectionType::kUniqueFieldName, kUnique); + ShardCollectionType shardCollectionType = assertGet(ShardCollectionType::fromBSON(builder.obj())); - + shardCollectionType.setUuid(uuid); shardCollectionType.setRefreshing(true); ASSERT_OK(updateShardCollectionsEntry(operationContext(), - BSON(ShardCollectionType::ns(kNss.ns())), + BSON(ShardCollectionType::kNssFieldName << kNss.ns()), shardCollectionType.toBSON(), BSONObj(), true /*upsert*/)); + return shardCollectionType; } @@ -175,8 +177,8 @@ TEST_F(ShardMetadataUtilTest, UpdateAndReadCollectionsEntry) { ShardCollectionType readShardCollectionType = assertGet(readShardCollectionsEntry(operationContext(), kNss)); - ASSERT_TRUE(readShardCollectionType.getUUID()); - ASSERT_EQUALS(*updateShardCollectionType.getUUID(), *readShardCollectionType.getUUID()); + ASSERT(readShardCollectionType.getUuid()); + ASSERT_EQUALS(*updateShardCollectionType.getUuid(), *readShardCollectionType.getUuid()); ASSERT_EQUALS(updateShardCollectionType.getNss(), readShardCollectionType.getNss()); ASSERT_EQUALS(updateShardCollectionType.getEpoch(), readShardCollectionType.getEpoch()); ASSERT_BSONOBJ_EQ(updateShardCollectionType.getKeyPattern().toBSON(), @@ -184,12 +186,12 @@ TEST_F(ShardMetadataUtilTest, UpdateAndReadCollectionsEntry) { ASSERT_BSONOBJ_EQ(updateShardCollectionType.getDefaultCollation(), readShardCollectionType.getDefaultCollation()); ASSERT_EQUALS(updateShardCollectionType.getUnique(), readShardCollectionType.getUnique()); - ASSERT_EQUALS(updateShardCollectionType.hasRefreshing(), - readShardCollectionType.hasRefreshing()); + ASSERT_EQUALS(*updateShardCollectionType.getRefreshing(), + *readShardCollectionType.getRefreshing()); // Refresh fields should not have been set. - ASSERT(!updateShardCollectionType.hasLastRefreshedCollectionVersion()); - ASSERT(!readShardCollectionType.hasLastRefreshedCollectionVersion()); + ASSERT(!updateShardCollectionType.getLastRefreshedCollectionVersion()); + ASSERT(!readShardCollectionType.getLastRefreshedCollectionVersion()); } TEST_F(ShardMetadataUtilTest, PersistedRefreshSignalStartAndFinish) { @@ -198,19 +200,19 @@ TEST_F(ShardMetadataUtilTest, PersistedRefreshSignalStartAndFinish) { ShardCollectionType shardCollectionsEntry = assertGet(readShardCollectionsEntry(operationContext(), kNss)); - ASSERT_EQUALS(*shardCollectionsEntry.getUUID(), uuid); + ASSERT_EQUALS(*shardCollectionsEntry.getUuid(), uuid); ASSERT_EQUALS(shardCollectionsEntry.getNss().ns(), kNss.ns()); ASSERT_EQUALS(shardCollectionsEntry.getEpoch(), maxCollVersion.epoch()); ASSERT_BSONOBJ_EQ(shardCollectionsEntry.getKeyPattern().toBSON(), keyPattern.toBSON()); ASSERT_BSONOBJ_EQ(shardCollectionsEntry.getDefaultCollation(), defaultCollation); ASSERT_EQUALS(shardCollectionsEntry.getUnique(), kUnique); - ASSERT_EQUALS(shardCollectionsEntry.getRefreshing(), true); - ASSERT(!shardCollectionsEntry.hasLastRefreshedCollectionVersion()); + ASSERT_EQUALS(*shardCollectionsEntry.getRefreshing(), true); + ASSERT(!shardCollectionsEntry.getLastRefreshedCollectionVersion()); // Signal refresh start again to make sure nothing changes ASSERT_OK(updateShardCollectionsEntry(operationContext(), - BSON(ShardCollectionType::ns() << kNss.ns()), - BSON(ShardCollectionType::refreshing() << true), + BSON(ShardCollectionType::kNssFieldName << kNss.ns()), + BSON(ShardCollectionType::kRefreshingFieldName << true), BSONObj(), false)); @@ -259,7 +261,7 @@ TEST_F(ShardMetadataUtilTest, WriteAndReadChunks) { boost::none, maxCollVersion.epoch())); - ASSERT_TRUE(readChunks.size() == 1); + ASSERT(readChunks.size() == 1); ASSERT_BSONOBJ_EQ(chunks.back().toShardBSON(), readChunks.front().toShardBSON()); } diff --git a/src/mongo/db/s/shard_server_catalog_cache_loader.cpp b/src/mongo/db/s/shard_server_catalog_cache_loader.cpp index 7a87d9c9ed0..81142fd65ef 100644 --- a/src/mongo/db/s/shard_server_catalog_cache_loader.cpp +++ b/src/mongo/db/s/shard_server_catalog_cache_loader.cpp @@ -85,21 +85,23 @@ Status persistCollectionAndChangedChunks(OperationContext* opCtx, const NamespaceString& nss, const CollectionAndChangedChunks& collAndChunks) { // Update the collections collection entry for 'nss' in case there are any new updates. - ShardCollectionType update = ShardCollectionType(nss, - collAndChunks.uuid, - collAndChunks.epoch, - collAndChunks.shardKeyPattern, - collAndChunks.defaultCollation, - collAndChunks.shardKeyIsUnique); + ShardCollectionType update = ShardCollectionType( + nss, collAndChunks.epoch, collAndChunks.shardKeyPattern, collAndChunks.shardKeyIsUnique); + + update.setUuid(collAndChunks.uuid); + if (!collAndChunks.defaultCollation.isEmpty()) { + update.setDefaultCollation(collAndChunks.defaultCollation.getOwned()); + } // Mark the chunk metadata as refreshing, so that secondaries are aware of refresh. update.setRefreshing(true); - Status status = updateShardCollectionsEntry(opCtx, - BSON(ShardCollectionType::ns() << nss.ns()), - update.toBSON(), - BSONObj(), - true /*upsert*/); + Status status = + updateShardCollectionsEntry(opCtx, + BSON(ShardCollectionType::kNssFieldName << nss.ns()), + update.toBSON(), + BSONObj(), + true /*upsert*/); if (!status.isOK()) { return status; } @@ -211,7 +213,7 @@ CollectionAndChangedChunks getPersistedMetadataSinceVersion(OperationContext* op auto changedChunks = uassertStatusOK( readShardChunks(opCtx, nss, diff.query, diff.sort, boost::none, startingVersion.epoch())); - return CollectionAndChangedChunks{shardCollectionEntry.getUUID(), + return CollectionAndChangedChunks{shardCollectionEntry.getUuid(), shardCollectionEntry.getEpoch(), shardCollectionEntry.getKeyPattern().toBSON(), shardCollectionEntry.getDefaultCollation(), @@ -1040,7 +1042,7 @@ void ShardServerCatalogCacheLoader::_updatePersistedCollAndChunksMetadata( } uassertStatusOKWithContext( - persistCollectionAndChangedChunks(opCtx, nss, task.collectionAndChangedChunks.get()), + persistCollectionAndChangedChunks(opCtx, nss, *task.collectionAndChangedChunks), str::stream() << "Failed to update the persisted chunk metadata for collection '" << nss.ns() << "' from '" @@ -1271,7 +1273,7 @@ ShardServerCatalogCacheLoader::CollAndChunkTaskList::getEnqueuedMetadataForTerm( } else { if (task.collectionAndChangedChunks->epoch != collAndChunks.epoch) { // An epoch change should reset the metadata and start from the new. - collAndChunks = task.collectionAndChangedChunks.get(); + collAndChunks = *task.collectionAndChangedChunks; } else { // Epochs match, so the new results should be appended. diff --git a/src/mongo/db/s/shard_server_op_observer.cpp b/src/mongo/db/s/shard_server_op_observer.cpp index 9969d9c9f30..aa1ec89d5ec 100644 --- a/src/mongo/db/s/shard_server_op_observer.cpp +++ b/src/mongo/db/s/shard_server_op_observer.cpp @@ -133,7 +133,7 @@ void onConfigDeleteInvalidateCachedCollectionMetadataAndNotify(OperationContext* // Extract which collection entry is being deleted from the _id field. std::string deletedCollection; fassert(40479, - bsonExtractStringField(query, ShardCollectionType::ns.name(), &deletedCollection)); + bsonExtractStringField(query, ShardCollectionType::kNssFieldName, &deletedCollection)); const NamespaceString deletedNss(deletedCollection); // Need the WUOW to retain the lock for CollectionVersionLogOpHandler::commit(). @@ -263,7 +263,7 @@ void ShardServerOpObserver::onUpdate(OperationContext* opCtx, const OplogUpdateE std::string coll; fassert(40477, bsonExtractStringField( - args.updateArgs.criteria, ShardCollectionType::ns.name(), &coll)); + args.updateArgs.criteria, ShardCollectionType::kNssFieldName, &coll)); return NamespaceString(coll); }()); @@ -277,13 +277,13 @@ void ShardServerOpObserver::onUpdate(OperationContext* opCtx, const OplogUpdateE // Need the WUOW to retain the lock for CollectionVersionLogOpHandler::commit() AutoGetCollection autoColl(opCtx, updatedNss, MODE_IX); - if (setField.hasField(ShardCollectionType::refreshing.name()) && + if (setField.hasField(ShardCollectionType::kLastRefreshedCollectionVersionFieldName) && !setField.getBoolField("refreshing")) { opCtx->recoveryUnit()->registerChange( new CollectionVersionLogOpHandler(opCtx, updatedNss)); } - if (setField.hasField(ShardCollectionType::enterCriticalSectionCounter.name())) { + if (setField.hasField(ShardCollectionType::kEnterCriticalSectionCounterFieldName)) { // Force subsequent uses of the namespace to refresh the filtering metadata so they // can synchronize with any work happening on the primary (e.g., migration critical // section). diff --git a/src/mongo/idl/basic_types.idl b/src/mongo/idl/basic_types.idl index 7005fdc4bc3..7ebee57fb3f 100644 --- a/src/mongo/idl/basic_types.idl +++ b/src/mongo/idl/basic_types.idl @@ -136,9 +136,15 @@ types: object: bson_serialization_type: object - description: "A BSONObj without custom deserialization or serialization" + description: "An unowned BSONObj without custom deserialization or serialization" cpp_type: "mongo::BSONObj" + object_owned: + bson_serialization_type: object + description: "An owned BSONObj" + cpp_type: "mongo::BSONObj" + deserializer: "BSONObj::getOwned" + date: bson_serialization_type: date description: "A BSON UTC DateTime" 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" diff --git a/src/mongo/util/uuid.h b/src/mongo/util/uuid.h index 69e72d479fe..2553e6383f2 100644 --- a/src/mongo/util/uuid.h +++ b/src/mongo/util/uuid.h @@ -78,6 +78,7 @@ class UUID { friend class repl::OplogEntryBase; friend class repl::DurableReplOperation; friend class ResumeTokenInternal; + friend class ShardCollectionTypeBase; friend class VoteCommitIndexBuild; public: |