summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/ttl_operation_metrics.js
blob: 0d24af7893289506eaf83d18faf6837d139cfe87 (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
/**
 * Tests resource consumption metrics for TTL indexes.
 *
 * @tags: [
 *   requires_replication,
 *   requires_wiredtiger,
 * ]
 */
(function() {
'use strict';

load('jstests/noPassthrough/libs/index_build.js');  // For IndexBuildTest
load("jstests/libs/fail_point_util.js");

var rst = new ReplSetTest({
    nodes: 2,
    nodeOptions: {
        setParameter: {
            "aggregateOperationResourceConsumptionMetrics": true,
            "ttlMonitorSleepSecs": 1,
        }
    }
});
rst.startSet();
rst.initiate();

const dbName = 'test';
const collName = 'test';
const primary = rst.getPrimary();
const secondary = rst.getSecondary();
const primaryDB = primary.getDB(dbName);
const secondaryDB = secondary.getDB(dbName);

const clearMetrics = (conn) => {
    conn.getDB('admin').aggregate([{$operationMetrics: {clearMetrics: true}}]);
};

// Get aggregated metrics keyed by database name.
const getMetrics = (conn) => {
    const cursor = conn.getDB('admin').aggregate([{$operationMetrics: {}}]);

    let allMetrics = {};
    while (cursor.hasNext()) {
        let doc = cursor.next();
        allMetrics[doc.db] = doc;
    }
    return allMetrics;
};

const assertMetrics = (conn, assertFn) => {
    let metrics = getMetrics(conn);
    try {
        assertFn(metrics);
    } catch (e) {
        print("caught exception while checking metrics on " + tojson(conn) +
              ", metrics: " + tojson(metrics));
        throw e;
    }
};

const waitForTtlPass = (db) => {
    // Wait for the TTL monitor to run at least twice (in case we weren't finished setting up our
    // collection when it ran the first time).
    let ttlPass = db.serverStatus().metrics.ttl.passes;
    assert.soon(function() {
        return db.serverStatus().metrics.ttl.passes >= ttlPass + 2;
    }, "TTL monitor didn't run before timing out.");
};

// Create a TTL index and pause the thread.
assert.commandWorked(primaryDB[collName].createIndex({x: 1}, {expireAfterSeconds: 0}));

let pauseTtl = configureFailPoint(primary, 'hangTTLMonitorWithLock');
pauseTtl.wait();

clearMetrics(primary);

let now = new Date();
let later = new Date(now.getTime() + 1000 * 60 * 60);
assert.commandWorked(primaryDB[collName].insert({_id: 0, x: now}));
assert.commandWorked(primaryDB[collName].insert({_id: 1, x: now}));
assert.commandWorked(primaryDB[collName].insert({_id: 2, x: later}));

assertMetrics(primary, (metrics) => {
    // With replication enabled, oplog writes are counted towards bytes written. Only assert that we
    // insert at least as many bytes in the documents.
    // Document size is 29 bytes.
    assert.gte(metrics[dbName].docBytesWritten, 29 * 3);
    assert.gte(metrics[dbName].docUnitsWritten, 3);
    assert.gte(metrics[dbName].totalUnitsWritten, 3);
});

// Clear metrics and wait for a TTL pass to delete the documents.
clearMetrics(primary);
pauseTtl.off();
waitForTtlPass(primaryDB);

// Ensure that the TTL monitor deleted 2 documents on the primary and recorded read and write
// metrics.
assertMetrics(primary, (metrics) => {
    // The TTL monitor generates oplog entries for each deletion on the primary. Assert that we
    // write at least as many bytes in the documents. Document size is 29 bytes.
    assert.gte(metrics[dbName].primaryMetrics.docBytesRead, 29 * 2);
    assert.gte(metrics[dbName].primaryMetrics.docUnitsRead, 2);
    assert.gte(metrics[dbName].docBytesWritten, 29 * 2);
    assert.gte(metrics[dbName].docUnitsWritten, 2);
    assert.gte(metrics[dbName].totalUnitsWritten, 2);
    // Key size is 12 bytes.
    assert.gte(metrics[dbName].primaryMetrics.idxEntryBytesRead, 12 * 2);
    assert.gte(metrics[dbName].primaryMetrics.idxEntryUnitsRead, 2);
    // At least 2 keys (_id and x) should be deleted for each document.
    assert.gte(metrics[dbName].idxEntryUnitsWritten, 2 * 2);
    assert.gte(metrics[dbName].idxEntryBytesWritten, 12 * 2);
});

rst.awaitReplication();

// There should be no activity on the secondary.
assertMetrics(secondary, (metrics) => {
    assert(!metrics.hasOwnProperty(dbName));
});

// Ensure the last document was not deleted.
assert.eq(primaryDB[collName].count({}), 1);
assert.eq(secondaryDB[collName].count({}), 1);

rst.stopSet();
}());