Last active
April 24, 2020 16:47
-
-
Save cheahuychou/f511a55957c62958a4044fb46b86c873 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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