summaryrefslogtreecommitdiff
path: root/jstests/replsets/explain_secondaryok.js
blob: f3215af9ab6df39050e853b65b073757835229db (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
// Test the explain command on the primary and on secondaries:
//
// 1) Explain of read operations should work on the secondaries iff secondaryOk is set.
//
// 2) Explain of write operations should
//     --fail on secondaries, even if secondaryOk is set,
//     --succeed on primary without applying any writes.

var name = "explain_secondaryok";

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_secondaryok.insert({a: 1});
replTest.awaitReplication();

// Check that the document is present on the primary.
assert.eq(1, primary.getDB("test").explain_secondaryok.findOne({a: 1})["a"]);

// We shouldn't be able to read from the secondary with secondaryOk off.
var secondary = replTest.getSecondary();
secondary.getDB("test").getMongo().setSecondaryOk(false);
assert.throws(function() {
    secondary.getDB("test").explain_secondaryok.findOne({a: 1});
});

// With secondaryOk on, we should be able to read from the secondary.
secondary.getDB("test").getMongo().setSecondaryOk();
assert.eq(1, secondary.getDB("test").explain_secondaryok.findOne({a: 1})["a"]);

//
// Test explains on primary.
//

// Explain a count on the primary.
var explainOut = primary.getDB("test").runCommand(
    {explain: {count: "explain_secondaryok", 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_secondaryok", 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_secondaryok.findOne({a: 1})["a"]);
secondary.getDB("test").getMongo().setSecondaryOk();
assert.eq(1, secondary.getDB("test").explain_secondaryok.findOne({a: 1})["a"]);

//
// Test explains on secondary.
//

// Explain a count on the secondary with secondaryOk off. Should fail because
// secondaryOk is required for explains on a secondary.
secondary.getDB("test").getMongo().setSecondaryOk(false);
explainOut = secondary.getDB("test").runCommand(
    {explain: {count: "explain_secondaryok", query: {a: 1}}, verbosity: "executionStats"});
assert.commandFailed(explainOut, "explain read op on secondary, secondaryOk false");

// Explain of count should succeed once secondaryOk is true.
secondary.getDB("test").getMongo().setSecondaryOk();
explainOut = secondary.getDB("test").runCommand(
    {explain: {count: "explain_secondaryok", query: {a: 1}}, verbosity: "executionStats"});
assert.commandWorked(explainOut, "explain read op on secondary, secondaryOk true");

// Explain .find() on a secondary, setting secondaryOk directly on the query.
secondary.getDB("test").getMongo().setSecondaryOk(false);
assert.throws(function() {
    secondary.getDB("test").explain_secondaryok.explain("executionStats").find({a: 1}).finish();
});

secondary.getDB("test").getMongo().setSecondaryOk(false);
explainOut = secondary.getDB("test")
                 .explain_secondaryok.explain("executionStats")
                 .find({a: 1})
                 .addOption(DBQuery.Option.slaveOk)
                 .finish();
assert.commandWorked(explainOut, "explain read op on secondary, slaveOk bit set to true on query");

secondary.getDB("test").getMongo().setSecondaryOk();
explainOut =
    secondary.getDB("test").explain_secondaryok.explain("executionStats").find({a: 1}).finish();
assert.commandWorked(explainOut, "explain .find() on secondary, secondaryOk set to true");

// Explain .find() on a secondary, setting secondaryOk to false with various read preferences.
var readPrefModes = ["secondary", "secondaryPreferred", "primaryPreferred", "nearest"];
readPrefModes.forEach(function(prefString) {
    secondary.getDB("test").getMongo().setSecondaryOk(false);
    explainOut = secondary.getDB("test")
                     .explain_secondaryok.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_secondaryok.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 secondaryOk to false with read preference set to
// primary.
var prefStringPrimary = "primary";
secondary.getDB("test").getMongo().setSecondaryOk(false);
explainOut = secondary.getDB("test").runCommand(
    {explain: {find: "explain_secondaryok", query: {a: 1}}, verbosity: "executionStats"});
assert.commandFailed(explainOut, "not primary and secondaryOk=false");

// Similarly should fail if a read preference is set on the connection.
secondary.setReadPref(prefStringPrimary);
explainOut = secondary.getDB("test").runCommand(
    {explain: {find: "explain_secondaryok", query: {a: 1}}, verbosity: "executionStats"});
assert.commandFailed(explainOut, "not primary and secondaryOk=false");
// Unset read pref on the connection.
secondary.setReadPref();

// Explain an update on the secondary with secondaryOk off. Should fail because
// secondaryOk is required for explains on a secondary.
secondary.getDB("test").getMongo().setSecondaryOk(false);
explainOut = secondary.getDB("test").runCommand({
    explain: {update: "explain_secondaryok", updates: [{q: {a: 1}, u: {$set: {a: 5}}}]},
    verbosity: "executionStats"
});
assert.commandFailed(explainOut, "explain write op on secondary, secondaryOk false");

// Explain of the update should also fail with secondaryOk on.
secondary.getDB("test").getMongo().setSecondaryOk();
explainOut = secondary.getDB("test").runCommand({
    explain: {update: "explain_secondaryok", updates: [{q: {a: 1}, u: {$set: {a: 5}}}]},
    verbosity: "executionStats"
});
assert.commandFailed(explainOut, "explain write op on secondary, secondaryOk true");
replTest.stopSet();