Skip to content

Instantly share code, notes, and snippets.

@cheahuychou
Last active April 24, 2020 16:47
Show Gist options
  • Save cheahuychou/f511a55957c62958a4044fb46b86c873 to your computer and use it in GitHub Desktop.
Save cheahuychou/f511a55957c62958a4044fb46b86c873 to your computer and use it in GitHub Desktop.
/*
* The _id of a chunk is the concatenation of its namespace and minKey. The string
* representation of the UUID type was changed from "BinData(...)" in 3.4 to "UUID(...)"
* in 3.6. This test is to verify this chunks for a sharded collection with a UUID shard key
* created in 3.4 cannot be moved, split and merged after the cluster is upgraded
* to "4.0", but one can circumvent this by manually modifying the _id of the chunk docs.
*/
(function() {
"use strict";
load('jstests/multiVersion/libs/multi_cluster.js');
TestData.skipCheckingUUIDsConsistentAcrossCluster = true;
/*
* Creates a sharded collection with collection name 'collName', database name 'dbName', and
* shard key 'shardKey'. Inserts 'numDocs' 1 KB docs with a UUID field into the collection, and
* waits for the balancer to split the initial chunk.
*/
function setUpChunks(dbName, collName, shardKey, numDocs) {
const ns = dbName + "." + collName;
jsTest.log(`Set up sharded collection ${ns} with UUID shard key`);
assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: shardKey}));
jsTest.log(`Insert docs for ${ns}`);
const doc1k = (new Array(1024)).join('x');
for (let x = 0; x < numDocs; x++) {
assert.commandWorked(st.s.getDB(dbName).runCommand({
insert: collName,
documents: [{x: UUID(), v: doc1k}],
ordered: false,
writeConcern: {w: 1}
}));
}
jsTest.log(`Wait for the autosplitter to split the chunk for ${ns}`);
assert.soon(() => st.s.getDB("config").chunks.count({ns: ns}) > 1);
return ns;
}
/*
* Upgrades the entire cluster to the given binVersion and waits for config server and shards
* to become available and for the replica set monitors on the mongos and each shard to reflect
* the state of all shards. Then, runs setFCV to the given FCV version.
*/
function upgradeCluster(binVersion, featureCompatibilityVersion) {
st.stopBalancer();
st.upgradeCluster(binVersion, {
upgradeMongos: true,
upgradeConfigs: true,
upgradeShards: true,
waitUntilStable: true
});
st.s.adminCommand({setFeatureCompatibilityVersion: featureCompatibilityVersion});
st.restartMongoses();
}
/*
* Converts the _id of the form 3.4 format to 3.6+ format. For example, from
* foo.bar-34-x_BinData(4, FA431A0570EC41F384F7B82FC61544D5) to
* foo.bar-34-x_UUID("fa431a05-70ec-41f3-84f7-b82fc61544d5").
*/
function toUUIDChunkId(_id) {
const matches = _id.match(/(.*)_BinData\(4, (\w*)\)/);
const prefix = matches[1];
const bindata = matches[2].toLowerCase();
return prefix + '_UUID("' + bindata.substring(0, 8) + "-" + bindata.substring(8, 12) + "-" +
bindata.substring(12, 16) + "-" + bindata.substring(16, 20) + "-" +
bindata.substring(20, 32) + '\")';
}
/*
* If the "min" of the given chunkDoc is not MinKey, reformats the "_id" of the given
* chunkDoc to match the query used in 4.0 and 4.2.
*/
function reformatChunkDoc(chunkDoc) {
if (chunkDoc._id.includes("MinKey")) {
return;
}
const shardKeyFields = Object.keys(chunkDoc.min);
assert.eq(1, shardKeyFields.length);
chunkDoc._id =
chunkDoc.ns + "-" + shardKeyFields[0] + "_" + chunkDoc.min[shardKeyFields[0]];
return chunkDoc;
}
jsTest.log("Start a \"3.4\" sharded cluster");
const st = new ShardingTest({
shards: 2,
mongos: 1,
config: 1,
other: {
chunkSize: 1,
enableAutoSplit: true,
mongosOptions: {binVersion: "3.4"},
configOptions: {binVersion: "3.4", verbose: 2},
shardOptions: {binVersion: "3.4"},
rsOptions: {binVersion: "3.4"},
rs: true,
}
});
const kDbName = "foo";
const kCollName = "bar";
const kShardKey = {x: 1};
const kNumDocs = 1500;
assert.commandWorked(st.s.adminCommand({enableSharding: kDbName}));
st.ensurePrimaryShard(kDbName, st.shard0.shardName);
const ns = setUpChunks(kDbName, kCollName + "-34", kShardKey, kNumDocs);
jsTest.log("Upgrade the cluster to \"3.6\"");
upgradeCluster("3.6", "3.6");
jsTest.log("Upgrade the cluster to \"4.0\" and create a sharded collection");
upgradeCluster("4.0", "4.0");
jsTest.log("Perform operations on chunks created when the cluster was in \"3.4\"");
// Make sure there is at least two chunks whose min is not MinKey.
const numOriginalChunks = st.s.getDB("config").chunks.count({ns: ns});
assert.gt(numOriginalChunks, 2);
const chunkA = st.s.getDB("config").chunks.findOne(
{ns: ns, shard: st.shard0.shardName, min: {$ne: {x: MinKey}}, max: {x: MaxKey}});
const chunkB =
st.s.getDB("config").chunks.findOne({ns: ns, shard: st.shard0.shardName, max: chunkA.min});
jsTest.log("chunkA " + tojson(chunkA));
jsTest.log("chunkB " + tojson(chunkB));
jsTest.log("moveChunk should fail due to id mismatch");
assert.commandFailedWithCode(
st.s.adminCommand({moveChunk: ns, find: chunkA.min, to: st.shard1.shardName}),
ErrorCodes.IncompatibleShardingMetadata);
jsTest.log("Split the chunk");
assert.commandFailedWithCode(
st.s.adminCommand({split: ns, middle: {x: UUID("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFE")}}),
ErrorCodes.IncompatibleShardingMetadata);
jsTest.log("Merge the resulting chunks");
assert.commandFailedWithCode(
st.s.adminCommand({mergeChunks: ns, bounds: [chunkB.min, chunkA.max]}),
ErrorCodes.DuplicateKey);
jsTest.log("Manually change the _id of the chunks");
for (let chunk of[chunkA, chunkB]) {
const binDataChunkId = chunk._id;
reformatChunkDoc(chunk);
assert.eq(chunk._id, toUUIDChunkId(binDataChunkId));
jsTest.log(
`Remove chunk with _id ${binDataChunkId} and insert the updated chunk ${tojson(chunk)}`);
assert.commandWorked(st.s.getDB("config").chunks.remove({_id: binDataChunkId}));
assert.commandWorked(st.s.getDB("config").chunks.insert(chunk));
}
jsTest.log("moveChunk should work");
assert.commandWorked(
st.s.adminCommand({moveChunk: ns, find: chunkA.min, to: st.shard1.shardName}));
jsTest.log("split should work");
assert.commandWorked(
st.s.adminCommand({split: ns, middle: {x: UUID("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFE")}}));
assert.eq(numOriginalChunks + 1, st.s.getDB("config").chunks.count({ns: ns}));
jsTest.log("mergeChunk should work");
// Move chunkB to shard1 so it can be merged with chunkA.
assert.commandWorked(
st.s.adminCommand({moveChunk: ns, find: chunkB.min, to: st.shard1.shardName}));
assert.commandWorked(st.s.adminCommand({mergeChunks: ns, bounds: [chunkB.min, chunkA.max]}));
assert.eq(numOriginalChunks - 1, st.s.getDB("config").chunks.count({ns: ns}));
st.stop();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment