summaryrefslogtreecommitdiff
path: root/jstests/core/currentop_shell.js
blob: 1af9b86cd96d6835d4e37104812ec63bb92f62b2 (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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
 * Tests that the shell helper db.currentOpCursor isn't constrained by the legacy currentOp server
 * command - ie. the result set isn't limited to 16MB and long operations aren't truncated.
 *
 * The test runs commands that are not allowed with security token: getLog.
 * @tags: [
 *   not_allowed_with_security_token,
 *   uses_parallel_shell,
 *   # This test uses currentOp to check whether an aggregate command is running. In replica set
 *   # environments, because currentOp is run against the admin database it is routed to the
 *   # primary, while the aggregate may be routed to a secondary. If currentOp is running on one
 *   # node and the expected command is run on another, the latter will not show up in the
 *   # currentOp results.
 *   assumes_read_preference_unchanged,
 *   no_selinux,
 *   # Uses $function operator.
 *   requires_scripting,
 *   #TODO SERVER-72404 Unblock this test from the telemetry passthrough
 *   exclude_from_telemetry_passthrough,
 * ]
 */

(function() {
"use strict";

load("jstests/libs/fixture_helpers.js");  // for FixtureHelpers

const coll = db.currentOp_cursor;
coll.drop();

for (let i = 0; i < 3; i++) {
    assert.commandWorked(coll.insert({val: 1}));
}

// Test that db.currentOpCursor() returns an iterable cursor.
let res = db.currentOpCursor();
assert(res.hasNext());
assert(res.next());

// Test that db.currentOp() interface does not change.
res = db.currentOp();
assert("inprog" in res, "Result contains 'inprog' field");
assert("ok" in res, "Result contains 'ok' field");

// Attempting to access the fsyncLock field from the results throws with an error message.
let error = assert.throws(() => res.fsyncLock);
assert(
    /fsyncLock is no longer included in the currentOp shell helper, run db\.runCommand\({currentOp: 1}\) instead/
        .test(error));

function shellOp() {
    function createLargeDoc() {
        let doc = {};
        for (let i = 0; i < 100; i++) {
            doc[i] = "Testing testing 1 2 3...";
        }
        return doc;
    }

    assert.commandFailedWithCode(db.runCommand({
        aggregate: "currentOp_cursor",
        pipeline: [{
            $addFields: {
                newVal: {$function: {args: [], body: "sleep(1000000)", lang: "js"}},
                bigDoc: createLargeDoc()
            }
        }],
        comment: TestData.comment,
        cursor: {}
    }),
                                 ErrorCodes.Interrupted);
}

function startShellWithOp(comment) {
    TestData.comment = comment;
    const awaitShell = startParallelShell(shellOp);

    // Confirm that the operation has started in the parallel shell.
    assert.soon(
        function() {
            let aggRes =
                db.getSiblingDB("admin")
                    .aggregate([
                        {$currentOp: {}},
                        {$match: {ns: "test.currentOp_cursor", "command.comment": TestData.comment}}
                    ])
                    .toArray();
            return aggRes.length >= 1;
        },
        function() {
            return "Failed to find parallel shell operation in $currentOp output: " +
                tojson(db.currentOp());
        });
    return awaitShell;
}

// Test that the currentOp server command truncates long operations with a warning logged.
const serverCommandTest = startShellWithOp("currentOp_server");
res = db.adminCommand({
    currentOp: true,
    $and: [{"ns": "test.currentOp_cursor"}, {"command.comment": "currentOp_server"}]
});

if (FixtureHelpers.isMongos(db) && FixtureHelpers.isSharded(coll)) {
    // Assert currentOp truncation behavior for each shard in the cluster.
    assert(res.inprog.length >= 1, res);
    res.inprog.forEach((result) => {
        assert.eq(result.op, "getmore", result);
        assert(result.cursor.originatingCommand.hasOwnProperty("$truncated"), result);
    });
} else {
    // Assert currentOp truncation behavior for unsharded collections.
    assert.eq(res.inprog.length, 1, res);
    assert.eq(res.inprog[0].op, "command", res);
    assert(res.inprog[0].command.hasOwnProperty("$truncated"), res);
}

const log = FixtureHelpers.getPrimaryForNodeHostingDatabase(db).adminCommand({getLog: "global"});
assert(/will be truncated/.test(log.log));

res.inprog.forEach((op) => {
    assert.commandWorked(db.killOp(op.opid));
});

serverCommandTest();

// Test that the db.currentOp() shell helper does not truncate ops.
const shellHelperTest = startShellWithOp("currentOp_shell");
res = db.currentOp({"ns": "test.currentOp_cursor", "command.comment": "currentOp_shell"});

if (FixtureHelpers.isMongos(db) && FixtureHelpers.isSharded(coll)) {
    assert(res.inprog.length >= 1, res);
    res.inprog.forEach((result) => {
        assert.eq(result.op, "getmore", result);
        assert(!result.cursor.originatingCommand.hasOwnProperty("$truncated"), result);
    });
} else {
    assert.eq(res.inprog.length, 1, res);
    assert(!res.inprog[0].command.hasOwnProperty("$truncated"), res);
}

res.inprog.forEach((op) => {
    assert.commandWorked(db.killOp(op.opid));
});

shellHelperTest();
})();