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);
}());
|