summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenety Goh <benety@mongodb.com>2018-07-13 09:56:32 -0400
committerBenety Goh <benety@mongodb.com>2018-07-13 09:56:32 -0400
commit6a7e25af6aeb1f612019c15c73258341a5779c16 (patch)
tree77d19d6380c87dd22a30557b28bbed40b5f84f07
parent2c19e062e4ea38cb242a17e9b5cc0f160dc970ae (diff)
downloadmongo-6a7e25af6aeb1f612019c15c73258341a5779c16.tar.gz
SERVER-35824 disallow downgrade to 4.0 if long index namespaces are detected
-rw-r--r--jstests/noPassthrough/long_index_downgrade.js41
-rw-r--r--src/mongo/base/error_codes.err1
-rw-r--r--src/mongo/db/catalog/coll_mod.cpp59
-rw-r--r--src/mongo/db/catalog/coll_mod.h6
-rw-r--r--src/mongo/db/commands/set_feature_compatibility_version_command.cpp4
5 files changed, 105 insertions, 6 deletions
diff --git a/jstests/noPassthrough/long_index_downgrade.js b/jstests/noPassthrough/long_index_downgrade.js
new file mode 100644
index 00000000000..f7b29cc5292
--- /dev/null
+++ b/jstests/noPassthrough/long_index_downgrade.js
@@ -0,0 +1,41 @@
+/**
+ * Long index namespaces exceeding 127 characters are supported starting in 4.2.
+ * Since these are not supported in 4.0, we should not allow a server to be downgraded to FCV 4.0
+ * when there are indexes with long namespaces.
+ * @tags: [requires_replication]
+ * TODO: remove this test in 4.4.
+ */
+(function() {
+ 'use strict';
+
+ const rst = new ReplSetTest({nodes: 1});
+
+ rst.startSet();
+ rst.initiate();
+
+ const primary = rst.getPrimary();
+ const mydb = primary.getDB('test');
+ const coll = mydb.getCollection('long_index_name');
+
+ // Compute maximum index name length for this collection under FCV 4.0.
+ const maxNsLength = 127;
+ const maxIndexNameLength = maxNsLength - (coll.getFullName() + ".$").length;
+ jsTestLog('Max index name length under FCV 4.0 = ' + maxIndexNameLength);
+
+ // Create an index with the longest name allowed for this collection under FCV 4.0.
+ assert.commandWorked(coll.createIndex({a: 1}, {name: 'a'.repeat(maxIndexNameLength)}));
+
+ // Create an index with a name that exceeds the limit in 4.0.
+ const longName = 'b'.repeat(maxIndexNameLength + 1);
+ assert.commandWorked(coll.createIndex({b: 1}, {name: longName}));
+
+ // Downgrades should fail while we have indexes that are not compatible with 4.0.
+ assert.commandFailedWithCode(mydb.adminCommand({setFeatureCompatibilityVersion: '4.0'}),
+ ErrorCodes.IndexNamespaceTooLong);
+
+ // Drop index with long name before retrying downgrade.
+ assert.commandWorked(coll.dropIndex(longName));
+ assert.commandWorked(mydb.adminCommand({setFeatureCompatibilityVersion: '4.0'}));
+
+ rst.stopSet();
+})();
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err
index 74620aa4fe7..9477dcaf9eb 100644
--- a/src/mongo/base/error_codes.err
+++ b/src/mongo/base/error_codes.err
@@ -262,6 +262,7 @@ error_code("TooManyFilesOpen", 261);
error_code("FailPointSetFailed", 262)
error_code("OperationNotSupportedInTransaction", 263)
error_code("TooManyLogicalSessions", 264);
+error_code("IndexNamespaceTooLong", 265);
# Error codes 4000-8999 are reserved.
diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp
index 14c1ba4f3c3..936a99a368e 100644
--- a/src/mongo/db/catalog/coll_mod.cpp
+++ b/src/mongo/db/catalog/coll_mod.cpp
@@ -66,6 +66,19 @@ namespace {
// databases if none are provided).
MONGO_FAIL_POINT_DEFINE(hangBeforeDatabaseUpgrade);
+/**
+ * Returns list of database names.
+ */
+std::vector<std::string> getDatabaseNames(OperationContext* opCtx) {
+ std::vector<std::string> dbNames;
+ auto storageEngine = opCtx->getServiceContext()->getStorageEngine();
+ {
+ Lock::GlobalLock lk(opCtx, MODE_IS);
+ storageEngine->listDatabases(&dbNames);
+ }
+ return dbNames;
+}
+
struct CollModRequest {
const IndexDescriptor* idx = nullptr;
BSONElement indexExpireAfterSeconds = {};
@@ -600,12 +613,7 @@ Status updateNonReplicatedUniqueIndexes(OperationContext* opCtx) {
// Update all unique indexes belonging to all non-replicated collections.
// (_id indexes are not updated).
- std::vector<std::string> dbNames;
- StorageEngine* storageEngine = opCtx->getServiceContext()->getStorageEngine();
- {
- Lock::GlobalLock lk(opCtx, MODE_IS);
- storageEngine->listDatabases(&dbNames);
- }
+ auto dbNames = getDatabaseNames(opCtx);
for (auto it = dbNames.begin(); it != dbNames.end(); ++it) {
auto dbName = *it;
auto schemaStatus = _updateNonReplicatedUniqueIndexesPerDatabase(opCtx, dbName);
@@ -616,4 +624,43 @@ Status updateNonReplicatedUniqueIndexes(OperationContext* opCtx) {
return Status::OK();
}
+Status checkIndexNamespacesOnDowngrade(OperationContext* opCtx) {
+ auto dbNames = getDatabaseNames(opCtx);
+ for (const auto& dbName : dbNames) {
+ AutoGetDb autoDb(opCtx, dbName, MODE_IS);
+ auto db = autoDb.getDb();
+ // If the database no longer exists, there's nothing to do.
+ if (!db) {
+ continue;
+ }
+
+ for (auto collIter : *db) {
+ const auto nss = collIter->ns();
+ if (nss.isDropPendingNamespace()) {
+ continue;
+ }
+
+ AutoGetCollectionForRead autoColl(opCtx, nss);
+ auto coll = autoColl.getCollection();
+ if (!coll) {
+ continue;
+ }
+
+ const bool includeUnfinishedIndexes = true;
+ auto it = coll->getIndexCatalog()->getIndexIterator(opCtx, includeUnfinishedIndexes);
+ while (it.more()) {
+ auto desc = it.next();
+ if (desc->indexNamespace().length() > NamespaceString::MaxNsLen) {
+ return Status(ErrorCodes::IndexNamespaceTooLong,
+ str::stream() << "index namespace \"" << desc->indexNamespace()
+ << "\" is too long for downgrade to 4.0 ("
+ << NamespaceString::MaxNsLen
+ << " byte max)");
+ }
+ }
+ }
+ }
+ return Status::OK();
+}
+
} // namespace mongo
diff --git a/src/mongo/db/catalog/coll_mod.h b/src/mongo/db/catalog/coll_mod.h
index 3cf618ac803..c7d69ed197a 100644
--- a/src/mongo/db/catalog/coll_mod.h
+++ b/src/mongo/db/catalog/coll_mod.h
@@ -69,4 +69,10 @@ void updateUniqueIndexesOnUpgrade(OperationContext* opCtx);
* Updates non-replicated unique indexes to timestamp safe unique index format.
*/
Status updateNonReplicatedUniqueIndexes(OperationContext* opCtx);
+
+/*
+ * Checks index namespaces that would exceed 4.0 constraints on setFCV=4.0.
+ */
+Status checkIndexNamespacesOnDowngrade(OperationContext* opCtx);
+
} // namespace mongo
diff --git a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp
index e751532068b..347f90939fb 100644
--- a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp
+++ b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp
@@ -199,6 +199,10 @@ public:
return true;
}
+ // Before starting the downgrade process, check indexes for index namespaces that would
+ // fail 4.0 length constraints.
+ uassertStatusOK(checkIndexNamespacesOnDowngrade(opCtx));
+
FeatureCompatibilityVersion::setTargetDowngrade(opCtx);
{