summaryrefslogtreecommitdiff
path: root/jstests/libs/profiler.js
blob: 63af457299b5fee76bb2e6cf9ec4c7111a9f1dd8 (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
// Provides convenience methods for confirming system.profile content.

// Given a command, build its expected shape in the system profiler.
function buildCommandProfile(command, sharded) {
    let commandProfile = {};

    if (sharded && command.mapReduce) {
        // Unlike other read commands, mapReduce is rewritten to a different format when sent to
        // shards if the input collection is sharded, because it is executed in two phases.
        // We do not check for the 'map' and 'reduce' fields, because they are functions, and
        // we cannot compaare functions for equality.
        commandProfile["command.out"] = {$regex: "^tmp.mrs"};
        commandProfile["command.shardedFirstPass"] = true;
    } else if (command.update) {
        // Updates are batched, but only allow using buildCommandProfile() for an update batch that
        // contains a single update, since the profiler generates separate entries for each update
        // in the batch.
        assert(command.updates.length == 1);
        for (let key in command.updates[0]) {
            commandProfile["command." + key] = command.updates[0][key];
        }
        // Though 'upsert' and 'multi' are optional fields, they are written with the default value
        // in the profiler.
        commandProfile["command.upsert"] = commandProfile["command.upsert"] || false;
        commandProfile["command.multi"] = commandProfile["command.multi"] || false;
    } else if (command.delete) {
        // Deletes are batched, but only allow using buildCommandProfile() for a delete batch that
        // contains a single delete, since the profiler generates separate entries for each delete
        // in the batch.
        assert(command.deletes.length == 1);
        for (let key in command.deletes[0]) {
            commandProfile["command." + key] = command.deletes[0][key];
        }
    } else {
        for (let key in command) {
            commandProfile["command." + key] = command[key];
        }
    }
    return commandProfile;
}

// Retrieve latest system.profile entry.
function getLatestProfilerEntry(profileDB, filter) {
    if (filter === null) {
        filter = {};
    }
    var cursor = profileDB.system.profile.find(filter).sort({$natural: -1});
    assert(
        cursor.hasNext(),
        "could not find any entries in the profile collection matching filter: " + tojson(filter));
    return cursor.next();
}

// Returns a string representing the wire protocol used for commands run on the given connection.
// This string matches the system.profile "protocol" field when commands are profiled.
function getProfilerProtocolStringForCommand(conn) {
    const protocols = conn.getClientRPCProtocols();
    if ("all" === protocols || /Msg/.test(protocols))
        return "op_msg";
    if (/Query/.test(protocols))
        return "op_query";
    doassert(`Unknown prototocol string ${protocols}`);
}

/**
 * Throws an assertion if the profiler contains more than 'maxExpectedMatches' entries matching
 * "filter", or if there are no matches. Optional arguments "errorMsgFilter" and "errorMsgProj"
 * limit profiler output if this asserts.
 */
function profilerHasAtLeastOneAtMostNumMatchingEntriesOrThrow(
    {profileDB, filter, maxExpectedMatches, errorMsgFilter, errorMsgProj}) {
    assert(typeof maxExpectedMatches === 'number' && maxExpectedMatches > 0,
           "'maxExpectedMatches' must be a number > 0");

    const numMatches = profileDB.system.profile.find(filter).itcount();

    assert.gt(numMatches,
              0,
              "Expected at least 1 op matching: " + tojson(filter) + " in profiler " +
                  tojson(profileDB.system.profile.find(errorMsgFilter, errorMsgProj).toArray()));

    assert.lte(numMatches,
               maxExpectedMatches,
               "Expected at most " + maxExpectedMatches + " op(s) matching: " + tojson(filter) +
                   " in profiler " +
                   tojson(profileDB.system.profile.find(errorMsgFilter, errorMsgProj).toArray()));
}

/**
 * Throws an assertion if the profiler does not contain exactly 'numExpectedMatches' entries
 * matching "filter". Optional arguments "errorMsgFilter" and "errorMsgProj" limit profiler output
 * if this asserts.
 */
function profilerHasNumMatchingEntriesOrThrow(
    {profileDB, filter, numExpectedMatches, errorMsgFilter, errorMsgProj}) {
    assert(typeof numExpectedMatches === 'number' && numExpectedMatches >= 0,
           "'numExpectedMatches' must be a number >= 0");

    assert.eq(profileDB.system.profile.find(filter).itcount(),
              numExpectedMatches,
              "Expected exactly " + numExpectedMatches + " op(s) matching: " + tojson(filter) +
                  " in profiler " +
                  tojson(profileDB.system.profile.find(errorMsgFilter, errorMsgProj).toArray()));
}

/**
 * Throws an assertion if the profiler does not contain any entries matching "filter". Optional
 * arguments "errorMsgFilter" and "errorMsgProj" limit profiler output if this asserts.
 */
function profilerHasAtLeastOneMatchingEntryOrThrow(
    {profileDB, filter, errorMsgFilter, errorMsgProj}) {
    assert.gte(profileDB.system.profile.find(filter).itcount(),
               1,
               "Expected at least 1 op matching: " + tojson(filter) + " in profiler " +
                   tojson(profileDB.system.profile.find(errorMsgFilter, errorMsgProj).toArray()));
}

/**
 * Throws an assertion if the profiler does not contain exactly one entry matching "filter".
 * Optional arguments "errorMsgFilter" and "errorMsgProj" limit profiler output if this asserts.
 */
function profilerHasSingleMatchingEntryOrThrow({profileDB, filter, errorMsgFilter, errorMsgProj}) {
    profilerHasNumMatchingEntriesOrThrow({
        profileDB: profileDB,
        filter: filter,
        numExpectedMatches: 1,
        errorMsgFilter: errorMsgFilter,
        errorMsgProj: errorMsgProj
    });
}

/**
 * Throws an assertion if the profiler contains any entries matching "filter". Optional arguments
 * "errorMsgFilter" and "errorMsgProj" limit profiler output if this asserts.
 */
function profilerHasZeroMatchingEntriesOrThrow({profileDB, filter, errorMsgFilter, errorMsgProj}) {
    profilerHasNumMatchingEntriesOrThrow({
        profileDB: profileDB,
        filter: filter,
        numExpectedMatches: 0,
        errorMsgFilter: errorMsgFilter,
        errorMsgProj: errorMsgProj
    });
}