mirror of https://github.com/mongodb/mongo
231 lines
8.5 KiB
JavaScript
231 lines
8.5 KiB
JavaScript
// Test that the 'cloneCatalogData' command works correctly.
|
|
// Eventually, _shardsvrMovePrimary will use this command.
|
|
import {FixtureHelpers} from "jstests/libs/fixture_helpers.js";
|
|
import {ShardingTest} from "jstests/libs/shardingtest.js";
|
|
|
|
// Do not check metadata consistency as unsharded collections are cloned to non-primary shards for
|
|
// testing purposes.
|
|
TestData.skipCheckMetadataConsistency = true;
|
|
TestData.skipCheckOrphans = true;
|
|
|
|
(() => {
|
|
function sortByName(a, b) {
|
|
if (a.name < b.name) return -1;
|
|
if (a.name > b.name) return 1;
|
|
return 0;
|
|
}
|
|
|
|
let st = new ShardingTest({shards: 2}),
|
|
testDB = st.s.getDB("test");
|
|
|
|
// Enable sharding on the test DB.
|
|
assert.commandWorked(st.s.adminCommand({enableSharding: "test"}));
|
|
|
|
// Create two collections with non-default options.
|
|
let coll1Options = {capped: true, size: 500},
|
|
coll2Options = {validator: {$jsonSchema: {required: ["a"]}}};
|
|
|
|
assert.commandWorked(testDB.createCollection("coll1", coll1Options));
|
|
assert.commandWorked(testDB.createCollection("coll2", coll2Options));
|
|
|
|
// Create some test documents and put them in each collection.
|
|
[
|
|
{a: 1, b: 2, c: 4},
|
|
{a: 2, b: 4, c: 8},
|
|
{a: 3, b: 6, c: 12},
|
|
].forEach((d) => {
|
|
assert.commandWorked(testDB.coll1.insert(d));
|
|
assert.commandWorked(testDB.coll2.insert(d));
|
|
});
|
|
|
|
// Create indexes on each collection.
|
|
let coll1Indexes = [
|
|
{key: {a: 1}, name: "index1", sparse: true},
|
|
{key: {b: -1}, name: "index2", unique: true},
|
|
],
|
|
coll2Indexes = [
|
|
{key: {a: 1, b: 1}, name: "index3"},
|
|
{key: {c: 1}, name: "index4"},
|
|
];
|
|
|
|
assert.commandWorked(testDB.runCommand({createIndexes: "coll1", indexes: coll1Indexes}));
|
|
assert.commandWorked(testDB.runCommand({createIndexes: "coll2", indexes: coll2Indexes}));
|
|
|
|
// Shard coll2, but leave coll1 unsharded.
|
|
assert.commandWorked(st.s.adminCommand({shardCollection: "test.coll2", key: {_id: 1}}));
|
|
|
|
// Wait for the write to config.collections to be visible in the majority snapshot on all config
|
|
// secondaries, since the test directly talks to shards and so does not gossip the configOpTime
|
|
// to those shards.
|
|
st.configRS.awaitLastOpCommitted();
|
|
|
|
// Get the primary shard, and the non-primary shard.
|
|
let fromShard = st.getPrimaryShard("test");
|
|
let toShard = st.getOther(fromShard);
|
|
|
|
let res = fromShard.getDB("test").runCommand({listCollections: 1});
|
|
assert.commandWorked(res);
|
|
let collections = res.cursor.firstBatch;
|
|
|
|
collections.sort(sortByName);
|
|
let coll2uuid = collections[1].info.uuid;
|
|
|
|
// Have the other shard clone the DB from the primary.
|
|
assert.commandWorked(
|
|
toShard.adminCommand({_shardsvrCloneCatalogData: "test", from: fromShard.host, writeConcern: {w: "majority"}}),
|
|
);
|
|
|
|
// Ask the shard that just called _shardsvrCloneCatalogData for the collections.
|
|
res = toShard.getDB("test").runCommand({listCollections: 1});
|
|
assert.commandWorked(res);
|
|
|
|
collections = res.cursor.firstBatch;
|
|
|
|
// There should be 2 collections: coll1, coll2
|
|
assert.eq(collections.length, 2);
|
|
collections.sort(sortByName);
|
|
|
|
let c1, c2;
|
|
[c1, c2] = collections;
|
|
|
|
function checkName(c, expectedName) {
|
|
assert.eq(c.name, expectedName, "Expected collection to be " + expectedName + ", got " + c.name);
|
|
}
|
|
|
|
function checkOptions(c, expectedOptions) {
|
|
assert.hasFields(c, ["options"], "Missing options field for collection " + c.name);
|
|
assert.hasFields(c.options, expectedOptions, "Missing expected option(s) for collection " + c.name);
|
|
}
|
|
|
|
function checkUUID(c, expectedUUID) {
|
|
assert.hasFields(c, ["info"], "Missing info field for collection " + c.name);
|
|
assert.hasFields(c.info, ["uuid"], "Missing uuid field for collection " + c.name);
|
|
assert.eq(c.info.uuid, expectedUUID, "Incorrect uuid for collection " + c.name);
|
|
}
|
|
|
|
// c1 should be coll1.
|
|
checkName(c1, "coll1");
|
|
checkOptions(c1, Object.keys(coll1Options));
|
|
|
|
// c2 should be coll2.
|
|
checkName(c2, "coll2");
|
|
checkOptions(c2, Object.keys(coll2Options));
|
|
checkUUID(c2, coll2uuid);
|
|
|
|
function checkIndexes(collName, expectedIndexes, trackedColl) {
|
|
let res = toShard.getDB("test").runCommand({listIndexes: collName});
|
|
assert.commandWorked(res, "Failed to get indexes for collection " + collName);
|
|
let indexes = res.cursor.firstBatch;
|
|
indexes.sort(sortByName);
|
|
|
|
// For each unsharded, untracked collection, there should be a total of 3 indexes - one for
|
|
// the _id field and the other two that we have created. However, in the case of tracked
|
|
// collections, only the _id index is present. When cloning tracked collections, indexes are
|
|
// not copied.
|
|
if (trackedColl) assert(indexes.length === 1);
|
|
else assert(indexes.length === 3);
|
|
|
|
indexes.forEach((index, i) => {
|
|
let expected;
|
|
if (i == 0) expected = {name: "_id_", key: {_id: 1}};
|
|
else expected = expectedIndexes[i - 1];
|
|
Object.keys(expected).forEach((k) => {
|
|
assert.eq(index[k], expected[k]);
|
|
});
|
|
});
|
|
}
|
|
|
|
let unshardedColl = st.s.getDB(testDB).getCollection("coll1");
|
|
|
|
checkIndexes("coll1", coll1Indexes, FixtureHelpers.isTracked(unshardedColl) /*trackedColl*/);
|
|
checkIndexes("coll2", coll2Indexes, /*trackedColl*/ true);
|
|
|
|
// Verify that the data from the untracked collections resides on the new primary shard, and was
|
|
// copied as part of the clone.
|
|
function checkCount(shard, collName, count) {
|
|
let res = shard.getDB("test").runCommand({count: collName});
|
|
assert.commandWorked(res);
|
|
assert.eq(res.n, count);
|
|
}
|
|
|
|
checkCount(fromShard, "coll1", 3);
|
|
checkCount(fromShard, "coll2", 3);
|
|
if (FixtureHelpers.isTracked(unshardedColl)) {
|
|
checkCount(toShard, "coll1", 0);
|
|
} else {
|
|
checkCount(toShard, "coll1", 3);
|
|
}
|
|
checkCount(toShard, "coll2", 0);
|
|
|
|
// Check that the command fails without writeConcern majority.
|
|
assert.commandFailedWithCode(
|
|
toShard.adminCommand({
|
|
_shardsvrCloneCatalogData: "test",
|
|
from: fromShard.host,
|
|
}),
|
|
ErrorCodes.InvalidOptions,
|
|
);
|
|
|
|
// Check that the command fails when attempting to clone the admin database.
|
|
assert.commandFailedWithCode(
|
|
toShard.adminCommand({
|
|
_shardsvrCloneCatalogData: "admin",
|
|
from: fromShard.host,
|
|
writeConcern: {w: "majority"},
|
|
}),
|
|
ErrorCodes.InvalidOptions,
|
|
);
|
|
|
|
if (TestData.configShard && !FixtureHelpers.isTracked(unshardedColl)) {
|
|
// The config server is a shard and already has collections for the database. This is only
|
|
// a problem if collections will be cloned.
|
|
assert.commandFailedWithCode(
|
|
st.configRS.getPrimary().adminCommand({
|
|
_shardsvrCloneCatalogData: "test",
|
|
from: fromShard.host,
|
|
writeConcern: {w: "majority"},
|
|
}),
|
|
ErrorCodes.NamespaceExists,
|
|
);
|
|
} else {
|
|
// The config server is dedicated but supports config shard mode, so it can accept sharded
|
|
// commands.
|
|
assert.commandWorked(
|
|
st.configRS.getPrimary().adminCommand({
|
|
_shardsvrCloneCatalogData: "test",
|
|
from: fromShard.host,
|
|
writeConcern: {w: "majority"},
|
|
}),
|
|
);
|
|
}
|
|
|
|
// Check that the command fails when failing to specify a source.
|
|
assert.commandFailedWithCode(
|
|
toShard.adminCommand({_shardsvrCloneCatalogData: "test", from: "", writeConcern: {w: "majority"}}),
|
|
ErrorCodes.InvalidOptions,
|
|
);
|
|
|
|
// Check that clone errors when the collection already exists on the destination. This is only a
|
|
// problem if some collection will be cloned (ie. coll1 is untracked).
|
|
if (FixtureHelpers.isTracked(unshardedColl)) {
|
|
assert.commandWorked(
|
|
toShard.adminCommand({
|
|
_shardsvrCloneCatalogData: "test",
|
|
from: fromShard.host,
|
|
writeConcern: {w: "majority"},
|
|
}),
|
|
);
|
|
} else {
|
|
assert.commandFailedWithCode(
|
|
toShard.adminCommand({
|
|
_shardsvrCloneCatalogData: "test",
|
|
from: fromShard.host,
|
|
writeConcern: {w: "majority"},
|
|
}),
|
|
ErrorCodes.NamespaceExists,
|
|
);
|
|
}
|
|
|
|
st.stop();
|
|
})();
|