diff options
Diffstat (limited to 'jstests/noPassthrough/awaitable_ismaster.js')
-rw-r--r-- | jstests/noPassthrough/awaitable_ismaster.js | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/jstests/noPassthrough/awaitable_ismaster.js b/jstests/noPassthrough/awaitable_ismaster.js new file mode 100644 index 00000000000..3899f7a8f4c --- /dev/null +++ b/jstests/noPassthrough/awaitable_ismaster.js @@ -0,0 +1,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(); +})(); |