diff options
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/catalog/coll_mod.cpp | 70 | ||||
-rw-r--r-- | src/mongo/db/catalog/coll_mod.h | 11 | ||||
-rw-r--r-- | src/mongo/db/db.cpp | 16 | ||||
-rw-r--r-- | src/mongo/db/repl/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/repl/initial_syncer.cpp | 24 | ||||
-rw-r--r-- | src/mongo/db/repl/initial_syncer_test.cpp | 108 | ||||
-rw-r--r-- | src/mongo/db/repl/replication_coordinator_external_state_impl.cpp | 16 | ||||
-rw-r--r-- | src/mongo/db/repl/storage_interface.h | 12 | ||||
-rw-r--r-- | src/mongo/db/repl/storage_interface_impl.cpp | 18 | ||||
-rw-r--r-- | src/mongo/db/repl/storage_interface_impl.h | 5 | ||||
-rw-r--r-- | src/mongo/db/repl/storage_interface_impl_test.cpp | 97 | ||||
-rw-r--r-- | src/mongo/db/repl/storage_interface_mock.h | 23 |
12 files changed, 363 insertions, 38 deletions
diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp index 9badcf39050..cc58787cead 100644 --- a/src/mongo/db/catalog/coll_mod.cpp +++ b/src/mongo/db/catalog/coll_mod.cpp @@ -443,10 +443,10 @@ Status _collModInternal(OperationContext* opCtx, return Status::OK(); } -void _updateDBSchemaVersion(OperationContext* opCtx, - const std::string& dbname, - std::map<std::string, UUID>& collToUUID, - bool needUUIDAdded) { +void _updateDatabaseUUIDSchemaVersion(OperationContext* opCtx, + const std::string& dbname, + std::map<std::string, UUID>& collToUUID, + bool needUUIDAdded) { // Iterate through all collections of database dbname and make necessary UUID changes. std::vector<NamespaceString> collNamespaceStrings; { @@ -498,16 +498,17 @@ void _updateDBSchemaVersion(OperationContext* opCtx, } } -void _updateDBSchemaVersionNonReplicated(OperationContext* opCtx, - const std::string& dbname, - bool needUUIDAdded) { +Status _updateDatabaseUUIDSchemaVersionNonReplicated(OperationContext* opCtx, + const std::string& dbname, + bool needUUIDAdded) { // Iterate through all collections if we're in the "local" database. std::vector<NamespaceString> collNamespaceStrings; if (dbname == "local") { AutoGetDb autoDb(opCtx, dbname, MODE_X); Database* const db = autoDb.getDb(); if (!db) { - return; + return Status(ErrorCodes::NamespaceNotFound, + str::stream() << "database " << dbname << " does not exist"); } for (auto collectionIt = db->begin(); collectionIt != db->end(); ++collectionIt) { Collection* coll = *collectionIt; @@ -540,27 +541,14 @@ void _updateDBSchemaVersionNonReplicated(OperationContext* opCtx, } if ((needUUIDAdded && !coll->uuid()) || (!needUUIDAdded && coll->uuid())) { BSONObjBuilder resultWeDontCareAbout; - uassertStatusOK(_collModInternal( - opCtx, coll->ns(), collModObj, &resultWeDontCareAbout, /*upgradeUUID*/ true, uuid)); + auto collModStatus = _collModInternal( + opCtx, coll->ns(), collModObj, &resultWeDontCareAbout, /*upgradeUUID*/ true, uuid); + if (!collModStatus.isOK()) { + return collModStatus; + } } } -} - -void updateUUIDSchemaVersionNonReplicated(OperationContext* opCtx, bool upgrade) { - if (!enableCollectionUUIDs) { - return; - } - // Update UUIDs on all collections of all non-replicated databases. - std::vector<std::string> dbNames; - StorageEngine* storageEngine = opCtx->getServiceContext()->getGlobalStorageEngine(); - { - Lock::GlobalLock lk(opCtx, MODE_IS, UINT_MAX); - storageEngine->listDatabases(&dbNames); - } - for (auto it = dbNames.begin(); it != dbNames.end(); ++it) { - auto dbName = *it; - _updateDBSchemaVersionNonReplicated(opCtx, dbName, upgrade); - } + return Status::OK(); } } // namespace @@ -579,7 +567,10 @@ Status collModForUUIDUpgrade(OperationContext* opCtx, BSONObjBuilder resultWeDontCareAbout; // Update all non-replicated collection UUIDs. if (nss.ns() == "admin.system.version") { - updateUUIDSchemaVersionNonReplicated(opCtx, !!uuid); + auto schemaStatus = updateUUIDSchemaVersionNonReplicated(opCtx, !!uuid); + if (!schemaStatus.isOK()) { + return schemaStatus; + } } return _collModInternal(opCtx, nss, cmdObj, &resultWeDontCareAbout, /*upgradeUUID*/ true, uuid); } @@ -632,11 +623,32 @@ void updateUUIDSchemaVersion(OperationContext* opCtx, bool upgrade) { for (auto it = dbNames.begin(); it != dbNames.end(); ++it) { auto dbName = *it; - _updateDBSchemaVersion(opCtx, dbName, dbToCollToUUID[dbName], upgrade); + _updateDatabaseUUIDSchemaVersion(opCtx, dbName, dbToCollToUUID[dbName], upgrade); } const WriteConcernOptions writeConcern(WriteConcernOptions::kMajority, WriteConcernOptions::SyncMode::UNSET, /*timeout*/ INT_MAX); repl::getGlobalReplicationCoordinator()->awaitReplicationOfLastOpForClient(opCtx, writeConcern); } + +Status updateUUIDSchemaVersionNonReplicated(OperationContext* opCtx, bool upgrade) { + if (!enableCollectionUUIDs) { + return Status::OK(); + } + // Update UUIDs on all collections of all non-replicated databases. + std::vector<std::string> dbNames; + StorageEngine* storageEngine = opCtx->getServiceContext()->getGlobalStorageEngine(); + { + Lock::GlobalLock lk(opCtx, MODE_IS, UINT_MAX); + storageEngine->listDatabases(&dbNames); + } + for (auto it = dbNames.begin(); it != dbNames.end(); ++it) { + auto dbName = *it; + auto schemaStatus = _updateDatabaseUUIDSchemaVersionNonReplicated(opCtx, dbName, upgrade); + if (!schemaStatus.isOK()) { + return schemaStatus; + } + } + return Status::OK(); +} } // namespace mongo diff --git a/src/mongo/db/catalog/coll_mod.h b/src/mongo/db/catalog/coll_mod.h index 8a6eda14f46..6eb8b58b883 100644 --- a/src/mongo/db/catalog/coll_mod.h +++ b/src/mongo/db/catalog/coll_mod.h @@ -37,9 +37,20 @@ class Collection; class NamespaceString; class OperationContext; +/** + * If upgrade is true, adds UUIDs to all collections of all databases. If upgrade is false, removes + * UUIDs from all collections of all databases. It updates non-replicated collections by indirectly + * calling updateUUIDSchemaVersionNonReplicated(). + */ void updateUUIDSchemaVersion(OperationContext* opCtx, bool upgrade); /** + * If upgrade is true, adds UUIDs to all non-replicated collections of all databases. If upgrade is + * false, removes UUIDs from all non-replicated collections of all databases. + */ +Status updateUUIDSchemaVersionNonReplicated(OperationContext* opCtx, bool upgrade); + +/** * Performs the collection modification described in "cmdObj" on the collection "ns". */ Status collMod(OperationContext* opCtx, diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index d3bc602d95e..26fd4faab3b 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -633,6 +633,15 @@ ExitCode _initAndListen(int listenPort) { auto startupOpCtx = serviceContext->makeOperationContext(&cc()); + if (!storageGlobalParams.readOnly) { + if (!replSettings.usingReplSets() && !replSettings.isSlave() && + storageGlobalParams.engine != "devnull") { + Lock::GlobalWrite lk(startupOpCtx.get()); + FeatureCompatibilityVersion::setIfCleanStartup( + startupOpCtx.get(), repl::StorageInterface::get(serviceContext)); + } + } + repairDatabasesAndCheckVersion(startupOpCtx.get()); if (storageGlobalParams.upgrade) { @@ -771,13 +780,6 @@ ExitCode _initAndListen(int listenPort) { startTTLBackgroundJob(); } - if (!replSettings.usingReplSets() && !replSettings.isSlave() && - storageGlobalParams.engine != "devnull") { - Lock::GlobalWrite lk(startupOpCtx.get()); - FeatureCompatibilityVersion::setIfCleanStartup( - startupOpCtx.get(), repl::StorageInterface::get(serviceContext)); - } - if (replSettings.usingReplSets() || (!replSettings.isMaster() && replSettings.isSlave()) || !internalValidateFeaturesAsMaster) { serverGlobalParams.featureCompatibility.validateFeaturesAsMaster.store(false); diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript index 08592d21e55..63ab84d278a 100644 --- a/src/mongo/db/repl/SConscript +++ b/src/mongo/db/repl/SConscript @@ -1592,6 +1592,7 @@ env.Library( '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/client/clientdriver', '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/catalog/catalog_helpers', '$BUILD_DIR/mongo/db/cloner', '$BUILD_DIR/mongo/db/commands/dcommands_fcv', '$BUILD_DIR/mongo/db/commands/list_collections_filter', diff --git a/src/mongo/db/repl/initial_syncer.cpp b/src/mongo/db/repl/initial_syncer.cpp index 4660bf29f14..ee540a391dc 100644 --- a/src/mongo/db/repl/initial_syncer.cpp +++ b/src/mongo/db/repl/initial_syncer.cpp @@ -1011,6 +1011,30 @@ void InitialSyncer::_rollbackCheckerCheckForRollbackCallback( return; } + // Set UUIDs for all non-replicated collections on secondaries. See comment in + // ReplicationCoordinatorExternalStateImpl::initializeReplSetStorage() for the explanation of + // why we do this and why it is not necessary for sharded clusters. + if (serverGlobalParams.clusterRole != ClusterRole::ShardServer && + serverGlobalParams.clusterRole != ClusterRole::ConfigServer) { + const NamespaceString nss("admin", "system.version"); + auto opCtx = makeOpCtx(); + auto statusWithUUID = _storage->getCollectionUUID(opCtx.get(), nss); + if (!statusWithUUID.isOK()) { + // If the admin database does not exist, we intentionally fail initial sync. As part of + // SERVER-29448, we will disallow dropping the admin database, so failing here is fine. + onCompletionGuard->setResultAndCancelRemainingWork_inlock(lock, + statusWithUUID.getStatus()); + return; + } + if (statusWithUUID.getValue()) { + auto schemaStatus = _storage->upgradeUUIDSchemaVersionNonReplicated(opCtx.get()); + if (!schemaStatus.isOK()) { + onCompletionGuard->setResultAndCancelRemainingWork_inlock(lock, schemaStatus); + return; + } + } + } + // Success! onCompletionGuard->setResultAndCancelRemainingWork_inlock(lock, _lastApplied); } diff --git a/src/mongo/db/repl/initial_syncer_test.cpp b/src/mongo/db/repl/initial_syncer_test.cpp index 952c3d5e2f4..04e7b67c32a 100644 --- a/src/mongo/db/repl/initial_syncer_test.cpp +++ b/src/mongo/db/repl/initial_syncer_test.cpp @@ -222,6 +222,10 @@ protected: bool droppedUserDBs = false; std::vector<std::string> droppedCollections; int documentsInsertedCount = 0; + bool schemaUpgraded = false; + OptionalCollectionUUID uuid; + bool getCollectionUUIDShouldFail = false; + bool upgradeUUIDSchemaVersionNonReplicatedShouldFail = false; }; stdx::mutex _storageInterfaceWorkDoneMutex; // protects _storageInterfaceWorkDone. @@ -234,6 +238,10 @@ protected: const NamespaceString& nss) { LockGuard lock(_storageInterfaceWorkDoneMutex); _storageInterfaceWorkDone.createOplogCalled = true; + _storageInterfaceWorkDone.schemaUpgraded = false; + _storageInterfaceWorkDone.uuid = boost::none; + _storageInterfaceWorkDone.getCollectionUUIDShouldFail = false; + _storageInterfaceWorkDone.upgradeUUIDSchemaVersionNonReplicatedShouldFail = false; return Status::OK(); }; _storageInterface->truncateCollFn = [this](OperationContext* opCtx, @@ -286,6 +294,35 @@ protected: return StatusWith<std::unique_ptr<CollectionBulkLoader>>( std::unique_ptr<CollectionBulkLoader>(collInfo->loader)); }; + _storageInterface->getCollectionUUIDFn = [this](OperationContext* opCtx, + const NamespaceString& nss) { + LockGuard lock(_storageInterfaceWorkDoneMutex); + if (_storageInterfaceWorkDone.getCollectionUUIDShouldFail) { + // getCollectionUUID returns NamespaceNotFound if either the db or the collection is + // missing. + return StatusWith<OptionalCollectionUUID>(Status( + ErrorCodes::NamespaceNotFound, + str::stream() << "getCollectionUUID failed because namespace " << nss.ns() + << " not found.")); + } else { + return StatusWith<OptionalCollectionUUID>(_storageInterfaceWorkDone.uuid); + } + }; + + _storageInterface->upgradeUUIDSchemaVersionNonReplicatedFn = + [this](OperationContext* opCtx) { + LockGuard lock(_storageInterfaceWorkDoneMutex); + if (_storageInterfaceWorkDone.upgradeUUIDSchemaVersionNonReplicatedShouldFail) { + // One of the status codes a failed upgradeUUIDSchemaVersionNonReplicated call + // can return is NamespaceNotFound. + return Status(ErrorCodes::NamespaceNotFound, + "upgradeUUIDSchemaVersionNonReplicated failed because the " + "desired ns was not found."); + } else { + _storageInterfaceWorkDone.schemaUpgraded = true; + return Status::OK(); + } + }; _dbWorkThreadPool = stdx::make_unique<OldThreadPool>(1); @@ -405,6 +442,9 @@ protected: } } + void doSuccessfulInitialSyncWithOneBatch(); + OplogEntry doInitialSyncWithOneBatch(); + std::unique_ptr<TaskExecutorMock> _executorProxy; InitialSyncerOptions _options; @@ -2998,8 +3038,7 @@ TEST_F(InitialSyncerTest, InitialSyncerCancelsGetNextApplierBatchCallbackOnOplog ASSERT_EQUALS(ErrorCodes::OperationFailed, _lastApplied); } -TEST_F(InitialSyncerTest, - InitialSyncerReturnsLastAppliedOnReachingStopTimestampAfterApplyingOneBatch) { +OplogEntry InitialSyncerTest::doInitialSyncWithOneBatch() { auto initialSyncer = &getInitialSyncer(); auto opCtx = makeOpCtx(); @@ -3056,6 +3095,11 @@ TEST_F(InitialSyncerTest, } initialSyncer->join(); + return lastOp; +} + +void InitialSyncerTest::doSuccessfulInitialSyncWithOneBatch() { + auto lastOp = doInitialSyncWithOneBatch(); ASSERT_EQUALS(lastOp.getOpTime(), unittest::assertGet(_lastApplied).opTime); ASSERT_EQUALS(lastOp.getHash(), unittest::assertGet(_lastApplied).value); @@ -3064,6 +3108,17 @@ TEST_F(InitialSyncerTest, } TEST_F(InitialSyncerTest, + InitialSyncerReturnsLastAppliedOnReachingStopTimestampAfterApplyingOneBatch) { + // In this test, getCollectionUUID should not return a UUID. Hence, + // upgradeUUIDSchemaVersionNonReplicated should not be called. + doSuccessfulInitialSyncWithOneBatch(); + + // Ensure upgradeUUIDSchemaVersionNonReplicated was not called. + LockGuard lock(_storageInterfaceWorkDoneMutex); + ASSERT_FALSE(_storageInterfaceWorkDone.schemaUpgraded); +} + +TEST_F(InitialSyncerTest, InitialSyncerReturnsLastAppliedOnReachingStopTimestampAfterApplyingMultipleBatches) { auto initialSyncer = &getInitialSyncer(); auto opCtx = makeOpCtx(); @@ -3586,4 +3641,53 @@ TEST_F(InitialSyncerTest, GetInitialSyncProgressReturnsCorrectProgress) { << attempt1; } +TEST_F(InitialSyncerTest, InitialSyncerUpdatesCollectionUUIDsIfgetCollectionUUIDReturnsUUID) { + // Ensure getCollectionUUID returns a UUID. This should trigger a call to + // upgradeUUIDSchemaVersionNonReplicated. + { + LockGuard lock(_storageInterfaceWorkDoneMutex); + _storageInterfaceWorkDone.uuid = UUID::gen(); + } + doSuccessfulInitialSyncWithOneBatch(); + + // Ensure upgradeUUIDSchemaVersionNonReplicated was called. + LockGuard lock(_storageInterfaceWorkDoneMutex); + ASSERT_TRUE(_storageInterfaceWorkDone.schemaUpgraded); +} + +TEST_F(InitialSyncerTest, InitialSyncerCapturesGetCollectionUUIDError) { + // Ensure getCollectionUUID returns a bad status. This should be passed to the initial syncer. + { + LockGuard lock(_storageInterfaceWorkDoneMutex); + _storageInterfaceWorkDone.getCollectionUUIDShouldFail = true; + } + doInitialSyncWithOneBatch(); + + // Ensure the getCollectionUUID status was captured. + ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, _lastApplied); + + // Ensure upgradeUUIDSchemaVersionNonReplicated was not called. + LockGuard lock(_storageInterfaceWorkDoneMutex); + ASSERT_FALSE(_storageInterfaceWorkDone.schemaUpgraded); +} + +TEST_F(InitialSyncerTest, InitialSyncerCapturesUpgradeUUIDSchemaVersionError) { + // Ensure getCollectionUUID returns a UUID. This should trigger a call to + // upgradeUUIDSchemaVersionNonReplicated. + { + LockGuard lock(_storageInterfaceWorkDoneMutex); + _storageInterfaceWorkDone.uuid = UUID::gen(); + } + + // Ensure upgradeUUIDSchemaVersionNonReplicated returns a bad status. This should be passed to + // the initial syncer. + { + LockGuard lock(_storageInterfaceWorkDoneMutex); + _storageInterfaceWorkDone.upgradeUUIDSchemaVersionNonReplicatedShouldFail = true; + } + doInitialSyncWithOneBatch(); + + // Ensure the upgradeUUIDSchemaVersionNonReplicated status was captured. + ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, _lastApplied); +} } // namespace diff --git a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp index f1085c548f6..e62bbf0066d 100644 --- a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp @@ -38,6 +38,7 @@ #include "mongo/base/status_with.h" #include "mongo/bson/oid.h" #include "mongo/bson/util/bson_extract.h" +#include "mongo/db/catalog/coll_mod.h" #include "mongo/db/catalog/database.h" #include "mongo/db/catalog/database_holder.h" #include "mongo/db/client.h" @@ -390,6 +391,21 @@ Status ReplicationCoordinatorExternalStateImpl::initializeReplSetStorage(Operati waitForAllEarlierOplogWritesToBeVisible(opCtx); }); + // Set UUIDs for all non-replicated collections. This is necessary for independent replica + // sets started with no data files because collections in local are created prior to the + // featureCompatibilityVersion being set to 3.6, so the collections are not created with + // UUIDs. This is not an issue for sharded clusters because the config server sends a + // setFeatureCompatibilityVersion command with the featureCompatibilityVersion equal to the + // cluster's featureCompatibilityVersion during addShard, which will add UUIDs to all + // collections that do not already have them. Here, we add UUIDs to the non-replicated + // collections on the primary. We add them on the secondaries during InitialSync. + if (serverGlobalParams.clusterRole != ClusterRole::ShardServer && + serverGlobalParams.clusterRole != ClusterRole::ConfigServer) { + auto schemaStatus = updateUUIDSchemaVersionNonReplicated(opCtx, true); + if (!schemaStatus.isOK()) { + return schemaStatus; + } + } FeatureCompatibilityVersion::setIfCleanStartup(opCtx, _storageInterface); } catch (const DBException& ex) { return ex.toStatus(); diff --git a/src/mongo/db/repl/storage_interface.h b/src/mongo/db/repl/storage_interface.h index 837d73fa4f1..afbeb3561a2 100644 --- a/src/mongo/db/repl/storage_interface.h +++ b/src/mongo/db/repl/storage_interface.h @@ -290,6 +290,18 @@ public: const NamespaceString& nss) = 0; /** + * Returns the UUID of the collection specified by nss, if such a UUID exists. + */ + virtual StatusWith<OptionalCollectionUUID> getCollectionUUID(OperationContext* opCtx, + const NamespaceString& nss) = 0; + + /** + * Adds UUIDs for non-replicated collections. To be called only at the end of initial + * sync and only if the admin.system.version collection has a UUID. + */ + virtual Status upgradeUUIDSchemaVersionNonReplicated(OperationContext* opCtx) = 0; + + /** * Sets the highest timestamp at which the storage engine is allowed to take a checkpoint. * This timestamp can never decrease, and thus should be a timestamp that can never roll back. */ diff --git a/src/mongo/db/repl/storage_interface_impl.cpp b/src/mongo/db/repl/storage_interface_impl.cpp index 47f2fd8d92f..2e048f66c3f 100644 --- a/src/mongo/db/repl/storage_interface_impl.cpp +++ b/src/mongo/db/repl/storage_interface_impl.cpp @@ -43,6 +43,7 @@ #include "mongo/bson/bsonobjbuilder.h" #include "mongo/bson/util/bson_extract.h" #include "mongo/db/auth/authorization_manager.h" +#include "mongo/db/catalog/coll_mod.h" #include "mongo/db/catalog/collection.h" #include "mongo/db/catalog/collection_catalog_entry.h" #include "mongo/db/catalog/database.h" @@ -922,6 +923,23 @@ StatusWith<StorageInterface::CollectionCount> StorageInterfaceImpl::getCollectio return collection->numRecords(opCtx); } +StatusWith<OptionalCollectionUUID> StorageInterfaceImpl::getCollectionUUID( + OperationContext* opCtx, const NamespaceString& nss) { + AutoGetCollectionForRead autoColl(opCtx, nss); + + auto collectionResult = getCollection( + autoColl, nss, str::stream() << "Unable to get UUID of " << nss.ns() << " collection."); + if (!collectionResult.isOK()) { + return collectionResult.getStatus(); + } + auto collection = collectionResult.getValue(); + return collection->uuid(); +} + +Status StorageInterfaceImpl::upgradeUUIDSchemaVersionNonReplicated(OperationContext* opCtx) { + return updateUUIDSchemaVersionNonReplicated(opCtx, true); +} + void StorageInterfaceImpl::setStableTimestamp(ServiceContext* serviceCtx, SnapshotName snapshotName) { serviceCtx->getGlobalStorageEngine()->setStableTimestamp(snapshotName); diff --git a/src/mongo/db/repl/storage_interface_impl.h b/src/mongo/db/repl/storage_interface_impl.h index c020648106d..94d973ed4e7 100644 --- a/src/mongo/db/repl/storage_interface_impl.h +++ b/src/mongo/db/repl/storage_interface_impl.h @@ -137,6 +137,11 @@ public: StatusWith<StorageInterface::CollectionCount> getCollectionCount( OperationContext* opCtx, const NamespaceString& nss) override; + StatusWith<OptionalCollectionUUID> getCollectionUUID(OperationContext* opCtx, + const NamespaceString& nss) override; + + Status upgradeUUIDSchemaVersionNonReplicated(OperationContext* opCtx) override; + void setStableTimestamp(ServiceContext* serviceCtx, SnapshotName snapshotName) override; void setInitialDataTimestamp(ServiceContext* serviceCtx, SnapshotName snapshotName) override; diff --git a/src/mongo/db/repl/storage_interface_impl_test.cpp b/src/mongo/db/repl/storage_interface_impl_test.cpp index 607ba207b57..62cab56c86d 100644 --- a/src/mongo/db/repl/storage_interface_impl_test.cpp +++ b/src/mongo/db/repl/storage_interface_impl_test.cpp @@ -2283,6 +2283,103 @@ TEST_F(StorageInterfaceImplTest, GetCollectionCountReturnsCollectionCount) { } TEST_F(StorageInterfaceImplTest, + GetCollectionUUIDReturnsNamespaceNotFoundWhenDatabaseDoesNotExist) { + auto opCtx = getOperationContext(); + StorageInterfaceImpl storage; + NamespaceString nss("nosuchdb.coll"); + ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, storage.getCollectionUUID(opCtx, nss).getStatus()); +} + +TEST_F(StorageInterfaceImplTest, + GetCollectionUUIDReturnsNamespaceNotFoundWhenCollectionDoesNotExist) { + auto opCtx = getOperationContext(); + StorageInterfaceImpl storage; + auto nss = makeNamespace(_agent); + NamespaceString wrongColl(nss.db(), "wrongColl"_sd); + ASSERT_OK(storage.createCollection(opCtx, nss, CollectionOptions())); + ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, + storage.getCollectionUUID(opCtx, wrongColl).getStatus()); +} + +TEST_F(StorageInterfaceImplTest, GetCollectionUUIDReturnsBoostNoneWhenCollectionHasNoUUID) { + auto opCtx = getOperationContext(); + StorageInterfaceImpl storage; + auto nss = makeNamespace(_agent); + ASSERT_OK(storage.createCollection(opCtx, nss, CollectionOptions())); + auto uuid = unittest::assertGet(storage.getCollectionUUID(opCtx, nss)); + ASSERT_EQ(uuid, boost::none); +} + +TEST_F(StorageInterfaceImplTest, GetCollectionUUIDReturnsUUIDIfExists) { + auto opCtx = getOperationContext(); + StorageInterfaceImpl storage; + auto nss = makeNamespace(_agent); + CollectionOptions options; + options.uuid = UUID::gen(); + ASSERT_OK(storage.createCollection(opCtx, nss, options)); + auto uuid = unittest::assertGet(storage.getCollectionUUID(opCtx, nss)); + ASSERT_EQ(uuid, options.uuid); +} + +TEST_F(StorageInterfaceImplTest, UpgradeUUIDSchemaVersionNonReplicatedUpgradesLocalCollections) { + auto opCtx = getOperationContext(); + StorageInterfaceImpl storage; + auto nss = makeNamespace(_agent); + + // Create a collection on the local database with no UUID. + ASSERT_OK(storage.createCollection(opCtx, nss, CollectionOptions())); + auto uuid = unittest::assertGet(storage.getCollectionUUID(opCtx, nss)); + ASSERT_EQ(uuid, boost::none); + ASSERT_OK(storage.upgradeUUIDSchemaVersionNonReplicated(opCtx)); + + // Ensure a UUID now exists on the collection. + uuid = unittest::assertGet(storage.getCollectionUUID(opCtx, nss)); + ASSERT_NOT_EQUALS(uuid, boost::none); +} + +TEST_F(StorageInterfaceImplTest, UpgradeUUIDSchemaVersionNonReplicatedIgnoresUpgradedCollections) { + auto opCtx = getOperationContext(); + StorageInterfaceImpl storage; + auto nss = makeNamespace(_agent); + CollectionOptions options; + options.uuid = UUID::gen(); + + // Create a collection on the local database with a UUID. + ASSERT_OK(storage.createCollection(opCtx, nss, options)); + auto uuid = unittest::assertGet(storage.getCollectionUUID(opCtx, nss)); + ASSERT_EQ(uuid, options.uuid); + ASSERT_OK(storage.upgradeUUIDSchemaVersionNonReplicated(opCtx)); + + // Ensure the UUID has not changed after the upgrade. + uuid = unittest::assertGet(storage.getCollectionUUID(opCtx, nss)); + ASSERT_EQUALS(uuid, options.uuid); +} + +TEST_F(StorageInterfaceImplTest, UpgradeUUIDSchemaVersionNonReplicatedUpgradesSystemDotProfile) { + auto opCtx = getOperationContext(); + StorageInterfaceImpl storage; + const NamespaceString nss("testdb", "system.profile"); + + // Create a system.profile collection with no UUID. + ASSERT_OK(storage.createCollection(opCtx, nss, CollectionOptions())); + auto uuid = unittest::assertGet(storage.getCollectionUUID(opCtx, nss)); + ASSERT_EQ(uuid, boost::none); + + // Also create another collection on the same database that will not be assigned a UUID. + const NamespaceString noUUIDNss("testdb", "noUUIDCollection"); + ASSERT_OK(storage.createCollection(opCtx, noUUIDNss, CollectionOptions())); + auto noUUID = unittest::assertGet(storage.getCollectionUUID(opCtx, noUUIDNss)); + ASSERT_EQ(noUUID, boost::none); + ASSERT_OK(storage.upgradeUUIDSchemaVersionNonReplicated(opCtx)); + + // Ensure a UUID now exists on the system.profile collection but not noUUIDCollection. + uuid = unittest::assertGet(storage.getCollectionUUID(opCtx, nss)); + ASSERT_NOT_EQUALS(uuid, boost::none); + noUUID = unittest::assertGet(storage.getCollectionUUID(opCtx, noUUIDNss)); + ASSERT_EQ(noUUID, boost::none); +} + +TEST_F(StorageInterfaceImplTest, GetCollectionSizeReturnsNamespaceNotFoundWhenDatabaseDoesNotExist) { auto opCtx = getOperationContext(); StorageInterfaceImpl storage; diff --git a/src/mongo/db/repl/storage_interface_mock.h b/src/mongo/db/repl/storage_interface_mock.h index f0c14a61b9e..9c88532781f 100644 --- a/src/mongo/db/repl/storage_interface_mock.h +++ b/src/mongo/db/repl/storage_interface_mock.h @@ -122,6 +122,9 @@ public: BoundInclusion boundInclusion, std::size_t limit)>; using IsAdminDbValidFn = stdx::function<Status(OperationContext* opCtx)>; + using GetCollectionUUIDFn = stdx::function<StatusWith<OptionalCollectionUUID>( + OperationContext* opCtx, const NamespaceString& nss)>; + using UpgradeUUIDSchemaVersionNonReplicatedFn = stdx::function<Status(OperationContext* opCtx)>; StorageInterfaceMock() = default; @@ -253,6 +256,15 @@ public: return 0; } + StatusWith<OptionalCollectionUUID> getCollectionUUID(OperationContext* opCtx, + const NamespaceString& nss) override { + return getCollectionUUIDFn(opCtx, nss); + } + + Status upgradeUUIDSchemaVersionNonReplicated(OperationContext* opCtx) override { + return upgradeUUIDSchemaVersionNonReplicatedFn(opCtx); + } + void setStableTimestamp(ServiceContext* serviceCtx, SnapshotName snapshotName) override; void setInitialDataTimestamp(ServiceContext* serviceCtx, SnapshotName snapshotName) override; @@ -330,6 +342,15 @@ public: IsAdminDbValidFn isAdminDbValidFn = [](OperationContext*) { return Status{ErrorCodes::IllegalOperation, "IsAdminDbValidFn not implemented."}; }; + GetCollectionUUIDFn getCollectionUUIDFn = []( + OperationContext* opCtx, const NamespaceString& nss) -> StatusWith<OptionalCollectionUUID> { + return Status{ErrorCodes::IllegalOperation, "GetCollectionUUIDFn not implemented."}; + }; + UpgradeUUIDSchemaVersionNonReplicatedFn upgradeUUIDSchemaVersionNonReplicatedFn = + [](OperationContext* opCtx) -> Status { + return Status{ErrorCodes::IllegalOperation, + "UpgradeUUIDSchemaVersionNonReplicatedFn not implemented."}; + }; private: mutable stdx::mutex _mutex; @@ -337,6 +358,8 @@ private: bool _rbidInitialized = false; SnapshotName _stableTimestamp = SnapshotName::min(); SnapshotName _initialDataTimestamp = SnapshotName::min(); + OptionalCollectionUUID _uuid; + bool _schemaUpgraded; }; } // namespace repl |