This is just a cleanup work to hide some of the private state of ReplSetTest so it is easier to encapsulate and add new logic. Also enables strict mode.
197 lines
7.3 KiB
JavaScript
197 lines
7.3 KiB
JavaScript
// Test the explain command on the primary and on secondaries:
|
|
//
|
|
// 1) Explain of read operations should work on the secondaries iff slaveOk is set.
|
|
//
|
|
// 2) Explain of write operations should
|
|
// --fail on secondaries, even if slaveOk is set,
|
|
// --succeed on primary without applying any writes.
|
|
|
|
var name = "explain_slaveok";
|
|
|
|
print("Start replica set with two nodes");
|
|
var replTest = new ReplSetTest({name: name, nodes: 2});
|
|
var nodes = replTest.startSet();
|
|
replTest.initiate();
|
|
var primary = replTest.getPrimary();
|
|
|
|
// Insert a document and let it sync to the secondary.
|
|
print("Initial sync");
|
|
primary.getDB("test").explain_slaveok.insert({a: 1});
|
|
replTest.awaitReplication();
|
|
|
|
// Check that the document is present on the primary.
|
|
assert.eq(1, primary.getDB("test").explain_slaveok.findOne({a: 1})["a"]);
|
|
|
|
// We shouldn't be able to read from the secondary with slaveOk off.
|
|
var secondary = replTest.getSecondary();
|
|
secondary.getDB("test").getMongo().setSlaveOk(false);
|
|
assert.throws(function() {
|
|
secondary.getDB("test").explain_slaveok.findOne({a: 1});
|
|
});
|
|
|
|
// With slaveOk on, we should be able to read from the secondary.
|
|
secondary.getDB("test").getMongo().setSlaveOk(true);
|
|
assert.eq(1, secondary.getDB("test").explain_slaveok.findOne({a: 1})["a"]);
|
|
|
|
//
|
|
// Test explains on primary.
|
|
//
|
|
|
|
// Explain a count on the primary.
|
|
var explainOut = primary.getDB("test").runCommand({
|
|
explain: {
|
|
count: "explain_slaveok",
|
|
query: {a: 1}
|
|
},
|
|
verbosity: "executionStats"
|
|
});
|
|
assert.commandWorked(explainOut, "explain read op on primary");
|
|
|
|
// Explain an update on the primary.
|
|
explainOut = primary.getDB("test").runCommand({
|
|
explain: {
|
|
update: "explain_slaveok",
|
|
updates: [
|
|
{q: {a: 1}, u: {$set: {a: 5}}}
|
|
]
|
|
},
|
|
verbosity: "executionStats"
|
|
});
|
|
assert.commandWorked(explainOut, "explain write op on primary");
|
|
|
|
// Plan should have an update stage at its root, reporting that it would
|
|
// modify a single document.
|
|
var stages = explainOut.executionStats.executionStages;
|
|
assert.eq("UPDATE", stages.stage);
|
|
assert.eq(1, stages.nWouldModify);
|
|
|
|
// Confirm that the document did not actually get modified on the primary
|
|
// or on the secondary.
|
|
assert.eq(1, primary.getDB("test").explain_slaveok.findOne({a: 1})["a"]);
|
|
secondary.getDB("test").getMongo().setSlaveOk(true);
|
|
assert.eq(1, secondary.getDB("test").explain_slaveok.findOne({a: 1})["a"]);
|
|
|
|
//
|
|
// Test explains on secondary.
|
|
//
|
|
|
|
// Explain a count on the secondary with slaveOk off. Should fail because
|
|
// slaveOk is required for explains on a secondary.
|
|
secondary.getDB("test").getMongo().setSlaveOk(false);
|
|
explainOut = secondary.getDB("test").runCommand({
|
|
explain: {
|
|
count: "explain_slaveok",
|
|
query: {a: 1}
|
|
},
|
|
verbosity: "executionStats"
|
|
});
|
|
assert.commandFailed(explainOut, "explain read op on secondary, slaveOk false");
|
|
|
|
// Explain of count should succeed once slaveOk is true.
|
|
secondary.getDB("test").getMongo().setSlaveOk(true);
|
|
explainOut = secondary.getDB("test").runCommand({
|
|
explain: {
|
|
count: "explain_slaveok",
|
|
query: {a: 1}
|
|
},
|
|
verbosity: "executionStats"
|
|
});
|
|
assert.commandWorked(explainOut, "explain read op on secondary, slaveOk true");
|
|
|
|
// Explain .find() on a secondary, setting slaveOk directly on the query.
|
|
secondary.getDB("test").getMongo().setSlaveOk(false);
|
|
assert.throws(function() {
|
|
secondary.getDB("test").explain_slaveok.explain("executionStats")
|
|
.find({a: 1})
|
|
.finish();
|
|
});
|
|
|
|
secondary.getDB("test").getMongo().setSlaveOk(false);
|
|
explainOut = secondary.getDB("test").explain_slaveok.explain("executionStats")
|
|
.find({a: 1})
|
|
.addOption(DBQuery.Option.slaveOk)
|
|
.finish();
|
|
assert.commandWorked(explainOut, "explain read op on secondary, slaveOk set to true on query");
|
|
|
|
secondary.getDB("test").getMongo().setSlaveOk(true);
|
|
explainOut = secondary.getDB("test").explain_slaveok.explain("executionStats")
|
|
.find({a: 1})
|
|
.finish();
|
|
assert.commandWorked(explainOut, "explain .find() on secondary, slaveOk set to true");
|
|
|
|
// Explain .find() on a secondary, setting slaveOk to false with various read preferences.
|
|
var readPrefModes = ["secondary", "secondaryPreferred", "primaryPreferred", "nearest"];
|
|
readPrefModes.forEach(function(prefString) {
|
|
secondary.getDB("test").getMongo().setSlaveOk(false);
|
|
explainOut = secondary.getDB("test").explain_slaveok.explain("executionStats")
|
|
.find({a: 1})
|
|
.readPref(prefString)
|
|
.finish();
|
|
assert.commandWorked(explainOut, "explain .find() on secondary, '"
|
|
+ prefString
|
|
+ "' read preference on query");
|
|
|
|
// Similarly should succeed if a read preference is set on the connection.
|
|
secondary.setReadPref(prefString);
|
|
explainOut = secondary.getDB("test").explain_slaveok.explain("executionStats")
|
|
.find({a: 1})
|
|
.finish();
|
|
assert.commandWorked(explainOut, "explain .find() on secondary, '"
|
|
+ prefString
|
|
+ "' read preference on connection");
|
|
// Unset read pref on the connection.
|
|
secondary.setReadPref();
|
|
});
|
|
|
|
// Fail explain find() on a secondary, setting slaveOk to false with read preference set to primary.
|
|
var prefStringPrimary = "primary";
|
|
secondary.getDB("test").getMongo().setSlaveOk(false);
|
|
explainOut = secondary.getDB("test").runCommand({
|
|
explain: {
|
|
find: "explain_slaveok",
|
|
query: {a: 1}
|
|
},
|
|
verbosity: "executionStats"
|
|
});
|
|
assert.commandFailed(explainOut, "not master and slaveOk=false");
|
|
|
|
// Similarly should fail if a read preference is set on the connection.
|
|
secondary.setReadPref(prefStringPrimary);
|
|
explainOut = secondary.getDB("test").runCommand({
|
|
explain: {
|
|
find: "explain_slaveok",
|
|
query: {a: 1}
|
|
},
|
|
verbosity: "executionStats"
|
|
});
|
|
assert.commandFailed(explainOut, "not master and slaveOk=false");
|
|
// Unset read pref on the connection.
|
|
secondary.setReadPref();
|
|
|
|
// Explain an update on the secondary with slaveOk off. Should fail because
|
|
// slaveOk is required for explains on a secondary.
|
|
secondary.getDB("test").getMongo().setSlaveOk(false);
|
|
explainOut = secondary.getDB("test").runCommand({
|
|
explain: {
|
|
update: "explain_slaveok",
|
|
updates: [
|
|
{q: {a: 1}, u: {$set: {a: 5}}}
|
|
]
|
|
},
|
|
verbosity: "executionStats"
|
|
});
|
|
assert.commandFailed(explainOut, "explain write op on secondary, slaveOk false");
|
|
|
|
// Explain of the update should also fail with slaveOk on.
|
|
secondary.getDB("test").getMongo().setSlaveOk(true);
|
|
explainOut = secondary.getDB("test").runCommand({
|
|
explain: {
|
|
update: "explain_slaveok",
|
|
updates: [
|
|
{q: {a: 1}, u: {$set: {a: 5}}}
|
|
]
|
|
},
|
|
verbosity: "executionStats"
|
|
});
|
|
assert.commandFailed(explainOut, "explain write op on secondary, slaveOk true");
|