Files
mongo/jstests/sharding/csrs_upgrade.js
Andy Schwerin 4727903fd6 SERVER-20694 Wait for nodes to catch up in csrs_upgrade.js by monitoring replSetGetStatus.
The original trick of doing a write with write concern { w:csrs.length } is not legal
after SERVER-20891.
2015-10-28 17:27:28 -04:00

250 lines
9.3 KiB
JavaScript

/**
* This test performs an upgrade from SCCC config servers to CSRS config servers.
*
* Along the way, it confirms that the config servers always offer the
* ability to read metadata, and checks that metadata is writable or
* unwritable as appropriate at certain steps in the process.
*
* During the setup phase, a new sharded cluster is created with SCCC
* config servers and a single sharded collection with documents on
* each of two shards.
*
* During the upgrade phase, chunks are split to confirm the
* availability or unavailability of metadata writes, and
* config.version is read to confirm the availability of metadata
* reads.
*/
var st;
(function() {
"use strict";
var testDBName = jsTestName();
var dataCollectionName = testDBName + ".data";
var csrsName = jsTestName() + "-csrs";
var numCsrsMembers;
if (TestData.storageEngine == "wiredTiger" || TestData.storageEngine == "") {
// TODO(schwerin): SERVER-19739 Support testing CSRS with storage engines other than wired
// tiger, when such other storage engines support majority read concern.
numCsrsMembers = 3;
} else {
numCsrsMembers = 4;
}
var nextSplit = 0;
/**
* Runs a split command with a never-before used middle split point. Returns the command result.
*/
var runNextSplit = function (snode) {
var splitPoint = nextSplit;
nextSplit += 10;
return snode.adminCommand({split: dataCollectionName, middle: { _id: splitPoint }});
};
/**
* Runs a config.version read, then splits the data collection and expects both operations to
* succeed.
*/
var assertCanSplit = function (snode, msg) {
if (msg) {
jsTest.log("Confirming that " + snode.name + " CAN run a split " + msg);
}
assert(snode.getCollection("config.version").findOne());
assert.commandWorked(runNextSplit(snode));
};
var waitUntilMaster = function (dnode) {
var isMasterReply;
assert.soon(function () {
isMasterReply = dnode.adminCommand({ismaster: 1});
return isMasterReply.ismaster;
}, function () {
return "Expected " + dnode.name + " to respond ismaster:true, but got " +
tojson(isMasterReply);
});
};
/**
* Runs a config.version read, then splits the data collection and expects the read to succed
* and the split to fail.
*/
var assertCannotSplit = function (snode, msg) {
jsTest.log("Confirming that " + snode.name + " CANNOT run a split " + msg);
assert(snode.getCollection("config.version").findOne());
assert.commandFailed(runNextSplit(snode));
};
jsTest.log("Setting up SCCC sharded cluster")
st = new ShardingTest({
name: "csrsUpgrade",
mongos: 2,
rs: { nodes: 3 },
shards: 2,
nopreallocj: true,
other: {
sync: true,
enableBalancer: false,
useHostname: true
}
});
var waitUntilAllCaughtUp = function(csrs) {
var rsStatus;
var firstConflictingIndex;
var ot;
var otherOt;
assert.soon(function () {
rsStatus = csrs[0].adminCommand('replSetGetStatus');
if (rsStatus.ok != 1) {
return false;
}
assert.eq(csrs.length, rsStatus.members.length, tojson(rsStatus));
ot = rsStatus.members[0].optime;
for (var i = 1; i < rsStatus.members.length; ++i) {
otherOt = rsStatus.members[i].optime;
if (bsonWoCompare({ts: otherOt.ts}, {ts: ot.ts}) ||
bsonWoCompare({t: otherOt.t}, {t: ot.t})) {
firstConflictingIndex = i;
return false;
}
}
return true;
}, function () {
return "Optimes of members 0 (" + tojson(ot) + ") and " + firstConflictingIndex + " (" +
tojson(otherOt) + ") are different in " + tojson(rsStatus);
});
};
var shardConfigs = st.s0.getCollection("config.shards").find().toArray();
assert.eq(2, shardConfigs.length);
var shard0Name = shardConfigs[0]._id;
jsTest.log("Enabling sharding on " + testDBName + " and making " + shard0Name +
" the primary shard");
assert.commandWorked(st.s0.adminCommand({enablesharding: testDBName}));
st.ensurePrimaryShard(testDBName, shard0Name);
jsTest.log("Creating a sharded collection " + dataCollectionName);
assert.commandWorked(st.s0.adminCommand({
shardcollection: dataCollectionName,
key: { _id: 1 }
}));
assertCanSplit(st.s0);
assert.commandWorked(st.s0.adminCommand({
moveChunk: dataCollectionName,
find: { _id: 0 },
to: shardConfigs[1]._id
}));
jsTest.log("Inserting data into " + dataCollectionName);
st.s1.getCollection(dataCollectionName).insert(
(function () {
var result = [];
var i;
for (i = -20; i < 20; ++i) {
result.push({ _id: i });
}
return result;
}()));
jsTest.log("Restarting " + st.c0.name + " as a standalone replica set");
var csrsConfig = {
_id: csrsName,
version: 1,
configsvr: true,
members: [ { _id: 0, host: st.c0.name }]
};
assert.commandWorked(st.c0.adminCommand({replSetInitiate: csrsConfig}));
var csrs = [];
var csrs0Opts = Object.extend({}, st.c0.fullOptions, /* deep */ true);
csrs0Opts.restart = true; // Don't clean the data files from the old c0.
csrs0Opts.replSet = csrsName;
csrs0Opts.configsvrMode = "sccc";
MongoRunner.stopMongod(st.c0);
csrs.push(MongoRunner.runMongod(csrs0Opts));
waitUntilMaster(csrs[0]);
assertCanSplit(st.s0, "using SCCC protocol when first config server is a 1-node replica set");
jsTest.log("Starting new CSRS nodes");
for (var i = 1; i < numCsrsMembers; ++i) {
csrs.push(MongoRunner.runMongod({
replSet: csrsName,
configsvr: "",
storageEngine: "wiredTiger"
}));
csrsConfig.members.push({ _id: i, host: csrs[i].name, votes: 0, priority: 0 });
}
csrsConfig.version = 2;
jsTest.log("Adding non-voting members to csrs set: " + tojson(csrsConfig));
assert.commandWorked(csrs[0].adminCommand({replSetReconfig: csrsConfig}));
jsTest.log("Splitting a chunk to confirm that the SCCC protocol works w/ 1 rs " +
"node with secondaries");
assertCanSplit(st.s0, "using SCCC protocol when first config server is primary of " +
csrs.length + "-node replica set");
waitUntilAllCaughtUp(csrs);
jsTest.log("Shutting down second and third SCCC config server nodes");
MongoRunner.stopMongod(st.c1);
MongoRunner.stopMongod(st.c2);
assertCannotSplit(st.s0, "with two SCCC nodes down");
csrsConfig.members.forEach(function (member) { member.votes = 1; member.priority = 1});
csrsConfig.version = 3;
jsTest.log("Allowing all csrs members to vote: " + tojson(csrsConfig));
assert.commandWorked(csrs[0].adminCommand({replSetReconfig: csrsConfig}));
assertCannotSplit(st.s0, "with two SCCC nodes down, even though CSRS is almost ready");
jsTest.log("Restarting " + csrs[0].name + " in csrs mode");
delete csrs0Opts.configsvrMode;
try {
csrs[0].adminCommand({replSetStepDown: 60});
} catch (e) {} // Expected
MongoRunner.stopMongod(csrs[0]);
csrs[0] = MongoRunner.runMongod(csrs0Opts);
var csrsStatus;
assert.soon(function () {
csrsStatus = csrs[0].adminCommand({replSetGetStatus: 1});
if (csrsStatus.members[0].stateStr == "STARTUP" ||
csrsStatus.members[0].stateStr == "STARTUP2" ||
csrsStatus.members[0].stateStr == "RECOVERING") {
// Make sure first node is fully online or else mongoses still in SCCC mode might not
// find any node online to talk to.
return false;
}
var i;
for (i = 0; i < csrsStatus.members.length; ++i) {
if (TestData.storageEngine != "wiredTiger" && TestData.storageEngine != "") {
// NOTE: "" means default storage engine, which is WiredTiger.
if (csrsStatus.members[i].name == csrs[0].name &&
csrsStatus.members[i].stateStr != "REMOVED") {
return false;
}
}
if (csrsStatus.members[i].stateStr == "PRIMARY") {
return csrs[i].adminCommand({ismaster: 1}).ismaster;
}
}
return false;
}, function() {
return "No primary or non-WT engine not removed in " + tojson(csrsStatus);
});
var sconfig = Object.extend({}, st.s0.fullOptions, /* deep */ true);
delete sconfig.port;
sconfig.configdb = csrsName + "/" + csrs[0].name;
assertCanSplit(MongoRunner.runMongos(sconfig),
"when mongos started with --configdb=" + sconfig.configdb);
sconfig.configdb = st.s0.fullOptions.configdb;
assertCanSplit(MongoRunner.runMongos(sconfig),
"when mongos started with --configdb=" + sconfig.configdb);
assertCanSplit(st.s0, "on mongos that drove the upgrade");
assertCanSplit(st.s1, "on mongos that was previously unaware of the upgrade");
}());