Files
mongo/jstests/sharding/read_pref.js
2019-05-17 16:28:27 -04:00

211 lines
7.6 KiB
JavaScript

/**
* Integration test for read preference and tagging. The more comprehensive unit test can be found
* in dbtests/replica_set_monitor_test.cpp.
*/
// Checking UUID consistency involves talking to a shard node, which in this test is shutdown
TestData.skipCheckingUUIDsConsistentAcrossCluster = true;
(function() {
'use strict';
load("jstests/replsets/rslib.js");
var PRI_TAG = {dc: 'ny'};
var SEC_TAGS = [{dc: 'sf', s: "1"}, {dc: 'ma', s: "2"}, {dc: 'eu', s: "3"}, {dc: 'jp', s: "4"}];
var NODES = SEC_TAGS.length + 1;
var doTest = function(useDollarQuerySyntax) {
var st =
new ShardingTest({shards: {rs0: {nodes: NODES, oplogSize: 10, useHostName: true}}});
var replTest = st.rs0;
var primaryNode = replTest.getPrimary();
// The $-prefixed query syntax is only legal for compatibility mode reads, not for the
// find/getMore commands.
if (useDollarQuerySyntax && st.s.getDB("test").getMongo().useReadCommands()) {
st.stop();
return;
}
var setupConf = function() {
var replConf = primaryNode.getDB('local').system.replset.findOne();
replConf.version = (replConf.version || 0) + 1;
var secIdx = 0;
for (var x = 0; x < NODES; x++) {
var node = replConf.members[x];
if (node.host == primaryNode.name) {
node.tags = PRI_TAG;
} else {
node.tags = SEC_TAGS[secIdx++];
node.priority = 0;
}
}
try {
primaryNode.getDB('admin').runCommand({replSetReconfig: replConf});
} catch (x) {
jsTest.log('Exception expected because reconfiguring would close all conn, got ' +
x);
}
return replConf;
};
var checkTag = function(nodeToCheck, tag) {
for (var idx = 0; idx < NODES; idx++) {
var node = replConf.members[idx];
if (node.host.toLowerCase() == nodeToCheck.toLowerCase()) {
jsTest.log('node[' + node.host + '], Tag: ' + tojson(node['tags']));
jsTest.log('tagToCheck: ' + tojson(tag));
var nodeTag = node['tags'];
for (var key in tag) {
assert.eq(tag[key], nodeTag[key]);
}
return;
}
}
assert(false, 'node ' + nodeToCheck + ' not part of config!');
};
var replConf = setupConf();
var conn = st.s;
// Wait until the ReplicaSetMonitor refreshes its view and see the tags
var replConfig = replTest.getReplSetConfigFromNode();
replConfig.members.forEach(function(node) {
var nodeConn = new Mongo(node.host);
awaitRSClientHosts(conn, nodeConn, {ok: true, tags: node.tags}, replTest);
});
replTest.awaitReplication();
jsTest.log('New rs config: ' + tojson(primaryNode.getDB('local').system.replset.findOne()));
jsTest.log('connpool: ' + tojson(conn.getDB('admin').runCommand({connPoolStats: 1})));
var coll = conn.getDB('test').user;
assert.soon(function() {
var res = coll.insert({x: 1}, {writeConcern: {w: NODES}});
if (!res.hasWriteError()) {
return true;
}
var err = res.getWriteError().errmsg;
// Transient transport errors may be expected b/c of the replSetReconfig
if (err.indexOf("transport error") == -1) {
throw err;
}
return false;
});
var getExplain = function(readPrefMode, readPrefTags) {
if (useDollarQuerySyntax) {
var readPrefObj = {mode: readPrefMode};
if (readPrefTags) {
readPrefObj.tags = readPrefTags;
}
return coll.find({$query: {}, $readPreference: readPrefObj, $explain: true})
.limit(-1)
.next();
} else {
return coll.find().readPref(readPrefMode, readPrefTags).explain("executionStats");
}
};
var getExplainServer = function(explain) {
assert.eq("SINGLE_SHARD", explain.queryPlanner.winningPlan.stage);
var serverInfo = explain.queryPlanner.winningPlan.shards[0].serverInfo;
return serverInfo.host.toLowerCase() + ":" + serverInfo.port.toString();
};
// Read pref should work without slaveOk
var explain = getExplain("secondary");
var explainServer = getExplainServer(explain);
assert.neq(primaryNode.name, explainServer);
conn.setSlaveOk();
// It should also work with slaveOk
explain = getExplain("secondary");
explainServer = getExplainServer(explain);
assert.neq(primaryNode.name, explainServer);
// Check that $readPreference does not influence the actual query
assert.eq(1, explain.executionStats.nReturned);
explain = getExplain("secondaryPreferred", [{s: "2"}]);
explainServer = getExplainServer(explain);
checkTag(explainServer, {s: "2"});
assert.eq(1, explain.executionStats.nReturned);
// Cannot use tags with primaryOnly
assert.throws(function() {
getExplain("primary", [{s: "2"}]);
});
// Ok to use empty tags on primaryOnly
explain = getExplain("primary", [{}]);
explainServer = getExplainServer(explain);
assert.eq(primaryNode.name, explainServer);
explain = getExplain("primary", []);
explainServer = getExplainServer(explain);
assert.eq(primaryNode.name, explainServer);
// Check that mongos will try the next tag if nothing matches the first
explain = getExplain("secondary", [{z: "3"}, {dc: "jp"}]);
explainServer = getExplainServer(explain);
checkTag(explainServer, {dc: "jp"});
assert.eq(1, explain.executionStats.nReturned);
// Check that mongos will fallback to primary if none of tags given matches
explain = getExplain("secondaryPreferred", [{z: "3"}, {dc: "ph"}]);
explainServer = getExplainServer(explain);
// Call getPrimary again since the primary could have changed after the restart.
assert.eq(replTest.getPrimary().name, explainServer);
assert.eq(1, explain.executionStats.nReturned);
// Kill all members except one
var stoppedNodes = [];
for (var x = 0; x < NODES - 1; x++) {
replTest.stop(x);
stoppedNodes.push(replTest.nodes[x]);
}
// Wait for ReplicaSetMonitor to realize nodes are down
awaitRSClientHosts(conn, stoppedNodes, {ok: false}, replTest.name);
// Wait for the last node to be in steady state -> secondary (not recovering)
var lastNode = replTest.nodes[NODES - 1];
awaitRSClientHosts(conn, lastNode, {ok: true, secondary: true}, replTest.name);
jsTest.log('connpool: ' + tojson(conn.getDB('admin').runCommand({connPoolStats: 1})));
// Test to make sure that connection is ok, in prep for priOnly test
explain = getExplain("nearest");
explainServer = getExplainServer(explain);
assert.eq(explainServer, replTest.nodes[NODES - 1].name);
assert.eq(1, explain.executionStats.nReturned);
// Should assert if request with priOnly but no primary
assert.throws(function() {
getExplain("primary");
});
st.stop();
};
doTest(false);
doTest(true);
})();