diff options
author | Gregory Noma <gregory.noma@gmail.com> | 2022-02-09 15:56:24 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-02-09 17:16:30 +0000 |
commit | a9bdb9af6ee3b7edd6a3c932633035011ddf7b93 (patch) | |
tree | 7479f8301db1e20e3c48401db2b62907926fee8a | |
parent | 116a38b637faf897e1a6014abdaadff137cdb496 (diff) | |
download | mongo-a9bdb9af6ee3b7edd6a3c932633035011ddf7b93.tar.gz |
SERVER-63286 Ensure `CollectionUUIDMismatch` from `createIndexes` and `dropIndexes` commands does not omit the actual collection even if unsharded
-rw-r--r-- | jstests/sharding/collection_uuid_create_indexes.js | 65 | ||||
-rw-r--r-- | jstests/sharding/collection_uuid_drop_indexes.js | 68 | ||||
-rw-r--r-- | src/mongo/s/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/s/cluster_commands_helpers.cpp | 23 |
4 files changed, 155 insertions, 2 deletions
diff --git a/jstests/sharding/collection_uuid_create_indexes.js b/jstests/sharding/collection_uuid_create_indexes.js new file mode 100644 index 00000000000..e95d1644d67 --- /dev/null +++ b/jstests/sharding/collection_uuid_create_indexes.js @@ -0,0 +1,65 @@ +/** + * Tests the collectionUUID parameter of the createIndexes command when one collection is sharded + * and the other collection is unsharded. + * + * @tags: [ + * featureFlagCommandsAcceptCollectionUUID, + * ] + */ +(function() { +'use strict'; + +const st = new ShardingTest({shards: 2}); +const mongos = st.s; + +const db = mongos.getDB(jsTestName()); +assert.commandWorked(mongos.adminCommand({enableSharding: db.getName()})); +assert.commandWorked(mongos.adminCommand({movePrimary: db.getName(), to: st.shard0.name})); + +const shardedColl = db.sharded; +const unshardedColl = db.unsharded; + +assert.commandWorked(shardedColl.insert({_id: 0})); +assert.commandWorked(shardedColl.insert({_id: 1})); +assert.commandWorked(unshardedColl.insert({_id: 2})); + +const uuid = function(coll) { + return assert.commandWorked(db.runCommand({listCollections: 1})) + .cursor.firstBatch.find(c => c.name === coll.getName()) + .info.uuid; +}; + +assert.commandWorked( + mongos.adminCommand({shardCollection: shardedColl.getFullName(), key: {_id: 1}})); + +// Move {_id: 0} to shard0 and {_id: 1} to shard1. +assert.commandWorked(st.splitAt(shardedColl.getFullName(), {_id: 1})); +assert.commandWorked(mongos.adminCommand( + {moveChunk: shardedColl.getFullName(), find: {_id: 0}, to: st.shard0.shardName})); +assert.commandWorked(mongos.adminCommand( + {moveChunk: shardedColl.getFullName(), find: {_id: 1}, to: st.shard1.shardName})); + +// Run the command on the collection which is sharded, while specifying the UUID of the unsharded +// collection that exists only on shard0. +let res = assert.commandFailedWithCode(db.runCommand({ + createIndexes: shardedColl.getName(), + indexes: [{key: {a: 1}, name: 'a_1'}], + collectionUUID: uuid(unshardedColl), +}), + ErrorCodes.CollectionUUIDMismatch); +assert.eq(res.collectionUUID, uuid(unshardedColl)); +assert.eq(res.actualNamespace, unshardedColl.getFullName()); + +// Run the command on the collection which is unsharded and exists only on shard0, while specifying +// the UUID of the collection that is sharded. +res = assert.commandFailedWithCode(db.runCommand({ + createIndexes: unshardedColl.getName(), + indexes: [{key: {a: 1}, name: 'a_1'}], + collectionUUID: uuid(shardedColl), +}), + ErrorCodes.CollectionUUIDMismatch); +assert.eq(res.collectionUUID, uuid(shardedColl)); +assert.eq(res.actualNamespace, shardedColl.getFullName()); + +st.stop(); +})(); diff --git a/jstests/sharding/collection_uuid_drop_indexes.js b/jstests/sharding/collection_uuid_drop_indexes.js new file mode 100644 index 00000000000..d2bb5703353 --- /dev/null +++ b/jstests/sharding/collection_uuid_drop_indexes.js @@ -0,0 +1,68 @@ +/** + * Tests the collectionUUID parameter of the dropIndexes command when one collection is sharded and + * the other collection is unsharded. + * + * @tags: [ + * featureFlagCommandsAcceptCollectionUUID, + * ] + */ +(function() { +'use strict'; + +const st = new ShardingTest({shards: 2}); +const mongos = st.s; + +const db = mongos.getDB(jsTestName()); +assert.commandWorked(mongos.adminCommand({enableSharding: db.getName()})); +assert.commandWorked(mongos.adminCommand({movePrimary: db.getName(), to: st.shard0.name})); + +const shardedColl = db.sharded; +const unshardedColl = db.unsharded; + +assert.commandWorked(shardedColl.insert({_id: 0})); +assert.commandWorked(shardedColl.insert({_id: 1})); +assert.commandWorked(unshardedColl.insert({_id: 2})); + +const uuid = function(coll) { + return assert.commandWorked(db.runCommand({listCollections: 1})) + .cursor.firstBatch.find(c => c.name === coll.getName()) + .info.uuid; +}; + +assert.commandWorked( + mongos.adminCommand({shardCollection: shardedColl.getFullName(), key: {_id: 1}})); + +// Move {_id: 0} to shard0 and {_id: 1} to shard1. +assert.commandWorked(st.splitAt(shardedColl.getFullName(), {_id: 1})); +assert.commandWorked(mongos.adminCommand( + {moveChunk: shardedColl.getFullName(), find: {_id: 0}, to: st.shard0.shardName})); +assert.commandWorked(mongos.adminCommand( + {moveChunk: shardedColl.getFullName(), find: {_id: 1}, to: st.shard1.shardName})); + +assert.commandWorked(shardedColl.createIndex({a: 1})); +assert.commandWorked(unshardedColl.createIndex({a: 1})); + +// Run the command on the collection which is sharded, while specifying the UUID of the unsharded +// collection that exists only on shard0. +let res = assert.commandFailedWithCode(db.runCommand({ + dropIndexes: shardedColl.getName(), + index: {a: 1}, + collectionUUID: uuid(unshardedColl), +}), + ErrorCodes.CollectionUUIDMismatch); +assert.eq(res.collectionUUID, uuid(unshardedColl)); +assert.eq(res.actualNamespace, unshardedColl.getFullName()); + +// Run the command on the collection which is unsharded and exists only on shard0, while specifying +// the UUID of the collection that is sharded. +res = assert.commandFailedWithCode(db.runCommand({ + dropIndexes: unshardedColl.getName(), + index: {a: 1}, + collectionUUID: uuid(shardedColl), +}), + ErrorCodes.CollectionUUIDMismatch); +assert.eq(res.collectionUUID, uuid(shardedColl)); +assert.eq(res.actualNamespace, shardedColl.getFullName()); + +st.stop(); +})(); diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript index 6410d32a0df..df8ccfb00be 100644 --- a/src/mongo/s/SConscript +++ b/src/mongo/s/SConscript @@ -65,6 +65,7 @@ env.Library( 'grid', ], LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/db/catalog/collection_uuid_mismatch_info', '$BUILD_DIR/mongo/db/internal_transactions_feature_flag', '$BUILD_DIR/mongo/db/sessions_collection', ], diff --git a/src/mongo/s/cluster_commands_helpers.cpp b/src/mongo/s/cluster_commands_helpers.cpp index a4d47c09825..32bc6e50137 100644 --- a/src/mongo/s/cluster_commands_helpers.cpp +++ b/src/mongo/s/cluster_commands_helpers.cpp @@ -36,6 +36,7 @@ #include "mongo/s/cluster_commands_helpers.h" #include "mongo/bson/util/bson_extract.h" +#include "mongo/db/catalog/collection_uuid_mismatch_info.h" #include "mongo/db/commands.h" #include "mongo/db/curop.h" #include "mongo/db/error_labels.h" @@ -622,13 +623,31 @@ RawResponsesResult appendRawResponses( } // There was an error. Choose the first error as the top-level error. - const auto& firstError = genericErrorsReceived.front().second; + auto& firstError = genericErrorsReceived.front().second; + + if (firstError.code() == ErrorCodes::CollectionUUIDMismatch && + firstError.extraInfo<CollectionUUIDMismatchInfo>()->actualNamespace().isEmpty()) { + // The first error is a CollectionUUIDMismatchInfo but it doesn't contain an actual + // namespace. It's possible that the acutal namespace is unsharded, in which case only the + // error from the primary shard will contain this information. Iterate through the errors to + // see if this is the case. + for (const auto& error : genericErrorsReceived) { + if (error.second.code() == ErrorCodes::CollectionUUIDMismatch && + !error.second.extraInfo<CollectionUUIDMismatchInfo>() + ->actualNamespace() + .isEmpty()) { + firstError = error.second; + break; + } + } + } + output->append("code", firstError.code()); output->append("codeName", ErrorCodes::errorString(firstError.code())); - *errmsg = firstError.reason(); if (auto extra = firstError.extraInfo()) { extra->serialize(output); } + *errmsg = firstError.reason(); return {false, shardsWithSuccessResponses, successARSResponses, firstStaleConfigErrorReceived}; } |