summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/interrupt_while_yielded.js
blob: b063fb98b8c963e53d94357077b0b3d96e881412 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**
 * @tags: [
 *   # TODO SERVER-64007: Support yielding in CQF plans.
 *   cqf_incompatible,
 * ]
 */
(function() {
"use strict";

const kFailPointName = "setYieldAllLocksHang";
const kCommandComment = "interruptedWhileYieldedComment";

const conn = MongoRunner.runMongod();
assert.neq(null, conn, "mongod was unable to start up");
const db = conn.getDB("test");
const coll = db.interrupt_while_yielded;

coll.drop();
assert.commandWorked(coll.insert({a: 1, b: 1, c: 1}));
assert.commandWorked(coll.insert({a: 1, b: 1, c: 1}));

assert.commandWorked(coll.createIndex({a: 1, b: 1}));
assert.commandWorked(coll.createIndex({a: 1, c: 1}));

// This is needed to make sure that a yield point is reached.
assert.commandWorked(db.adminCommand({setParameter: 1, internalQueryExecYieldIterations: 1}));

/**
 * Executes 'queryFn' in a parallel shell while a failpoint is enabled to hang operations during
 * yield. Ensures that operation run by 'queryFn' reaches the yield point, then runs killOp()
 * against the yielded operation.
 */
function runTestWithQuery(queryFn) {
    let waitForParallelShell = null;

    try {
        assert.commandWorked(db.adminCommand({
            configureFailPoint: kFailPointName,
            mode: "alwaysOn",
            data: {namespace: coll.getFullName(), checkForInterruptAfterHang: true}
        }));

        // Run a command that should hit the fail point in a parallel shell.
        let code = `let queryFn = ${queryFn};`;
        code += `const coll = db.${coll.getName()};`;
        code += `const kCommandComment = "${kCommandComment}";`;
        function parallelShellFn() {
            const err = assert.throws(queryFn);
            assert.commandFailedWithCode(err, [ErrorCodes.Interrupted]);
        }
        code += "(" + parallelShellFn.toString() + ")();";

        waitForParallelShell = startParallelShell(code, conn.port);

        // Find the operation running the query.
        let opId = null;

        assert.soon(function() {
            const ops = db.getSiblingDB("admin")
                            .aggregate([
                                {$currentOp: {allUsers: true, localOps: true}},
                                {
                                    $match: {
                                        numYields: {$gt: 0},
                                        ns: coll.getFullName(),
                                        "command.comment": kCommandComment
                                    }
                                }
                            ])
                            .toArray();

            if (ops.length > 0) {
                assert.eq(ops.length, 1);
                opId = ops[0].opid;
                return true;
            }

            return false;
        });

        // Kill the op.
        db.killOp(opId);

    } finally {
        // Disable the failpoint so that the server will continue, and hit an interrupt check.
        assert.commandWorked(db.adminCommand({configureFailPoint: kFailPointName, mode: "off"}));

        if (waitForParallelShell) {
            waitForParallelShell();
        }
    }

    // Check that the server is still up.
    assert.commandWorked(db.adminCommand({hello: 1}));
}

function rootedOr() {
    coll.find({$or: [{a: 1}, {b: 1}]}).comment(kCommandComment).itcount();
}
runTestWithQuery(rootedOr);

function groupFindDistinct() {
    coll.aggregate([{$group: {_id: "$a"}}], {comment: kCommandComment}).itcount();
}
runTestWithQuery(groupFindDistinct);

function projectImmediatelyAfterMatch() {
    coll.aggregate([{$match: {a: 1}}, {$project: {_id: 0, a: 1}}, {$unwind: "$a"}],
                   {comment: kCommandComment})
        .itcount();
}
runTestWithQuery(projectImmediatelyAfterMatch);

function sortImmediatelyAfterMatch() {
    coll.aggregate([{$match: {a: 1, b: 1, c: 1}}, {$sort: {a: 1}}], {comment: kCommandComment})
        .itcount();
}
runTestWithQuery(sortImmediatelyAfterMatch);

function sortAndProjectionImmediatelyAfterMatch() {
    coll.aggregate([{$match: {a: 1}}, {$project: {_id: 0, a: 1}}, {$sort: {a: 1}}],
                   {comment: kCommandComment})
        .itcount();
}
runTestWithQuery(sortAndProjectionImmediatelyAfterMatch);

MongoRunner.stopMongod(conn);
}());