summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/child_op_numyields.js
diff options
context:
space:
mode:
authorsamontea <merciers.merciers@gmail.com>2018-05-31 13:23:16 -0400
committersamontea <merciers.merciers@gmail.com>2018-06-21 13:24:55 -0400
commitd80d5ec7a75f6f32a27712ca4e904350318ce8c9 (patch)
tree5fccc9ae8d6e04df0ca1129d6278ab8bcae5645e /jstests/noPassthrough/child_op_numyields.js
parentf462615fce8b26ad43c6ba1b4f9fe739c3625344 (diff)
downloadmongo-d80d5ec7a75f6f32a27712ca4e904350318ce8c9.tar.gz
SERVER-33870 make numYields of parent CurOps reflect child yields
Diffstat (limited to 'jstests/noPassthrough/child_op_numyields.js')
-rw-r--r--jstests/noPassthrough/child_op_numyields.js120
1 files changed, 120 insertions, 0 deletions
diff --git a/jstests/noPassthrough/child_op_numyields.js b/jstests/noPassthrough/child_op_numyields.js
new file mode 100644
index 00000000000..04c79d308ba
--- /dev/null
+++ b/jstests/noPassthrough/child_op_numyields.js
@@ -0,0 +1,120 @@
+/**
+ * Confirms that a parent operation correctly inherits 'numYields' from each of its child operations
+ * as the latter are popped off the CurOp stack.
+ */
+(function() {
+ "use strict";
+
+ // Start a single mongoD using MongoRunner.
+ const conn = MongoRunner.runMongod({});
+ assert.neq(null, conn, "mongod was unable to start up");
+
+ // Create the test DB and collection.
+ const testDB = conn.getDB("currentop_yield");
+ const adminDB = conn.getDB("admin");
+ const testColl = testDB.test;
+
+ // Queries current operations until a single matching operation is found.
+ function awaitMatchingCurrentOp(match) {
+ let currentOp = null;
+ assert.soon(() => {
+ currentOp = adminDB.aggregate([{$currentOp: {}}, match]).toArray();
+ return (currentOp.length === 1);
+ });
+ return currentOp[0];
+ }
+
+ // Executes a bulk remove using the specified 'docsToRemove' array, captures the 'numYields'
+ // metrics from each child op, and confirms that the parent op's 'numYields' total is equivalent
+ // to the sum of the child ops.
+ function runYieldTest(docsToRemove) {
+ // Sets parameters such that all operations will yield & the operation hangs on the server
+ // when we need to test.
+ assert.commandWorked(
+ testDB.adminCommand({setParameter: 1, internalQueryExecYieldIterations: 1}));
+ assert.commandWorked(testDB.adminCommand(
+ {configureFailPoint: "hangBeforeChildRemoveOpFinishes", mode: "alwaysOn"}));
+ assert.commandWorked(testDB.adminCommand(
+ {configureFailPoint: "hangAfterAllChildRemoveOpsArePopped", mode: "alwaysOn"}));
+
+ // Starts parallel shell to run the command that will hang.
+ const awaitShell = startParallelShell(`{
+ const testDB = db.getSiblingDB("currentop_yield");
+ const bulkRemove = testDB.test.initializeOrderedBulkOp();
+ for(let doc of ${tojsononeline(docsToRemove)}) {
+ bulkRemove.find(doc).removeOne();
+ }
+ bulkRemove.execute();
+ }`,
+ testDB.getMongo().port);
+
+ let childOpId = null;
+ let childYields = 0;
+
+ // Get child operations and sum yields. Each child op encounters two failpoints while
+ // running: 'hangBeforeChildRemoveOpFinishes' followed by 'hangBeforeChildRemoveOpIsPopped'.
+ // We use these two failpoints as an 'airlock', hanging at the first while we enable the
+ // second, then hanging at the second while we enable the first, to ensure that each child
+ // op is caught and their individual 'numYields' recorded.
+ for (let childCount = 0; childCount < docsToRemove.length; childCount++) {
+ // Wait for the child op to hit the first of two failpoints.
+ let childCurOp = awaitMatchingCurrentOp(
+ {$match: {ns: testColl.getFullName(), msg: "hangBeforeChildRemoveOpFinishes"}});
+
+ // Add the child's yield count to the running total, and record the opid.
+ assert(childOpId === null || childOpId === childCurOp.opid);
+ assert.gt(childCurOp.numYields, 0);
+ childYields += childCurOp.numYields;
+ childOpId = childCurOp.opid;
+
+ // Enable the subsequent 'hangBeforeChildRemoveOpIsPopped' failpoint, just after the
+ // child op finishes but before it is popped from the stack.
+ assert.commandWorked(testDB.adminCommand(
+ {configureFailPoint: "hangBeforeChildRemoveOpIsPopped", mode: "alwaysOn"}));
+
+ // Let the operation proceed to the 'hangBeforeChildRemoveOpIsPopped' failpoint.
+ assert.commandWorked(testDB.adminCommand(
+ {configureFailPoint: "hangBeforeChildRemoveOpFinishes", mode: "off"}));
+ awaitMatchingCurrentOp(
+ {$match: {ns: testColl.getFullName(), msg: "hangBeforeChildRemoveOpIsPopped"}});
+
+ // If this is not the final child op, re-enable the 'hangBeforeChildRemoveOpFinishes'
+ // failpoint from earlier so that we don't miss the next child.
+ if (childCount + 1 < docsToRemove.length) {
+ assert.commandWorked(testDB.adminCommand(
+ {configureFailPoint: "hangBeforeChildRemoveOpFinishes", mode: "alwaysOn"}));
+ }
+
+ // Finally, allow the operation to continue.
+ assert.commandWorked(testDB.adminCommand(
+ {configureFailPoint: "hangBeforeChildRemoveOpIsPopped", mode: "off"}));
+ }
+
+ // Wait for the operation to hit the 'hangAfterAllChildRemoveOpsArePopped' failpoint, then
+ // take the total number of yields recorded by the parent op.
+ const parentCurOp = awaitMatchingCurrentOp(
+ {$match: {opid: childOpId, op: "command", msg: "hangAfterAllChildRemoveOpsArePopped"}});
+
+ // Verify that the parent's yield count equals the sum of the child ops' yields.
+ assert.eq(parentCurOp.numYields, childYields);
+ assert.eq(parentCurOp.opid, childOpId);
+
+ // Allow the parent operation to complete.
+ assert.commandWorked(testDB.adminCommand(
+ {configureFailPoint: "hangAfterAllChildRemoveOpsArePopped", mode: "off"}));
+
+ // Wait for the parallel shell to complete.
+ awaitShell();
+ }
+
+ // Test that a parent remove op inherits the sum of its children's yields for a single remove.
+ assert.commandWorked(testDB.test.insert({a: 2}));
+ runYieldTest([{a: 2}]);
+
+ // Test that a parent remove op inherits the sum of its children's yields for multiple removes.
+ const docsToTest = [{a: 1}, {a: 2}, {a: 3}, {a: 4}, {a: 5}];
+ assert.commandWorked(testDB.test.insert(docsToTest));
+ runYieldTest(docsToTest);
+
+ MongoRunner.stopMongod(conn);
+})();