summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/max_time_ms_does_not_leak_shard_cursor.js
blob: 3c060826aaf0d996cc867df0008a474184f87756 (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
// Tests that if a mongoS cursor exceeds the maxTimeMs timeout, the cursors on the shards will be
// cleaned up. Exercises the fix for the bug described in SERVER-62710.
//
// @tags: []

(function() {
"use strict";

load("jstests/libs/fail_point_util.js");  // for 'configureFailPoint()'

function getIdleCursors(conn, collName) {
    return conn.getDB('admin')
        .aggregate([
            {$currentOp: {idleCursors: true}},
            {$match: {$and: [{type: "idleCursor"}, {"cursor.originatingCommand.find": collName}]}}
        ])
        .toArray();
}

function assertNoIdleCursors(conn, collName) {
    const sleepTimeMS = 10 * 1000;
    const retries = 2;
    assert.soon(() => (getIdleCursors(conn, collName).length === 0),
                () => tojson(getIdleCursors(conn, collName)),
                retries * sleepTimeMS,
                sleepTimeMS,
                {runHangAnalyzer: false});
}

const st = new ShardingTest({shards: 1, mongos: 1, config: 1});

const dbName = "test";
const collName = jsTestName();
const coll = st.s.getCollection(dbName + "." + collName);

// Insert some data (1000 simple documents) into the collection.
assert.commandWorked(coll.insert(Array.from({length: 1000}, _ => ({a: 1}))));

// Ensure there are no idle cursors on shart0 before tests begin.
assertNoIdleCursors(st.shard0, collName);

// Ensure the timeout happens on the shard after on exit from 'getMore' call. The query might
// occasionally return the 'NetworkInterfaceExceededTimeLimit' error.
{
    const curs = coll.find().batchSize(2).maxTimeMS(100);
    const fp = configureFailPoint(st.shard0,
                                  "waitBeforeUnpinningOrDeletingCursorAfterGetMoreBatch",
                                  {shouldCheckForInterrupt: true});
    assert.throwsWithCode(
        () => curs.itcount(),
        [ErrorCodes.MaxTimeMSExpired, ErrorCodes.NetworkInterfaceExceededTimeLimit]);
    fp.off();
    assertNoIdleCursors(st.shard0, collName);
}

// Perform a query that sleeps after retrieving each document. This is guaranteed to exceed the
// specified maxTimeMS limit. The timeout may happen either on mongoS or on shard. The query might
// occasionally return the 'NetworkInterfaceExceededTimeLimit' error.
{
    const curs = coll.find({
                         $where: function() {
                             sleep(1);
                             return true;
                         }
                     })
                     .batchSize(2)
                     .maxTimeMS(100);
    assert.eq(getIdleCursors(st.shard0, collName).length, 0);
    assert.throwsWithCode(
        () => curs.itcount(),
        [ErrorCodes.MaxTimeMSExpired, ErrorCodes.NetworkInterfaceExceededTimeLimit]);
    assertNoIdleCursors(st.shard0, collName);
}

// Ensure the timeout happens on mongoS.
{
    const curs = coll.find({}).batchSize(2).maxTimeMS(100);
    const fp = configureFailPoint(st.s, "maxTimeAlwaysTimeOut", {}, "alwaysOn");
    assert.throwsWithCode(() => curs.itcount(), ErrorCodes.MaxTimeMSExpired);
    fp.off();
    assertNoIdleCursors(st.shard0, collName);
}

// Ensure the timeout happens on the shard.
{
    const curs = coll.find({}).batchSize(2).maxTimeMS(100);
    const fp = configureFailPoint(st.shard0, "maxTimeAlwaysTimeOut", {}, "alwaysOn");
    assert.throwsWithCode(() => curs.itcount(), ErrorCodes.MaxTimeMSExpired);
    fp.off();
    assertNoIdleCursors(st.shard0, collName);
}

st.stop();
})();