summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/multiVersion/set_schema_version.js43
-rw-r--r--src/mongo/db/catalog/coll_mod.cpp70
-rw-r--r--src/mongo/db/catalog/coll_mod.h11
-rw-r--r--src/mongo/db/db.cpp16
-rw-r--r--src/mongo/db/repl/SConscript1
-rw-r--r--src/mongo/db/repl/initial_syncer.cpp22
-rw-r--r--src/mongo/db/repl/initial_syncer_test.cpp108
-rw-r--r--src/mongo/db/repl/replication_coordinator_external_state_impl.cpp16
-rw-r--r--src/mongo/db/repl/storage_interface.h12
-rw-r--r--src/mongo/db/repl/storage_interface_impl.cpp18
-rw-r--r--src/mongo/db/repl/storage_interface_impl.h5
-rw-r--r--src/mongo/db/repl/storage_interface_impl_test.cpp97
-rw-r--r--src/mongo/db/repl/storage_interface_mock.h23
13 files changed, 379 insertions, 63 deletions
diff --git a/jstests/multiVersion/set_schema_version.js b/jstests/multiVersion/set_schema_version.js
index 928d788e4a1..e7ee9079230 100644
--- a/jstests/multiVersion/set_schema_version.js
+++ b/jstests/multiVersion/set_schema_version.js
@@ -8,7 +8,7 @@
const latest = "latest";
const downgrade = "3.4";
- let checkCollectionUUIDs = function(adminDB, isDowngrade, excludeLocal) {
+ let checkCollectionUUIDs = function(adminDB, isDowngrade) {
let databaseList = adminDB.runCommand({"listDatabases": 1}).databases;
databaseList.forEach(function(database) {
@@ -19,19 +19,12 @@
if (collectionInfos[i].name == "system.indexes") {
continue;
}
- if (excludeLocal) {
- // Exclude checking all collections in local until SERVER-30131 is fixed.
- if (currentDatabase != "local") {
- assert(collectionInfos[i].info.uuid);
- }
+ if (isDowngrade) {
+ assert(!collectionInfos[i].info.uuid,
+ "Unexpected uuid for collection: " + tojson(collectionInfos[i]));
} else {
- if (isDowngrade) {
- assert(!collectionInfos[i].info.uuid,
- "Unexpected uuid for collection: " + tojson(collectionInfos[i]));
- } else {
- assert(collectionInfos[i].info.uuid,
- "Expect uuid for collection: " + tojson(collectionInfos[i]));
- }
+ assert(collectionInfos[i].info.uuid,
+ "Expect uuid for collection: " + tojson(collectionInfos[i]));
}
}
});
@@ -92,13 +85,13 @@
insertDataForConn(conn, ["admin", "local", "test"]);
// Ensure all collections have UUIDs in 3.6 mode.
- checkCollectionUUIDs(adminDB, false, true);
+ checkCollectionUUIDs(adminDB, false);
// Set featureCompatibilityVersion to 3.4.
setFCV(adminDB, "3.4");
// Ensure no collections in a featureCompatibilityVersion 3.4 database have UUIDs.
- checkCollectionUUIDs(adminDB, true, false);
+ checkCollectionUUIDs(adminDB, true);
// Stop Mongod 3.6
MongoRunner.stopMongod(conn);
@@ -112,7 +105,7 @@
checkFCV(downgradeAdminDB, "3.4", true);
// Ensure there are no UUIDs
- checkCollectionUUIDs(downgradeAdminDB, true, false);
+ checkCollectionUUIDs(downgradeAdminDB, true);
// Stop 3.4
MongoRunner.stopMongod(downgradeConn);
@@ -125,7 +118,7 @@
// Ensure all collections have UUIDs after switching back to featureCompatibilityVersion
// 3.6.
setFCV(adminDB, "3.6");
- checkCollectionUUIDs(adminDB, false, true);
+ checkCollectionUUIDs(adminDB, false);
// Stop Mongod 3.6 for the last time
MongoRunner.stopMongod(conn);
@@ -157,10 +150,10 @@
}
// Ensure all collections have UUIDs in 3.6 mode on both primary and secondaries.
- checkCollectionUUIDs(primaryAdminDB, false, true);
+ checkCollectionUUIDs(primaryAdminDB, false);
for (let j = 0; j < secondaries.length; j++) {
let secondaryAdminDB = secondaries[j].getDB("admin");
- checkCollectionUUIDs(secondaryAdminDB, false, true);
+ checkCollectionUUIDs(secondaryAdminDB, false);
}
// Change featureCompatibilityVersion to 3.4.
@@ -172,10 +165,10 @@
}
// Ensure no collections have UUIDs in 3.4 mode on both primary and secondaries.
- checkCollectionUUIDs(primaryAdminDB, true, false);
+ checkCollectionUUIDs(primaryAdminDB, true);
for (let j = 0; j < secondaries.length; j++) {
let secondaryAdminDB = secondaries[j].getDB("admin");
- checkCollectionUUIDs(secondaryAdminDB, true, false);
+ checkCollectionUUIDs(secondaryAdminDB, true);
}
// Stop 3.6 RS
@@ -191,14 +184,14 @@
let downgradeSecondaries = downgradeRst.getSecondaries();
// Initially featureCompatibilityVersion document is 3.4 on primary and secondaries.
- checkCollectionUUIDs(downgradePrimaryAdminDB, true, false);
+ checkCollectionUUIDs(downgradePrimaryAdminDB, true);
checkFCV(downgradePrimaryAdminDB, "3.4", true);
for (let j = 0; j < downgradeSecondaries.length; j++) {
let secondaryAdminDB = downgradeSecondaries[j].getDB("admin");
checkFCV(secondaryAdminDB, "3.4", true);
// Ensure no collections have UUIDs
- checkCollectionUUIDs(secondaryAdminDB, true, false);
+ checkCollectionUUIDs(secondaryAdminDB, true);
}
downgradeRst.stopSet();
@@ -219,10 +212,10 @@
checkFCV(secondaryAdminDB, "3.6");
}
- checkCollectionUUIDs(primaryAdminDB, false, true);
+ checkCollectionUUIDs(primaryAdminDB, false);
for (let j = 0; j < secondaries.length; j++) {
let secondaryAdminDB = secondaries[j].getDB("admin");
- checkCollectionUUIDs(secondaryAdminDB, false, true);
+ checkCollectionUUIDs(secondaryAdminDB, false);
}
rst.stopSet();
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..471bb84f034 100644
--- a/src/mongo/db/repl/initial_syncer.cpp
+++ b/src/mongo/db/repl/initial_syncer.cpp
@@ -1011,6 +1011,28 @@ 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()) {
+ 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