mirror of https://github.com/mongodb/mongo
180 lines
8.0 KiB
JavaScript
180 lines
8.0 KiB
JavaScript
/**
|
|
* Perform tests for moveChunk and splitChunk commands when the shard key is compound hashed.
|
|
*
|
|
* @tags: [
|
|
* multiversion_incompatible,
|
|
* ]
|
|
*/
|
|
import {ShardingTest} from "jstests/libs/shardingtest.js";
|
|
import {findChunksUtil} from "jstests/sharding/libs/find_chunks_util.js";
|
|
|
|
const st = new ShardingTest({shards: 2, other: {chunkSize: 1}});
|
|
const configDB = st.s0.getDB("config");
|
|
const shard0 = st.shard0.shardName;
|
|
const shard1 = st.shard1.shardName;
|
|
assert.commandWorked(configDB.adminCommand({enableSharding: "test", primaryShard: shard0}));
|
|
|
|
const testDBOnPrimary = st.rs0.getPrimary().getDB("test");
|
|
|
|
function verifyChunkSplitIntoTwo(namespace, chunk) {
|
|
assert.eq(0, findChunksUtil.countChunksForNs(configDB, namespace, {min: chunk.min, max: chunk.max}));
|
|
assert.eq(1, findChunksUtil.countChunksForNs(configDB, namespace, {min: chunk.min}));
|
|
assert.eq(1, findChunksUtil.countChunksForNs(configDB, namespace, {max: chunk.max}));
|
|
}
|
|
|
|
const nonHashedFieldValue = 111;
|
|
const hashedFieldValue = convertShardKeyToHashed(nonHashedFieldValue);
|
|
|
|
/**
|
|
* Returns an object which has all the shard key fields. The hashed field will have the value
|
|
* provided for 'valueForHashedField'. We are doing this because, the 'bounds' and 'middle'
|
|
* parameters of splitChunk/moveChunk commands expect a hashed value for the hashed field, where as
|
|
* 'find' expect a non-hashed value.
|
|
*/
|
|
function buildObjWithAllShardKeyFields(shardKey, valueForHashedField) {
|
|
let splitObj = {};
|
|
for (let key in shardKey) {
|
|
if (shardKey[key] === "hashed") {
|
|
splitObj[key] = valueForHashedField;
|
|
} else {
|
|
splitObj[key] = 1;
|
|
}
|
|
}
|
|
return splitObj;
|
|
}
|
|
|
|
function testSplit(shardKey, collName) {
|
|
const namespace = testDBOnPrimary[collName].getFullName();
|
|
assert.commandWorked(configDB.adminCommand({shardCollection: namespace, key: shardKey}));
|
|
|
|
// Insert data since both 'find' and 'bounds' based split requires the chunk to contain some
|
|
// documents.
|
|
const bulk = st.s.getDB("test")[collName].initializeUnorderedBulkOp();
|
|
for (let x = -1200; x < 1200; x++) {
|
|
bulk.insert({x: x, y: x, z: x});
|
|
}
|
|
assert.commandWorked(bulk.execute());
|
|
|
|
// Attempt to split on a value that is not the shard key.
|
|
assert.commandFailed(configDB.adminCommand({split: namespace, middle: {someField: 100}}));
|
|
assert.commandFailed(configDB.adminCommand({split: namespace, find: {someField: 100}}));
|
|
assert.commandFailed(configDB.adminCommand({split: namespace, bounds: [{someField: MinKey}, {someField: MaxKey}]}));
|
|
|
|
let totalChunksBefore = findChunksUtil.countChunksForNs(configDB, namespace);
|
|
const lowestChunk = findChunksUtil.findChunksByNs(configDB, namespace).sort({min: 1}).limit(1).next();
|
|
assert(lowestChunk);
|
|
// Split the chunk based on 'bounds' and verify total chunks increased by one.
|
|
assert.commandWorked(configDB.adminCommand({split: namespace, bounds: [lowestChunk.min, lowestChunk.max]}));
|
|
assert.eq(++totalChunksBefore, findChunksUtil.countChunksForNs(configDB, namespace));
|
|
|
|
// Verify that a single chunk with the previous bounds no longer exists but split into two.
|
|
verifyChunkSplitIntoTwo(namespace, lowestChunk);
|
|
|
|
// Cannot split if 'min' and 'max' doesn't correspond to the same chunk.
|
|
assert.commandFailed(configDB.adminCommand({split: namespace, bounds: [lowestChunk.min, lowestChunk.max]}));
|
|
|
|
const splitObjWithHashedValue = buildObjWithAllShardKeyFields(shardKey, hashedFieldValue);
|
|
|
|
// Find the chunk to which 'splitObjWithHashedValue' belongs to.
|
|
let chunkToBeSplit = findChunksUtil.findChunksByNs(configDB, namespace, {
|
|
min: {$lte: splitObjWithHashedValue},
|
|
max: {$gt: splitObjWithHashedValue},
|
|
})[0];
|
|
assert(chunkToBeSplit);
|
|
|
|
// Split the 'chunkToBeSplit' using 'find'. Note that the object specified for 'find' is not a
|
|
// split point.
|
|
const splitObj = buildObjWithAllShardKeyFields(shardKey, nonHashedFieldValue);
|
|
assert.commandWorked(configDB.adminCommand({split: namespace, find: splitObj}));
|
|
assert.eq(++totalChunksBefore, findChunksUtil.countChunksForNs(configDB, namespace));
|
|
|
|
// Verify that a single chunk with the previous bounds no longer exists but split into two.
|
|
verifyChunkSplitIntoTwo(namespace, chunkToBeSplit);
|
|
assert.eq(0, findChunksUtil.countChunksForNs(configDB, namespace, {min: splitObjWithHashedValue}));
|
|
|
|
// Get the new chunk in which 'splitObj' belongs.
|
|
chunkToBeSplit = findChunksUtil.findChunksByNs(configDB, namespace, {
|
|
min: {$lte: splitObjWithHashedValue},
|
|
max: {$gt: splitObjWithHashedValue},
|
|
})[0];
|
|
|
|
// Use 'splitObj' as the middle point.
|
|
assert.commandWorked(configDB.adminCommand({split: namespace, middle: splitObjWithHashedValue}));
|
|
assert.eq(++totalChunksBefore, findChunksUtil.countChunksForNs(configDB, namespace));
|
|
verifyChunkSplitIntoTwo(namespace, chunkToBeSplit);
|
|
|
|
// TODO(SERVER-97588): Remove version check from tests when 8.1 becomes last LTS.
|
|
const fcvDoc = configDB.adminCommand({getParameter: 1, featureCompatibilityVersion: 1});
|
|
if (MongoRunner.compareBinVersions(fcvDoc.featureCompatibilityVersion.version, "8.1") >= 0) {
|
|
// It should not fail on boundaries that have already been split.
|
|
assert.commandWorked(configDB.adminCommand({split: namespace, middle: chunkToBeSplit.min}));
|
|
} else {
|
|
// Cannot split on existing chunk boundary with 'middle'.
|
|
assert.commandFailed(configDB.adminCommand({split: namespace, middle: chunkToBeSplit.min}));
|
|
}
|
|
st.s.getDB("test")[collName].drop();
|
|
}
|
|
|
|
testSplit({x: "hashed", y: 1, z: 1}, "compound_hashed_prefix");
|
|
testSplit({_id: "hashed", y: 1, z: 1}, "compound_hashed_prefix_id");
|
|
testSplit({x: 1, y: "hashed", z: 1}, "compound_nonhashed_prefix");
|
|
testSplit({x: 1, _id: "hashed", z: 1}, "compound_nonhashed_prefix_id");
|
|
|
|
function testMoveChunk(shardKey) {
|
|
const ns = "test.fooHashed";
|
|
assert.commandWorked(st.s0.adminCommand({shardCollection: ns, key: shardKey}));
|
|
|
|
// Fetch a chunk from 'shard0'.
|
|
const aChunk = findChunksUtil.findOneChunkByNs(configDB, ns, {shard: shard0});
|
|
assert(aChunk);
|
|
|
|
// Error if either of the bounds is not a valid shard key.
|
|
assert.commandFailedWithCode(
|
|
st.s0.adminCommand({moveChunk: ns, bounds: [NaN, aChunk.max], to: shard1}),
|
|
ErrorCodes.TypeMismatch,
|
|
);
|
|
assert.commandFailedWithCode(
|
|
st.s0.adminCommand({moveChunk: ns, bounds: [aChunk.min, NaN], to: shard1}),
|
|
ErrorCodes.TypeMismatch,
|
|
);
|
|
|
|
assert.commandWorked(st.s0.adminCommand({moveChunk: ns, bounds: [aChunk.min, aChunk.max], to: shard1}));
|
|
|
|
assert.eq(0, configDB.chunks.count({_id: aChunk._id, shard: shard0}));
|
|
assert.eq(1, configDB.chunks.count({_id: aChunk._id, shard: shard1}));
|
|
|
|
// Fail if 'find' doesn't have full shard key.
|
|
assert.commandFailed(st.s0.adminCommand({moveChunk: ns, find: {someField: 0}, to: shard1}));
|
|
|
|
// Find the chunk to which 'moveObjWithHashedValue' belongs to.
|
|
const moveObjWithHashedValue = buildObjWithAllShardKeyFields(shardKey, hashedFieldValue);
|
|
const chunk = findChunksUtil.findChunksByNs(st.config, ns, {
|
|
min: {$lte: moveObjWithHashedValue},
|
|
max: {$gt: moveObjWithHashedValue},
|
|
})[0];
|
|
assert(chunk);
|
|
|
|
// Verify that 'moveChunk' with 'find' works with pre-hashed value.
|
|
const otherShard = chunk.shard === shard1 ? shard0 : shard1;
|
|
const moveObj = buildObjWithAllShardKeyFields(shardKey, nonHashedFieldValue);
|
|
assert.commandWorked(st.s.adminCommand({moveChunk: ns, find: moveObj, to: otherShard}));
|
|
assert.eq(findChunksUtil.countChunksForNs(st.config, ns, {min: chunk.min, shard: otherShard}), 1);
|
|
|
|
// Fail if 'find' and 'bounds' are both set.
|
|
assert.commandFailed(
|
|
st.s0.adminCommand({
|
|
moveChunk: ns,
|
|
find: moveObjWithHashedValue,
|
|
bounds: [aChunk.min, aChunk.max],
|
|
to: shard1,
|
|
}),
|
|
);
|
|
|
|
st.s.getDB("test").fooHashed.drop();
|
|
}
|
|
|
|
testMoveChunk({_id: "hashed", b: 1, c: 1});
|
|
testMoveChunk({_id: 1, "b.c.d": "hashed", c: 1});
|
|
|
|
st.stop();
|