summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Williams <louis.williams@mongodb.com>2019-01-15 17:35:43 -0500
committerLouis Williams <louis.williams@mongodb.com>2019-01-17 15:54:40 -0500
commit9e87f6e272df4f97013dfccc4018efb79f68653a (patch)
treedc37d192f1821eba066af51c1ad9035a57e6c954
parentb956c24930044004fb229adc52e34f20d81a36bb (diff)
downloadmongo-9e87f6e272df4f97013dfccc4018efb79f68653a.tar.gz
SERVER-37272 Disable hybrid index builds on FCV 4.0
-rw-r--r--jstests/multiVersion/hybrid_indexes.js109
-rw-r--r--src/mongo/db/catalog/multi_index_block.cpp13
2 files changed, 121 insertions, 1 deletions
diff --git a/jstests/multiVersion/hybrid_indexes.js b/jstests/multiVersion/hybrid_indexes.js
new file mode 100644
index 00000000000..ecebef1d543
--- /dev/null
+++ b/jstests/multiVersion/hybrid_indexes.js
@@ -0,0 +1,109 @@
+/**
+ * Tests that hybrid index builds are only enabled in FCV 4.2.
+ */
+(function() {
+ 'use strict';
+
+ const dbName = "test";
+ const collName = "hybrid_indexes";
+ const dbpath = MongoRunner.dataPath + "hybrid_indexes";
+
+ load("jstests/libs/feature_compatibility_version.js");
+
+ let conn = MongoRunner.runMongod({binVersion: "latest", cleanData: true, dbpath: dbpath});
+ let testDB = conn.getDB(dbName);
+ let testColl = testDB[collName];
+ testColl.insert({i: 0});
+ assert.commandWorked(conn.adminCommand({setFeatureCompatibilityVersion: "4.0"}));
+
+ let buildIndex = function(config) {
+ const background = config.background;
+ const expected = config.expected;
+
+ let res = testDB.adminCommand({getParameter: 1, featureCompatibilityVersion: 1});
+ assert.commandWorked(res);
+ let fcv = res.version;
+
+ clearRawMongoProgramOutput();
+ assert.commandWorked(testDB.adminCommand(
+ {configureFailPoint: 'hangBeforeIndexBuildOf', mode: "alwaysOn", data: {"i": 0}}));
+
+ let awaitBuild;
+ if (background) {
+ awaitBuild = startParallelShell(function() {
+ assert.commandWorked(db.hybrid_indexes.createIndex({i: 1}, {background: true}));
+ }, conn.port);
+ } else {
+ awaitBuild = startParallelShell(function() {
+ assert.commandWorked(db.hybrid_indexes.createIndex({i: 1}, {background: false}));
+ }, conn.port);
+ }
+
+ let msg =
+ "starting on test.hybrid_indexes properties: { v: 2, key: { i: 1.0 }, name: \"i_1\"" +
+ ", ns: \"test.hybrid_indexes\", background: " + background + " } using method: " +
+ expected;
+ print(msg);
+ assert.soon(
+ () => rawMongoProgramOutput().indexOf(msg) >= 0, "Index build not started", 1000);
+ assert.soon(() => rawMongoProgramOutput().indexOf("Hanging before index build of i=0") >= 0,
+ "Index build not hanging",
+ 1000);
+
+ if (expected === "Background" || expected === "Hybrid") {
+ assert.commandWorked(testColl.insert({i: 1}));
+ } else {
+ assert.commandFailedWithCode(
+ testDB.runCommand({insert: collName, documents: [{i: 2}], maxTimeMS: 100}),
+ ErrorCodes.MaxTimeMSExpired);
+ }
+
+ assert.commandWorked(
+ testDB.adminCommand({configureFailPoint: 'hangBeforeIndexBuildOf', mode: "off"}));
+ awaitBuild();
+ assert.commandWorked(testColl.dropIndex("i_1"));
+ };
+
+ // Test: Background indexes behave as background indexes on FCV 4.0.
+
+ buildIndex({background: true, expected: "Background"});
+
+ // Test: Foreground indexes behave as foreground idnexes on FCV 4.0.
+
+ buildIndex({background: false, expected: "Foreground"});
+
+ // Test: Upgrade to FCV 4.2 while a background index build is in progress fails. This is subject
+ // to change, but characterizes the current behavior.
+
+ clearRawMongoProgramOutput();
+ assert.commandWorked(testDB.adminCommand(
+ {configureFailPoint: 'hangAfterStartingIndexBuildUnlocked', mode: "alwaysOn"}));
+
+ let awaitBuild = startParallelShell(function() {
+ // This fails because of the unlock failpoint.
+ assert.commandFailedWithCode(db.hybrid_indexes.createIndex({i: 1}, {background: true}),
+ ErrorCodes.OperationFailed);
+ }, conn.port);
+
+ assert.soon(() => rawMongoProgramOutput().indexOf("Hanging index build with no locks") >= 0,
+ "Index build not hanging");
+
+ assert.commandFailedWithCode(testDB.adminCommand({setFeatureCompatibilityVersion: "4.2"}),
+ ErrorCodes.BackgroundOperationInProgressForNamespace);
+
+ assert.commandWorked(testDB.adminCommand(
+ {configureFailPoint: 'hangAfterStartingIndexBuildUnlocked', mode: "off"}));
+ awaitBuild();
+
+ // Test: Background indexes behave as hybrid indexes on FCV 4.2.
+
+ assert.commandWorked(conn.adminCommand({setFeatureCompatibilityVersion: "4.2"}));
+
+ buildIndex({background: true, expected: "Hybrid"});
+
+ // Test: Foreground indexes behave as hybrid indexes on FCV 4.2.
+
+ buildIndex({background: false, expected: "Hybrid"});
+
+ MongoRunner.stopMongod(conn);
+})();
diff --git a/src/mongo/db/catalog/multi_index_block.cpp b/src/mongo/db/catalog/multi_index_block.cpp
index 2dcfe29b4e3..93389f0567b 100644
--- a/src/mongo/db/catalog/multi_index_block.cpp
+++ b/src/mongo/db/catalog/multi_index_block.cpp
@@ -140,7 +140,18 @@ bool MultiIndexBlock::areHybridIndexBuildsEnabled() {
return false;
}
- // TODO: SERVER-37272 Disable hybrid if in FCV 4.0.
+ // Hybrid index builds must only be used when in FCV 4.2. This restriction is due to the case
+ // where an index build starts in FCV 4.0, then continues during an upgrade to FCV 4.2. Because
+ // prepared transactions yield locks on secondaries, hybrid index builds may miss prepared, but
+ // uncommitted writes, leading to data corruption. With two-phase index builds, an FCV 4.2-only
+ // feature, the hybrid build will not complete until the primary writes an oplog entry
+ // indicating the index build can finish, implying that there are no uncommitted prepared
+ // transactions.
+ if (!serverGlobalParams.featureCompatibility.isVersionInitialized() ||
+ serverGlobalParams.featureCompatibility.getVersion() !=
+ ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo42) {
+ return false;
+ }
return enableHybridIndexBuilds.load();
}