mirror of https://github.com/mongodb/mongo
SERVER-109929 shardCollection/reshardCollection must attempt to create a shardkey when a multikey/wildcard index exists on the shard key field (#42869)
GitOrigin-RevId: 4e10f9972c7443e1d3a3583107da052c451d6c40
This commit is contained in:
parent
d4ce0777a9
commit
c43b6e0ba8
|
|
@ -25,7 +25,17 @@ const incompatibleShardKeyIndexes = [
|
|||
{
|
||||
key: {skey: 1, multiKeyField: 1},
|
||||
options: {},
|
||||
isMultiKey: true,
|
||||
makeItMultiKey: function (coll) {
|
||||
coll.insert({skey: 10, multiKeyField: [1, 2, 3]});
|
||||
},
|
||||
},
|
||||
{
|
||||
key: {skey: 1},
|
||||
options: {},
|
||||
makeItMultiKey: function (coll) {
|
||||
coll.insert({skey: [1, 2, 3]});
|
||||
},
|
||||
skeyFieldIsMultikey: true,
|
||||
},
|
||||
{
|
||||
key: {skey: 1, "a.$**": 1},
|
||||
|
|
@ -87,8 +97,10 @@ describe("testing incompatible shard key indexes", function () {
|
|||
beforeEach(() => {
|
||||
this.coll.drop();
|
||||
|
||||
// Insert a document that will make the index on 'multiKeyField' multikey.
|
||||
this.coll.insert({skey: 22, multiKeyField: [1, 2, 3]});
|
||||
// Make the index multikey if necessary.
|
||||
if (incompatibleShardKeyIndex.makeItMultiKey) {
|
||||
incompatibleShardKeyIndex.makeItMultiKey(this.coll);
|
||||
}
|
||||
|
||||
// Create the incompatible index.
|
||||
assert.commandWorked(
|
||||
|
|
@ -97,51 +109,50 @@ describe("testing incompatible shard key indexes", function () {
|
|||
});
|
||||
|
||||
it("can't shard a non-empty collection with only an incompatible shard key index", () => {
|
||||
const res = this.st.s.adminCommand({shardCollection: this.coll.getFullName(), key: shardKeyPattern});
|
||||
|
||||
this.coll.insert({skey: 33});
|
||||
|
||||
assert.commandFailedWithCode(
|
||||
this.st.s.adminCommand({shardCollection: this.coll.getFullName(), key: shardKeyPattern}),
|
||||
ErrorCodes.InvalidOptions,
|
||||
[ErrorCodes.InvalidOptions],
|
||||
);
|
||||
});
|
||||
|
||||
it("shardColleciont on an empty collection will attempt to create a shard key index", () => {
|
||||
it("shardCollecion on an empty collection will attempt to create a shard key index", () => {
|
||||
this.coll.deleteMany({});
|
||||
|
||||
if (incompatibleShardKeyIndex.isMultiKey) {
|
||||
// We can't test a multiKey index on an empty collection, since it would not
|
||||
// be multiKey.
|
||||
return;
|
||||
if (incompatibleShardKeyIndex.makeItMultiKey) {
|
||||
this.coll.remove({});
|
||||
}
|
||||
|
||||
const res = this.st.s.adminCommand({shardCollection: this.coll.getFullName(), key: shardKeyPattern});
|
||||
|
||||
assert.commandWorkedOrFailedWithCode(res, [
|
||||
ErrorCodes.InvalidOptions,
|
||||
ErrorCodes.IndexKeySpecsConflict,
|
||||
]);
|
||||
|
||||
if (res.ok) {
|
||||
// The shardCollection command succeeded, check that the shard key index was
|
||||
// created and it's not the incompatible index.
|
||||
|
||||
assert(
|
||||
incompatibleShardKeyIndex.canCoexistWithShardKeyIndex,
|
||||
"reshardCollection succeeded when it should have failed",
|
||||
);
|
||||
|
||||
const shardKeyIndex = this.coll.getIndexByKey(shardKeyPattern);
|
||||
const incompatibleIndex = this.coll.getIndexByKey(incompatibleShardKeyIndex.key);
|
||||
|
||||
assert.neq(shardKeyIndex, null, "shard key index was not created");
|
||||
assert.neq(incompatibleIndex, null, "incompatible index is missing");
|
||||
assert.neq(incompatibleIndex, shardKeyIndex, "shard key index mustn't be the incompatible index");
|
||||
if (!incompatibleShardKeyIndex.canCoexistWithShardKeyIndex) {
|
||||
assert.commandFailedWithCode(res, [
|
||||
ErrorCodes.IndexOptionsConflict,
|
||||
ErrorCodes.IndexKeySpecsConflict,
|
||||
ErrorCodes.InvalidOptions,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
assert.commandWorked(res);
|
||||
|
||||
// A valid shard key index should be implicitly created during the reshardCollection
|
||||
// operation.
|
||||
const shardKeyIndex = this.coll.getIndexByKey(shardKeyPattern);
|
||||
const incompatibleIndex = this.coll.getIndexByKey(incompatibleShardKeyIndex.key);
|
||||
|
||||
assert.neq(shardKeyIndex, null, "shard key index was not created");
|
||||
assert.neq(incompatibleIndex, null, "incompatible index is missing");
|
||||
assert.neq(incompatibleIndex, shardKeyIndex, "shard key index mustn't be the incompatible index");
|
||||
});
|
||||
|
||||
it("can't drop or hide last compatible shard key index", () => {
|
||||
if (incompatibleShardKeyIndex.skeyFieldIsMultikey) {
|
||||
// Can't create a compatible shard key index if the shard key field is multikey.
|
||||
return;
|
||||
}
|
||||
|
||||
// Shard the collection with a compatible shard key index.
|
||||
let compatibleShardKeyIndex = shardKeyPattern;
|
||||
if (!incompatibleShardKeyIndex.canCoexistWithShardKeyIndex) {
|
||||
|
|
@ -173,27 +184,30 @@ describe("testing incompatible shard key indexes", function () {
|
|||
numInitialChunks: 1,
|
||||
});
|
||||
|
||||
assert.commandWorkedOrFailedWithCode(res, [
|
||||
ErrorCodes.InvalidOptions,
|
||||
ErrorCodes.IndexKeySpecsConflict,
|
||||
]);
|
||||
|
||||
if (res.ok) {
|
||||
// The reshardCollection command succeeded, check that the shard key index was
|
||||
// created and it's not the incompatible index.
|
||||
|
||||
assert(
|
||||
incompatibleShardKeyIndex.canCoexistWithShardKeyIndex,
|
||||
"reshardCollection succeeded when it should have failed",
|
||||
);
|
||||
|
||||
const shardKeyIndex = this.coll.getIndexByKey(shardKeyPattern);
|
||||
const incompatibleIndex = this.coll.getIndexByKey(incompatibleShardKeyIndex.key);
|
||||
|
||||
assert.neq(shardKeyIndex, null, "shard key index was not created");
|
||||
assert.neq(incompatibleIndex, null, "incompatible index is missing");
|
||||
assert.neq(incompatibleIndex, shardKeyIndex, "shard key index mustn't be the incompatible index");
|
||||
if (!incompatibleShardKeyIndex.canCoexistWithShardKeyIndex) {
|
||||
assert.commandFailedWithCode(res, [
|
||||
ErrorCodes.IndexOptionsConflict,
|
||||
ErrorCodes.IndexKeySpecsConflict,
|
||||
ErrorCodes.InvalidOptions,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
assert.commandWorked(res);
|
||||
|
||||
// A valid shard key index should be implicitly created during the reshardCollection
|
||||
// operation.
|
||||
assert(
|
||||
incompatibleShardKeyIndex.canCoexistWithShardKeyIndex,
|
||||
"reshardCollection succeeded when it should have failed",
|
||||
);
|
||||
|
||||
const shardKeyIndex = this.coll.getIndexByKey(shardKeyPattern);
|
||||
const incompatibleIndex = this.coll.getIndexByKey(incompatibleShardKeyIndex.key);
|
||||
|
||||
assert.neq(shardKeyIndex, null, "shard key index was not created");
|
||||
assert.neq(incompatibleIndex, null, "incompatible index is missing");
|
||||
assert.neq(incompatibleIndex, shardKeyIndex, "shard key index mustn't be the incompatible index");
|
||||
});
|
||||
|
||||
it("can't call refineCollectionShardKey with only an incompatible shard key index", () => {
|
||||
|
|
|
|||
|
|
@ -189,10 +189,16 @@ bool validShardKeyIndexExists(OperationContext* opCtx,
|
|||
|
||||
// 2. Check for a useful index
|
||||
bool hasUsefulIndexForKey = false;
|
||||
bool indexPerfectlyMatchingShardKeyAlreadyExists = false;
|
||||
std::string allReasons;
|
||||
for (const auto& idx : indexes) {
|
||||
std::string reasons;
|
||||
BSONObj currentKey = idx["key"].embeddedObject();
|
||||
|
||||
if (shardKeyPattern.toBSON().woCompare(currentKey) == 0) {
|
||||
indexPerfectlyMatchingShardKeyAlreadyExists = true;
|
||||
}
|
||||
|
||||
// Check 2.i. and 2.ii.
|
||||
if (!idx["sparse"].trueValue() && idx["filter"].eoo() && idx["collation"].eoo() &&
|
||||
shardKeyPattern.toBSON().isPrefixOf(currentKey,
|
||||
|
|
@ -261,7 +267,17 @@ bool validShardKeyIndexExists(OperationContext* opCtx,
|
|||
|
||||
if (hasUsefulIndexForKey) {
|
||||
// Check 2.iii Make sure that there is a useful, non-multikey index available.
|
||||
behaviors.verifyUsefulNonMultiKeyIndex(nss, shardKeyPattern.toBSON());
|
||||
try {
|
||||
behaviors.verifyUsefulNonMultiKeyIndex(nss, shardKeyPattern.toBSON());
|
||||
} catch (ExceptionFor<ErrorCodes::InvalidOptions>& e) {
|
||||
if (indexPerfectlyMatchingShardKeyAlreadyExists) {
|
||||
e.addContext(str::stream()
|
||||
<< "can't shard collection because an index already exists on the "
|
||||
"shard key and it's a non shard key valid index.");
|
||||
throw;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return hasUsefulIndexForKey;
|
||||
|
|
|
|||
|
|
@ -219,6 +219,8 @@ bool validateShardKeyIndexExistsOrCreateIfPossible(OperationContext* opCtx,
|
|||
* Returns true if the shard key is valid and already exists. Steps 1, 2 and 3 of the previous
|
||||
* function.
|
||||
*
|
||||
* It throws an exception if a valid shard key index doesn't exist and it's not possible to create
|
||||
* one.
|
||||
*/
|
||||
bool validShardKeyIndexExists(OperationContext* opCtx,
|
||||
const NamespaceString& nss,
|
||||
|
|
|
|||
Loading…
Reference in New Issue