summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Wlodarek <gregory.wlodarek@mongodb.com>2019-02-21 11:24:31 -0500
committerGregory Wlodarek <gregory.wlodarek@mongodb.com>2019-02-25 15:36:04 -0500
commit54cc4d76250719b247080c1195d4b672322d989e (patch)
tree09298afdf6d20c8aba20cd7f10d2151ced3e3c11
parentf5e0120d8b8ac05baea642f423abb55a5a8b8f65 (diff)
downloadmongo-54cc4d76250719b247080c1195d4b672322d989e.tar.gz
SERVER-39719 Ensure that a node performing an initial sync from a secondary with a in-progress index build creates the index before initial sync is done
(cherry picked from commit efefc4f94203c3e6119916b89255017d117e982f)
-rw-r--r--jstests/replsets/secondary_as_sync_source.js88
-rw-r--r--src/mongo/shell/replsettest.js9
2 files changed, 97 insertions, 0 deletions
diff --git a/jstests/replsets/secondary_as_sync_source.js b/jstests/replsets/secondary_as_sync_source.js
new file mode 100644
index 00000000000..6f446842daa
--- /dev/null
+++ b/jstests/replsets/secondary_as_sync_source.js
@@ -0,0 +1,88 @@
+/**
+ * Tests that a new replica set member performing an initial sync from a secondary node as the sync
+ * source, which has an in-progress index build will also build the index as part of the initial
+ * sync operation.
+ * @tags: [requires_replication]
+ */
+(function() {
+ 'use strict';
+
+ load("jstests/replsets/rslib.js");
+
+ const dbName = "test";
+ const collName = "coll";
+
+ const firstIndexName = "_first";
+
+ function addTestDocuments(db) {
+ let size = 100;
+ jsTest.log("Creating " + size + " test documents.");
+ var bulk = db.getCollection(collName).initializeUnorderedBulkOp();
+ for (var i = 0; i < size; ++i) {
+ bulk.insert({i: i});
+ }
+ assert.writeOK(bulk.execute());
+ }
+
+ let replSet = new ReplSetTest({name: "indexBuilds", nodes: 2, useBridge: true});
+ let nodes = replSet.nodeList();
+
+ replSet.startSet({startClean: true});
+ replSet.initiate({
+ _id: "indexBuilds",
+ members: [
+ {_id: 0, host: nodes[0]},
+ {_id: 1, host: nodes[1], votes: 0, priority: 0},
+ ]
+ });
+
+ let primary = replSet.getPrimary();
+ let primaryDB = primary.getDB(dbName);
+
+ let secondary = replSet.getSecondary();
+ let secondaryDB = secondary.getDB(dbName);
+
+ addTestDocuments(primaryDB);
+
+ jsTest.log("Hanging index builds on the secondary node");
+ assert.commandWorked(secondaryDB.adminCommand(
+ {configureFailPoint: "hangAfterStartingIndexBuild", mode: "alwaysOn"}));
+
+ jsTest.log("Beginning index build: " + firstIndexName);
+ assert.commandWorked(primaryDB.runCommand({
+ createIndexes: collName,
+ indexes: [{key: {i: 1}, name: firstIndexName, background: true}],
+ writeConcern: {w: 2}
+ }));
+
+ jsTest.log("Adding a new node to the replica set");
+ let newNode = replSet.add({rsConfig: {votes: 0, priority: 0}});
+
+ // Ensure that the new node and primary cannot communicate to each other.
+ newNode.disconnect(primary);
+
+ replSet.reInitiate();
+
+ // Wait for the new node to finish initial sync.
+ waitForState(newNode, ReplSetTest.State.SECONDARY);
+
+ // Let the 'secondary' finish its index build.
+ jsTest.log("Removing index build hang on the secondary node to allow it to finish");
+ assert.commandWorked(
+ secondaryDB.adminCommand({configureFailPoint: "hangAfterStartingIndexBuild", mode: "off"}));
+
+ // Wait for the index builds to finish.
+ replSet.waitForAllIndexBuildsToFinish(dbName, collName);
+ jsTest.log("Checking if the indexes match between the new node and the secondary node");
+
+ let newNodeDB = newNode.getDB(dbName);
+ jsTest.log("New nodes indexes:");
+ printjson(newNodeDB.getCollection(collName).getIndexes());
+ jsTest.log("Secondary nodes indexes:");
+ printjson(secondaryDB.getCollection(collName).getIndexes());
+
+ assert.eq(newNodeDB.getCollection(collName).getIndexes().length,
+ secondaryDB.getCollection(collName).getIndexes().length);
+
+ replSet.stopSet();
+})();
diff --git a/src/mongo/shell/replsettest.js b/src/mongo/shell/replsettest.js
index aeae8d76eec..5848b2f288e 100644
--- a/src/mongo/shell/replsettest.js
+++ b/src/mongo/shell/replsettest.js
@@ -1475,6 +1475,15 @@ var ReplSetTest = function(opts) {
}, "awaiting replication", timeout);
};
+ this.waitForAllIndexBuildsToFinish = function(dbName, collName) {
+ // Run a no-op command and wait for it to be applied on secondaries. Due to the asynchronous
+ // completion nature of indexes on secondaries, we can guarantee an index build is complete
+ // on all secondaries once all secondaries have applied this collMod command.
+ assert.commandWorked(this.getPrimary().getDB(dbName).runCommand(
+ {collMod: collName, usePowerOf2Sizes: true}));
+ this.awaitReplication();
+ };
+
this.getHashesUsingSessions = function(sessions, dbName, {
filterCapped: filterCapped = true,
filterMapReduce: filterMapReduce = true,