mirror of https://github.com/mongodb/mongo
246 lines
9.8 KiB
JavaScript
246 lines
9.8 KiB
JavaScript
import {FixtureHelpers} from "jstests/libs/fixture_helpers.js";
|
|
import {ShardingTest} from "jstests/libs/shardingtest.js";
|
|
|
|
function sortByName(a, b) {
|
|
if (a.name < b.name) return -1;
|
|
if (a.name > b.name) return 1;
|
|
return 0;
|
|
}
|
|
|
|
function getCollections(shard) {
|
|
let res = shard.getDB("test1").runCommand({listCollections: 1});
|
|
assert.commandWorked(res);
|
|
|
|
let collections = res.cursor.firstBatch;
|
|
|
|
// Sort collections by name.
|
|
collections.sort(sortByName);
|
|
assert.eq(collections.length, 2);
|
|
|
|
return collections;
|
|
}
|
|
|
|
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 checkCollectionsCopiedCorrectly(fromShard, toShard, tracked, barUUID, fooUUID) {
|
|
let c1, c2;
|
|
[c1, c2] = getCollections(toShard);
|
|
|
|
function checkName(c, expectedName) {
|
|
assert.eq(c.name, expectedName, "Expected collection to be " + expectedName + ", got " + c.name);
|
|
}
|
|
|
|
function checkUUIDsEqual(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);
|
|
}
|
|
|
|
function checkUUIDsNotEqual(c, originalUUID) {
|
|
assert.hasFields(c, ["info"], "Missing info field for collection " + c.name);
|
|
assert.hasFields(c.info, ["uuid"], "Missing uuid field for collection " + c.name);
|
|
assert.neq(
|
|
c.info.uuid,
|
|
originalUUID,
|
|
"UUID for " + c.name + " should be different than the original collection but is the same",
|
|
);
|
|
}
|
|
|
|
function checkIndexes(collName, collTracked, expectedIndexes) {
|
|
let res = toShard.getDB("test1").runCommand({listIndexes: collName});
|
|
assert.commandWorked(res, "Failed to get indexes for collection " + collName);
|
|
let indexes = res.cursor.firstBatch;
|
|
indexes.sort(sortByName);
|
|
|
|
// For each unsharded collection, there should be a total of 2 indexes - one for the _id
|
|
// field and the other we have created. However, in the case of sharded collections, only
|
|
// the _id index is present. When running movePrimary, indexes of sharded collections are
|
|
// not copied.
|
|
if (collTracked) assert(indexes.length == 1);
|
|
else assert(indexes.length == 2);
|
|
|
|
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]);
|
|
});
|
|
});
|
|
}
|
|
|
|
function checkCount(shard, collName, count) {
|
|
let res = shard.getDB("test1").runCommand({count: collName});
|
|
assert.commandWorked(res);
|
|
assert.eq(res.n, count);
|
|
}
|
|
|
|
checkName(c1, "bar");
|
|
checkName(c2, "foo");
|
|
checkOptions(c1, Object.keys(barOptions));
|
|
checkIndexes("bar", tracked[1], barIndexes);
|
|
checkOptions(c2, Object.keys(fooOptions));
|
|
checkIndexes("foo", tracked[0], fooIndexes);
|
|
|
|
if (tracked[0]) {
|
|
checkCount(fromShard, "foo", 3);
|
|
checkCount(toShard, "foo", 0);
|
|
checkUUIDsEqual(c2, fooUUID);
|
|
} else {
|
|
checkCount(toShard, "foo", 3);
|
|
checkCount(fromShard, "foo", 0);
|
|
checkUUIDsNotEqual(c2, fooUUID);
|
|
}
|
|
if (tracked[1]) {
|
|
checkCount(fromShard, "bar", 3);
|
|
checkCount(toShard, "bar", 0);
|
|
checkUUIDsEqual(c1, barUUID);
|
|
} else {
|
|
checkCount(toShard, "bar", 3);
|
|
checkCount(fromShard, "bar", 0);
|
|
checkUUIDsNotEqual(c1, barUUID);
|
|
}
|
|
}
|
|
|
|
function createCollections(sharded) {
|
|
assert.commandWorked(st.getDB("test1").runCommand({dropDatabase: 1}));
|
|
let db = st.getDB("test1");
|
|
|
|
assert.commandWorked(db.createCollection("foo", fooOptions));
|
|
assert.commandWorked(db.createCollection("bar", barOptions));
|
|
|
|
for (let i = 0; i < 3; i++) {
|
|
assert.commandWorked(db.foo.insert({a: i}));
|
|
assert.commandWorked(db.bar.insert({a: i}));
|
|
}
|
|
assert.eq(3, db.foo.count());
|
|
assert.eq(3, db.bar.count());
|
|
|
|
assert.commandWorked(db.runCommand({createIndexes: "foo", indexes: fooIndexes}));
|
|
assert.commandWorked(db.runCommand({createIndexes: "bar", indexes: barIndexes}));
|
|
|
|
if (sharded) {
|
|
assert.commandWorked(db.adminCommand({enableSharding: "test1"}));
|
|
assert.commandWorked(db.adminCommand({shardCollection: "test1.foo", key: {_id: 1}}));
|
|
assert.commandWorked(db.adminCommand({shardCollection: "test1.bar", key: {_id: 1}}));
|
|
}
|
|
}
|
|
|
|
function movePrimaryWithFailpoint(sharded) {
|
|
let db = st.getDB("test1");
|
|
createCollections(sharded);
|
|
let tracked = [
|
|
FixtureHelpers.isTracked(st.s.getCollection("test1.foo")),
|
|
FixtureHelpers.isTracked(st.s.getCollection("test1.bar")),
|
|
];
|
|
|
|
let fromShard = st.getPrimaryShard("test1");
|
|
let toShard = st.getOther(fromShard);
|
|
|
|
assert.eq(3, fromShard.getDB("test1").foo.count(), "from shard doesn't have data before move");
|
|
assert.eq(0, toShard.getDB("test1").foo.count(), "to shard has data before move");
|
|
assert.eq(3, fromShard.getDB("test1").bar.count(), "from shard doesn't have data before move");
|
|
assert.eq(0, toShard.getDB("test1").bar.count(), "to shard has data before move");
|
|
|
|
let listCollsFrom = fromShard.getDB("test1").runCommand({listCollections: 1});
|
|
let fromColls = listCollsFrom.cursor.firstBatch;
|
|
fromColls.sort(sortByName);
|
|
let baruuid = fromColls[0].info.uuid;
|
|
let foouuid = fromColls[1].info.uuid;
|
|
|
|
assert.commandWorked(
|
|
toShard.getDB("admin").runCommand({configureFailPoint: "movePrimaryFailPoint", mode: "alwaysOn"}),
|
|
);
|
|
|
|
// Failpoint will cause movePrimary to fail after the first collection has been copied over
|
|
assert.commandFailed(st.s0.adminCommand({movePrimary: "test1", to: toShard.name}));
|
|
|
|
assert.commandWorked(toShard.getDB("admin").runCommand({configureFailPoint: "movePrimaryFailPoint", mode: "off"}));
|
|
|
|
if (sharded || tracked.includes(true)) {
|
|
// If the collections are sharded or tracked, the UUID of the collection on the donor should
|
|
// be copied over and the options should be the same so retrying the move should succeed.
|
|
assert.commandWorked(st.s0.adminCommand({movePrimary: "test1", to: toShard.name}));
|
|
checkCollectionsCopiedCorrectly(fromShard, toShard, tracked, baruuid, foouuid);
|
|
|
|
// Now change an option on the toShard, and verify that calling clone again succeeds
|
|
// when the options don't match.
|
|
assert.commandWorked(toShard.getDB("test1").runCommand({collMod: "bar", validationLevel: "moderate"}));
|
|
assert.commandWorked(st.s0.adminCommand({movePrimary: "test1", to: fromShard.name}));
|
|
|
|
// Assert that the fromShard does not have the new options, but the toShard does
|
|
let barOnToShard = getCollections(toShard)[0];
|
|
checkOptions(
|
|
barOnToShard,
|
|
Object.keys({validator: {$jsonSchema: {required: ["a"]}}, validationLevel: "moderate"}),
|
|
);
|
|
let barOnFromShard = getCollections(fromShard)[0];
|
|
checkOptions(barOnFromShard, Object.keys(barOptions));
|
|
|
|
// The docs should still be on the original primary shard (fromShard).
|
|
checkCollectionsCopiedCorrectly(fromShard, toShard, tracked, baruuid, foouuid);
|
|
|
|
// Now drop and recreate the collection on the toShard. The collection should now have
|
|
// a different UUID, so we should fail on movePrimary.
|
|
assert.commandWorked(toShard.getDB("test1").runCommand({drop: "bar"}));
|
|
assert.commandWorked(toShard.getDB("test1").bar.insert({"x": 1}));
|
|
|
|
assert.commandFailedWithCode(
|
|
st.s0.adminCommand({movePrimary: "test1", to: toShard.name}),
|
|
ErrorCodes.InvalidOptions,
|
|
);
|
|
} else {
|
|
// The failure of the previous attempt caused the dirty data on the recipient to be dropped,
|
|
// so the data cloning shouldn't find any impediments.
|
|
assert.commandWorked(st.s0.adminCommand({movePrimary: "test1", to: toShard.name}));
|
|
}
|
|
}
|
|
|
|
function movePrimaryNoFailpoint(sharded) {
|
|
let db = st.getDB("test1");
|
|
createCollections(sharded);
|
|
let tracked = [
|
|
FixtureHelpers.isTracked(st.s.getCollection("test1.foo")),
|
|
FixtureHelpers.isTracked(st.s.getCollection("test1.bar")),
|
|
];
|
|
|
|
let fromShard = st.getPrimaryShard("test1");
|
|
let toShard = st.getOther(fromShard);
|
|
|
|
assert.eq(3, fromShard.getDB("test1").foo.count(), "from shard doesn't have data before move");
|
|
assert.eq(0, toShard.getDB("test1").foo.count(), "to shard has data before move");
|
|
assert.eq(3, fromShard.getDB("test1").bar.count(), "from shard doesn't have data before move");
|
|
assert.eq(0, toShard.getDB("test1").bar.count(), "to shard has data before move");
|
|
|
|
let listCollsFrom = fromShard.getDB("test1").runCommand({listCollections: 1});
|
|
let fromColls = listCollsFrom.cursor.firstBatch;
|
|
fromColls.sort(sortByName);
|
|
let baruuid = fromColls[0].info.uuid;
|
|
let foouuid = fromColls[1].info.uuid;
|
|
|
|
assert.commandWorked(st.s0.adminCommand({movePrimary: "test1", to: toShard.name}));
|
|
|
|
checkCollectionsCopiedCorrectly(fromShard, toShard, tracked, baruuid, foouuid);
|
|
}
|
|
|
|
var st = new ShardingTest({shards: 2});
|
|
|
|
var fooOptions = {validationLevel: "off"};
|
|
var barOptions = {validator: {$jsonSchema: {required: ["a"]}}};
|
|
|
|
var fooIndexes = [{key: {a: 1}, name: "index1", expireAfterSeconds: 5000}];
|
|
var barIndexes = [{key: {a: -1}, name: "index2"}];
|
|
|
|
movePrimaryWithFailpoint(true);
|
|
movePrimaryWithFailpoint(false);
|
|
movePrimaryNoFailpoint(true);
|
|
movePrimaryNoFailpoint(false);
|
|
|
|
st.stop();
|