summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaley Connelly <haley.connelly@mongodb.com>2021-08-09 18:52:51 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-08-17 23:10:55 +0000
commit7853d994db8831e1f91e6c2a59cb01d26f1e6e45 (patch)
tree8d67c097599795b0df4c173cc790925b1eb1a9c9
parent1ddb121d46d2fa14b2ec4c5a151f3f3ff0771a02 (diff)
downloadmongo-7853d994db8831e1f91e6c2a59cb01d26f1e6e45.tar.gz
SERVER-52728 Upgrade path from mongoDB 2.4 to 4.2 requires dropping ping_ index
-rw-r--r--jstests/multiVersion/upgrade_from_v24_ping_index_conflict.js92
-rw-r--r--src/mongo/db/s/sharding_initialization_mongod.cpp35
-rw-r--r--src/mongo/shell/servers.js1
3 files changed, 128 insertions, 0 deletions
diff --git a/jstests/multiVersion/upgrade_from_v24_ping_index_conflict.js b/jstests/multiVersion/upgrade_from_v24_ping_index_conflict.js
new file mode 100644
index 00000000000..3cc48b0155b
--- /dev/null
+++ b/jstests/multiVersion/upgrade_from_v24_ping_index_conflict.js
@@ -0,0 +1,92 @@
+/**
+ * In v2.4, an index {ping: 1} named "ping_" was created on the config.lockpings collection.
+ * Starting in 4.2, creating an index identical to an existing index, but with a different name, is
+ * explicitly disallowed.
+ *
+ * Internally, modern mongods implicitly create an index on {ping: 1} named "ping_1" when stepping
+ * up to primary - conflicting with index naming restrictions that there cannot be 2 identical
+ * indexes with different names.
+ *
+ * Tests that upgrading a config server to v4.2 fails to start up and relays a message to drop the
+ * "ping_" index introduced in v2.4 before upgrade.
+ */
+(function() {
+"use strict";
+
+const cluster = new ShardingTest({
+ shards: 2,
+ other: {
+ mongosOptions: {binVersion: "last-stable"},
+ configOptions: {
+ binVersion: "last-stable",
+ },
+ rsOptions: {binVersion: "last-stable"},
+ },
+ rs: {nodes: 3}
+});
+const configRS = cluster.configRS;
+
+// Returns the 'config' database from the cluster's current primary config server.
+const getConfigDB = function() {
+ return configRS.getPrimary().getDB("config");
+};
+
+// Asserts that an index with indexName exists in on the config.lockpings collection.
+const assertLockpingsHasIndex = function(indexName, configDB) {
+ const listIndexesResult = assert.commandWorked(configDB.runCommand({listIndexes: "lockpings"}));
+
+ const containsIndex = listIndexesResult.cursor.firstBatch.reduce((acc, indexSpec) => {
+ return acc || indexSpec.name == indexName;
+ }, false);
+
+ assert.eq(true,
+ containsIndex,
+ `Index with name ${indexName} does not exist: ${tojson(listIndexesResult)}`);
+};
+
+jsTest.log("Setting up collection to contain index 'ping_'");
+// Drop the implicitly created "ping_1" index to allow for the creation of the "ping_"
+// index.
+let configDB = getConfigDB();
+assert.commandWorked(configDB["lockpings"].dropIndex({ping: 1}));
+assert.commandWorked(
+ configDB.runCommand({createIndexes: "lockpings", indexes: [{key: {ping: 1}, name: "ping_"}]}));
+assertLockpingsHasIndex("ping_", configDB);
+
+jsTest.log("Testing secondary crashes on upgrade to 'latest' due to 'ping_' index");
+const secondary = configRS.getSecondary();
+try {
+ configRS.restart(secondary, {binVersion: "latest"});
+} catch (error) {
+ // Catch the error thrown when the secondary crashes during restart.
+}
+assert.soon(() => {
+ // Confirm the secondary crashed with the fatal assertion with instructions to drop the "ping_"
+ // index.
+ return rawMongoProgramOutput().search(/Fatal assertion 5272800/) != -1;
+});
+
+jsTest.log("Restarting secondary as 'last-stable' to drop 'ping_' index");
+// Before upgrading, user must drop the "ping_" index and await replication.
+configRS.restart(secondary, {binVersion: "last-stable", allowedExitCode: MongoRunner.EXIT_ABORT});
+assert.commandWorked(configDB["lockpings"].dropIndex("ping_"));
+configRS.awaitReplication();
+
+jsTest.log("Testing rolling upgrade succeeds in absence of 'ping_' index");
+// First upgrade both secondaries.
+const secondaries = configRS.getSecondaries();
+configRS.restart(secondaries[0], {binVersion: "latest"});
+configRS.restart(secondaries[1], {binVersion: "latest"});
+
+// Step up a new primary before upgrading the existing primary.
+const originalPrimary = configRS.getPrimary();
+configRS.getSecondary().adminCommand({replSetStepUp: 1});
+configRS.restart(originalPrimary, {binVersion: "latest"});
+
+configRS.awaitNodesAgreeOnPrimary();
+
+// The "ping_1" index should exist once an upgraded (v4.2) config server steps up to primary.
+assertLockpingsHasIndex("ping_1", getConfigDB());
+
+cluster.stop();
+})();
diff --git a/src/mongo/db/s/sharding_initialization_mongod.cpp b/src/mongo/db/s/sharding_initialization_mongod.cpp
index 659997e7dc3..06617f93b2c 100644
--- a/src/mongo/db/s/sharding_initialization_mongod.cpp
+++ b/src/mongo/db/s/sharding_initialization_mongod.cpp
@@ -38,6 +38,7 @@
#include "mongo/client/remote_command_targeter.h"
#include "mongo/client/remote_command_targeter_factory_impl.h"
#include "mongo/client/replica_set_monitor.h"
+#include "mongo/db/catalog/index_catalog.h"
#include "mongo/db/catalog_raii.h"
#include "mongo/db/concurrency/d_concurrency.h"
#include "mongo/db/dbhelpers.h"
@@ -55,6 +56,7 @@
#include "mongo/db/server_options.h"
#include "mongo/executor/task_executor_pool.h"
#include "mongo/rpc/metadata/egress_metadata_hook_list.h"
+#include "mongo/s/catalog/type_lockpings.h"
#include "mongo/s/catalog_cache.h"
#include "mongo/s/client/shard_connection.h"
#include "mongo/s/client/shard_factory.h"
@@ -138,6 +140,35 @@ private:
ServiceContext* _serviceContext;
};
+// In v2.4, an index on {ping: 1} with index name "ping_" was created on the config.lockpings
+// collection due to a bug. The existence of "ping_" clashes with an identical index with a
+// different name, "ping_1", that is created internally when a secondary steps up to primary.
+//
+// If the "ping_" index exists, fatally prevent nodes from upgrading until the index is dropped.
+void assertLegacyPingIndexDoesNotExist(OperationContext* opCtx) {
+ if (!serverGlobalParams.featureCompatibility.isVersionInitialized() ||
+ serverGlobalParams.featureCompatibility.getVersion() !=
+ ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo40) {
+ // The server is not upgrading to 4.2, bypass the check.
+ return;
+ }
+
+ AutoGetDb autoDb(opCtx, LockpingsType::ConfigNS.db(), MODE_IS);
+ Collection* collection = autoDb.getDb()->getCollection(opCtx, LockpingsType::ConfigNS);
+ if (collection) {
+ IndexCatalog* indexCatalog = collection->getIndexCatalog();
+ invariant(indexCatalog);
+
+ auto desc = indexCatalog->findIndexByName(opCtx, "ping_");
+ if (desc) {
+ auto status = Status(ErrorCodes::IndexOptionsConflict,
+ "Must drop index 'ping_' on config.lockpings and await "
+ "replication before upgrading");
+ fassertFailedWithStatus(5272800, status);
+ }
+ }
+}
+
} // namespace
void ShardingInitializationMongoD::initializeShardingEnvironmentOnShardServer(
@@ -279,6 +310,10 @@ bool ShardingInitializationMongoD::initializeShardingAwarenessIfNeeded(Operation
return true;
} else {
+ if (serverGlobalParams.clusterRole == ClusterRole::ConfigServer) {
+ assertLegacyPingIndexDoesNotExist(opCtx);
+ }
+
// Warn if a shardIdentity document is found on disk but *not* started with --shardsvr.
if (!shardIdentityBSON.isEmpty()) {
warning() << "Not started with --shardsvr, but a shardIdentity document was found "
diff --git a/src/mongo/shell/servers.js b/src/mongo/shell/servers.js
index 01f725e7159..349f698579d 100644
--- a/src/mongo/shell/servers.js
+++ b/src/mongo/shell/servers.js
@@ -241,6 +241,7 @@ MongoRunner.logicalOptions = {
waitForConnect: true,
bridgeOptions: true,
skipValidation: true,
+ allowedExitCode: true,
};
MongoRunner.toRealPath = function(path, pathOpts) {