summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/awaitable_ismaster.js
blob: 3899f7a8f4c127d09c04e856e095a8508ea87957 (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
148
149
150
151
152
153
154
155
156
157
158
159
160
/**
 * Tests the maxAwaitTimeMS and topologyVersion parameters of the isMaster command.
 * @tags: [requires_replication]
 */
(function() {
"use strict";
load("jstests/libs/parallel_shell_helpers.js");

function runTest(db) {
    // Check isMaster response contains a topologyVersion even if maxAwaitTimeMS and topologyVersion
    // are not included in the request.
    const res = assert.commandWorked(db.runCommand({isMaster: 1}));
    assert(res.hasOwnProperty("topologyVersion"), tojson(res));

    const topologyVersionField = res.topologyVersion;
    assert(topologyVersionField.hasOwnProperty("processId"), tojson(topologyVersionField));
    assert(topologyVersionField.hasOwnProperty("counter"), tojson(topologyVersionField));

    // Check that isMaster succeeds when passed a valid topologyVersion and maxAwaitTimeMS. In this
    // case, use the topologyVersion from the previous isMaster response. The topologyVersion field
    // is expected to be of the form {processId: <ObjectId>, counter: <long>}.
    assert.commandWorked(
        db.runCommand({isMaster: 1, topologyVersion: topologyVersionField, maxAwaitTimeMS: 0}));

    // Ensure isMaster waits for at least maxAwaitTimeMS before returning.
    let now = new Date();
    assert.commandWorked(
        db.runCommand({isMaster: 1, topologyVersion: topologyVersionField, maxAwaitTimeMS: 2000}));
    let isMasterDuration = new Date() - now;
    // Allow for some clock imprecision between the server and the jstest.
    assert.gte(
        isMasterDuration,
        1000,
        `isMaster should have taken at least 1000ms, but completed in ${isMasterDuration}ms`);

    // Check that when a different processId is given, the server responds immediately.
    now = new Date();
    assert.commandWorked(db.runCommand({
        isMaster: 1,
        topologyVersion: {processId: ObjectId(), counter: topologyVersionField.counter},
        maxAwaitTimeMS: 2000
    }));
    isMasterDuration = new Date() - now;
    assert.lt(isMasterDuration,
              1000,
              `isMaster should have taken at most 1000ms, but completed in ${isMasterDuration}ms`);

    // Check that when a different processId is given, a higher counter is allowed.
    assert.commandWorked(db.runCommand({
        isMaster: 1,
        topologyVersion:
            {processId: ObjectId(), counter: NumberLong(topologyVersionField.counter + 1)},
        maxAwaitTimeMS: 0
    }));

    // Check that when the same processId is given, a higher counter is not allowed.
    assert.commandFailedWithCode(db.runCommand({
        isMaster: 1,
        topologyVersion: {
            processId: topologyVersionField.processId,
            counter: NumberLong(topologyVersionField.counter + 1)
        },
        maxAwaitTimeMS: 0
    }),
                                 [31382, 51761]);

    // Check that passing a topologyVersion not of type object fails.
    assert.commandFailedWithCode(
        db.runCommand({isMaster: 1, topologyVersion: "topology_version_string", maxAwaitTimeMS: 0}),
        10065);

    // Check that a topologyVersion with an invalid processId and valid counter fails.
    assert.commandFailedWithCode(db.runCommand({
        isMaster: 1,
        topologyVersion: {processId: "pid1", counter: topologyVersionField.counter},
        maxAwaitTimeMS: 0
    }),
                                 ErrorCodes.TypeMismatch);

    // Check that a topologyVersion with a valid processId and invalid counter fails.
    assert.commandFailedWithCode(db.runCommand({
        isMaster: 1,
        topologyVersion: {processId: topologyVersionField.processId, counter: 0},
        maxAwaitTimeMS: 0
    }),
                                 ErrorCodes.TypeMismatch);

    // Check that a topologyVersion with a valid processId but missing counter fails.
    assert.commandFailedWithCode(db.runCommand({
        isMaster: 1,
        topologyVersion: {processId: topologyVersionField.processId},
        maxAwaitTimeMS: 0
    }),
                                 40414);

    // Check that a topologyVersion with a missing processId and valid counter fails.
    assert.commandFailedWithCode(db.runCommand({
        isMaster: 1,
        topologyVersion: {counter: topologyVersionField.counter},
        maxAwaitTimeMS: 0
    }),
                                 40414);

    // Check that a topologyVersion with a valid processId and negative counter fails.
    assert.commandFailedWithCode(db.runCommand({
        isMaster: 1,
        topologyVersion: {processId: topologyVersionField.processId, counter: NumberLong("-1")},
        maxAwaitTimeMS: 0
    }),
                                 [31372, 51758]);

    // Check that isMaster fails if there is an extra field in its topologyVersion.
    assert.commandFailedWithCode(db.runCommand({
        isMaster: 1,
        topologyVersion: {
            processId: topologyVersionField.processId,
            counter: topologyVersionField.counter,
            randomField: "I should cause an error"
        },
        maxAwaitTimeMS: 0
    }),
                                 40415);

    // A client following the awaitable isMaster protocol must include topologyVersion in their
    // request if and only if they include maxAwaitTimeMS. Check that isMaster fails if there is a
    // topologyVersion but no maxAwaitTimeMS field.
    assert.commandFailedWithCode(
        db.runCommand({isMaster: 1, topologyVersion: topologyVersionField}), [31368, 51760]);

    // Check that isMaster fails if there is a maxAwaitTimeMS field but no topologyVersion.
    assert.commandFailedWithCode(db.runCommand({isMaster: 1, maxAwaitTimeMS: 0}), [31368, 51760]);

    // Check that isMaster fails if there is a valid topologyVersion but invalid maxAwaitTimeMS
    // type.
    assert.commandFailedWithCode(db.runCommand({
        isMaster: 1,
        topologyVersion: topologyVersionField,
        maxAwaitTimeMS: "stringMaxAwaitTimeMS"
    }),
                                 ErrorCodes.TypeMismatch);

    // Check that isMaster fails if there is a valid topologyVersion but negative maxAwaitTimeMS.
    assert.commandFailedWithCode(db.runCommand({
        isMaster: 1,
        topologyVersion: topologyVersionField,
        maxAwaitTimeMS: -1,
    }),
                                 [31373, 51759]);
}

const replTest = new ReplSetTest({nodes: 1});
replTest.startSet();
replTest.initiate();
runTest(replTest.getPrimary().getDB("admin"));
replTest.stopSet();

const st = new ShardingTest({mongos: 1, shards: [{nodes: 1}], config: 1});
runTest(st.s.getDB("admin"));
st.stop();
})();