diff options
5 files changed, 179 insertions, 107 deletions
diff --git a/jstests/sharding/reshard_collection_basic.js b/jstests/sharding/reshard_collection_basic.js index c536503c82a..734fb20fb4b 100644 --- a/jstests/sharding/reshard_collection_basic.js +++ b/jstests/sharding/reshard_collection_basic.js @@ -3,25 +3,128 @@ // @tags: [requires_fcv_47] // +load("jstests/libs/uuid_util.js"); + (function() { 'use strict'; const st = new ShardingTest({mongos: 1, shards: 2}); const kDbName = 'db'; -const collName = '.foo'; -const ns = kDbName + collName; +const collName = 'foo'; +const ns = kDbName + '.' + collName; const mongos = st.s0; +const mongosConfig = mongos.getDB('config'); + +let shardToRSMap = {}; +shardToRSMap[st.shard0.shardName] = st.rs0; +shardToRSMap[st.shard1.shardName] = st.rs1; + +let getUUIDFromCollectionInfo = (dbName, collName, collInfo) => { + if (collInfo) { + return extractUUIDFromObject(collInfo.info.uuid); + } + + const uuidObject = getUUIDFromListCollections(mongos.getDB(dbName), collName); + return extractUUIDFromObject(uuidObject); +}; + +let constructTemporaryReshardingCollName = (dbName, collName, collInfo) => { + const existingUUID = getUUIDFromCollectionInfo(dbName, collName, collInfo); + return 'system.resharding.' + existingUUID; +}; + +let getAllShardIdsFromExpectedChunks = (expectedChunks) => { + let shardIds = new Set(); + expectedChunks.forEach(chunk => { + shardIds.add(chunk.recipientShardId); + }); + return shardIds; +}; + +let verifyTemporaryReshardingChunksMatchExpected = (expectedChunks) => { + const tempReshardingCollNs = + kDbName + '.' + constructTemporaryReshardingCollName(kDbName, collName); + const tempReshardingChunks = mongosConfig.chunks.find({ns: tempReshardingCollNs}).toArray(); + + expectedChunks.sort(); + tempReshardingChunks.sort(); + + assert.eq(expectedChunks.size, tempReshardingChunks.size); + for (let i = 0; i < expectedChunks.size; i++) { + assert.eq(expectedChunks[i].recipientShardId, tempReshardingChunks[i].shard); + assert.eq(expectedChunks[i].min, tempReshardingChunks[i].min); + assert.eq(expectedChunks[i].max, tempReshardingChunks[i].max); + } +}; + +let verifyTemporaryReshardingCollectionExistsWithCorrectOptionsForConn = + (expectedCollInfo, tempCollName, conn) => { + const tempReshardingCollInfo = + conn.getDB(kDbName).getCollectionInfos({name: tempCollName})[0]; + assert.neq(tempReshardingCollInfo, null); + assert.eq(expectedCollInfo.options, tempReshardingCollInfo.options); + }; + +let verifyTemporaryReshardingCollectionExistsWithCorrectOptions = (expectedRecipientShards) => { + const originalCollInfo = mongos.getDB(kDbName).getCollectionInfos({name: collName})[0]; + assert.neq(originalCollInfo, null); + + const tempReshardingCollName = + constructTemporaryReshardingCollName(kDbName, collName, originalCollInfo); + + verifyTemporaryReshardingCollectionExistsWithCorrectOptionsForConn( + originalCollInfo, tempReshardingCollName, mongos); + + expectedRecipientShards.forEach(shardId => { + verifyTemporaryReshardingCollectionExistsWithCorrectOptionsForConn( + originalCollInfo, tempReshardingCollName, shardToRSMap[shardId].getPrimary()); + }); +}; let removeAllReshardingCollections = () => { + const tempReshardingCollName = constructTemporaryReshardingCollName(kDbName, collName); mongos.getDB(kDbName).foo.drop(); - mongos.getDB('config').reshardingOperations.remove({nss: ns}); - mongos.getDB('config').collections.remove({reshardingFields: {$exists: true}}); + mongos.getDB(kDbName)[tempReshardingCollName].drop(); + mongosConfig.reshardingOperations.remove({nss: ns}); + mongosConfig.collections.remove({reshardingFields: {$exists: true}}); st.rs0.getPrimary().getDB('config').localReshardingOperations.donor.remove({nss: ns}); st.rs0.getPrimary().getDB('config').localReshardingOperations.recipient.remove({nss: ns}); st.rs1.getPrimary().getDB('config').localReshardingOperations.donor.remove({nss: ns}); st.rs1.getPrimary().getDB('config').localReshardingOperations.recipient.remove({nss: ns}); }; +let assertSuccessfulReshardCollection = (commandObj, presetReshardedChunks) => { + assert.commandWorked(mongos.adminCommand({shardCollection: ns, key: {_id: 1}})); + + if (presetReshardedChunks) { + commandObj._presetReshardedChunks = presetReshardedChunks; + } else { + assert.eq(commandObj._presetReshardedChunks, null); + const configChunksArray = mongosConfig.chunks.find({'ns': ns}); + presetReshardedChunks = []; + configChunksArray.forEach(chunk => { + presetReshardedChunks.push( + {recipientShardId: chunk.shard, min: chunk.min, max: chunk.max}); + }); + } + + assert.commandWorked(mongos.adminCommand(commandObj)); + + verifyTemporaryReshardingCollectionExistsWithCorrectOptions( + getAllShardIdsFromExpectedChunks(presetReshardedChunks)); + verifyTemporaryReshardingChunksMatchExpected(presetReshardedChunks); + + removeAllReshardingCollections(); +}; + +let presetReshardedChunks = + [{recipientShardId: st.shard1.shardName, min: {_id: MinKey}, max: {_id: MaxKey}}]; +const existingZoneName = 'x1'; + +/** + * Fail cases + */ + // Fail if sharding is disabled. assert.commandFailedWithCode(mongos.adminCommand({reshardCollection: ns, key: {_id: 1}}), ErrorCodes.NamespaceNotFound); @@ -37,39 +140,14 @@ assert.commandWorked(mongos.adminCommand({shardCollection: ns, key: {_id: 1}})); // Fail if missing required key. assert.commandFailedWithCode(mongos.adminCommand({reshardCollection: ns}), 40414); -// Fail if collation is specified and is not {locale: 'simple'}. -assert.commandFailedWithCode( - mongos.adminCommand({reshardCollection: ns, key: {_id: 1}, collation: {locale: 'en_US'}}), - ErrorCodes.BadValue); - -// Succeed when correct locale is provided. -assert.commandWorked( - mongos.adminCommand({reshardCollection: ns, key: {_id: 1}, collation: {locale: 'simple'}})); - -removeAllReshardingCollections(); - -assert.commandWorked(mongos.adminCommand({shardCollection: ns, key: {_id: 1}})); - // Fail if unique is specified and is true. assert.commandFailedWithCode( mongos.adminCommand({reshardCollection: ns, key: {_id: 1}, unique: true}), ErrorCodes.BadValue); -// Succeed if unique is specified and is false. -assert.commandWorked(mongos.adminCommand({reshardCollection: ns, key: {_id: 1}, unique: false})); - -removeAllReshardingCollections(); - -// Succeed if _presetReshardedChunks is provided and test commands are enabled (default). -assert.commandWorked(mongos.adminCommand({shardCollection: ns, key: {_id: 1}})); -assert.commandWorked(mongos.adminCommand({ - reshardCollection: ns, - key: {_id: 1}, - _presetReshardedChunks: - [{recipientShardId: st.shard1.shardName, min: {_id: MinKey}, max: {_id: MaxKey}}] -})); - -removeAllReshardingCollections(); -assert.commandWorked(mongos.adminCommand({shardCollection: ns, key: {_id: 1}})); +// Fail if collation is specified and is not {locale: 'simple'}. +assert.commandFailedWithCode( + mongos.adminCommand({reshardCollection: ns, key: {_id: 1}, collation: {locale: 'en_US'}}), + ErrorCodes.BadValue); // Fail if both numInitialChunks and _presetReshardedChunks are provided. assert.commandFailedWithCode(mongos.adminCommand({ @@ -78,46 +156,13 @@ assert.commandFailedWithCode(mongos.adminCommand({ unique: false, collation: {locale: 'simple'}, numInitialChunks: 2, - _presetReshardedChunks: [ - {recipientShardId: st.shard0.shardName, min: {_id: MinKey}, max: {_id: 0}}, - {recipientShardId: st.shard1.shardName, min: {_id: 0}, max: {_id: MaxKey}} - ] + _presetReshardedChunks: presetReshardedChunks }), ErrorCodes.BadValue); -// Succeed if all optional fields and numInitialChunks are provided with correct values. -assert.commandWorked(mongos.adminCommand({ - reshardCollection: ns, - key: {_id: 1}, - unique: false, - collation: {locale: 'simple'}, - numInitialChunks: 2, -})); - -removeAllReshardingCollections(); - -// Succeed if all optional fields and _presetReshardedChunks are provided with correct values and -// test commands are enabled (default). -assert.commandWorked(mongos.adminCommand({shardCollection: ns, key: {_id: 1}})); -assert.commandWorked(mongos.adminCommand({ - reshardCollection: ns, - key: {_id: 1}, - unique: false, - collation: {locale: 'simple'}, - _presetReshardedChunks: [ - {recipientShardId: st.shard1.shardName, min: {_id: 0}, max: {_id: MaxKey}}, - {recipientShardId: st.shard0.shardName, min: {_id: MinKey}, max: {_id: 0}} - ] -})); - -removeAllReshardingCollections(); - -const existingZoneName = 'x1'; - // Fail if authoritative tags exist in config.tags collection and zones are not provided. assert.commandWorked( st.s.adminCommand({addShardToZone: st.shard1.shardName, zone: existingZoneName})); -assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: {_id: 1}})); assert.commandWorked(st.s.adminCommand( {updateZoneKeyRange: ns, min: {_id: 0}, max: {_id: 5}, zone: existingZoneName})); @@ -142,21 +187,55 @@ assert.commandFailedWithCode(mongos.adminCommand({ }), ErrorCodes.BadValue); +/** + * Success cases + */ + +removeAllReshardingCollections(); + +// Succeed when correct locale is provided. +assertSuccessfulReshardCollection( + {reshardCollection: ns, key: {_id: 1}, collation: {locale: 'simple'}}); + +// Succeed base case. +assertSuccessfulReshardCollection({reshardCollection: ns, key: {_id: 1}}); + +// Succeed if unique is specified and is false. +assertSuccessfulReshardCollection({reshardCollection: ns, key: {_id: 1}, unique: false}); + +// Succeed if _presetReshardedChunks is provided and test commands are enabled (default). +assertSuccessfulReshardCollection({reshardCollection: ns, key: {_id: 1}}, presetReshardedChunks); + +presetReshardedChunks = [ + {recipientShardId: st.shard0.shardName, min: {_id: MinKey}, max: {_id: 0}}, + {recipientShardId: st.shard1.shardName, min: {_id: 0}, max: {_id: MaxKey}} +]; + +// Succeed if all optional fields and numInitialChunks are provided with correct values. +assertSuccessfulReshardCollection({ + reshardCollection: ns, + key: {_id: 1}, + unique: false, + collation: {locale: 'simple'}, + numInitialChunks: 2, +}); + +// Succeed if all optional fields and _presetReshardedChunks are provided with correct values and +// test commands are enabled (default). +assertSuccessfulReshardCollection( + {reshardCollection: ns, key: {_id: 1}, unique: false, collation: {locale: 'simple'}}, + presetReshardedChunks); + // Succeed if authoritative tags exist in config.tags collection and zones are provided and use an // existing zone's name. -assert.commandWorked(mongos.adminCommand({ +assertSuccessfulReshardCollection({ reshardCollection: ns, key: {_id: 1}, unique: false, collation: {locale: 'simple'}, - zones: [{tag: existingZoneName, min: {_id: 5}, max: {_id: 10}, ns: ns}], - _presetReshardedChunks: [ - {recipientShardId: st.shard1.shardName, min: {_id: 0}, max: {_id: MaxKey}}, - {recipientShardId: st.shard0.shardName, min: {_id: MinKey}, max: {_id: 0}} - ] -})); - -removeAllReshardingCollections(); + zones: [{tag: existingZoneName, min: {_id: 5}, max: {_id: 10}, ns: ns}] +}, + presetReshardedChunks); st.stop(); })(); diff --git a/src/mongo/db/s/config/configsvr_reshard_collection_cmd.cpp b/src/mongo/db/s/config/configsvr_reshard_collection_cmd.cpp index ccebb894ecf..99d097b8528 100644 --- a/src/mongo/db/s/config/configsvr_reshard_collection_cmd.cpp +++ b/src/mongo/db/s/config/configsvr_reshard_collection_cmd.cpp @@ -211,9 +211,7 @@ public: instance->getObserver()->awaitAllDonorsReadyToDonate().wait(); // This promise is currently automatically filled by recipient shards after creating - // their ReshardingRecipientStateMachines. - // TODO SERVER-51217 Update this comment to reflect that the temporary collection - // has been created. + // the temporary resharding collection. instance->getObserver()->awaitAllRecipientsFinishedApplying().wait(); instance->interrupt( diff --git a/src/mongo/db/s/resharding/resharding_recipient_service.cpp b/src/mongo/db/s/resharding/resharding_recipient_service.cpp index 25c87941614..1f62ed5c3ae 100644 --- a/src/mongo/db/s/resharding/resharding_recipient_service.cpp +++ b/src/mongo/db/s/resharding/resharding_recipient_service.cpp @@ -49,21 +49,15 @@ namespace mongo { namespace resharding { void createTemporaryReshardingCollectionLocally(OperationContext* opCtx, - const NamespaceString& reshardingNss, + const NamespaceString& originalNss, + const UUID& reshardingUUID, + const UUID& existingUUID, Timestamp fetchTimestamp) { LOGV2_DEBUG( - 5002300, 1, "Creating temporary resharding collection", "namespace"_attr = reshardingNss); + 5002300, 1, "Creating temporary resharding collection", "originalNss"_attr = originalNss); auto catalogCache = Grid::get(opCtx)->catalogCache(); - auto reshardingCm = - uassertStatusOK(catalogCache->getCollectionRoutingInfo(opCtx, reshardingNss)); - uassert( - 5002301, - "Expected cached metadata for resharding temporary collection to have resharding fields", - reshardingCm.getReshardingFields() && - reshardingCm.getReshardingFields()->getRecipientFields()); - auto originalNss = - reshardingCm.getReshardingFields()->getRecipientFields()->getOriginalNamespace(); + auto reshardingNss = constructTemporaryReshardingNss(originalNss.db(), existingUUID); // Load the original collection's options from the database's primary shard. auto [collOptions, uuid] = sharded_agg_helpers::shardVersionRetry( @@ -74,12 +68,9 @@ void createTemporaryReshardingCollectionLocally(OperationContext* opCtx, [&]() -> MigrationDestinationManager::CollectionOptionsAndUUID { auto originalCm = uassertStatusOK(catalogCache->getCollectionRoutingInfo(opCtx, originalNss)); - uassert(ErrorCodes::InvalidUUID, - "Expected cached metadata for resharding temporary collection to have a UUID", - originalCm.getUUID()); return MigrationDestinationManager::getCollectionOptions( opCtx, - NamespaceStringOrUUID(originalNss.db().toString(), *originalCm.getUUID()), + NamespaceStringOrUUID(originalNss.db().toString(), existingUUID), originalCm.dbPrimary(), originalCm, fetchTimestamp); @@ -97,13 +88,10 @@ void createTemporaryReshardingCollectionLocally(OperationContext* opCtx, uassert(ErrorCodes::NamespaceNotSharded, str::stream() << "Expected collection " << originalNss << " to be sharded", originalCm.isSharded()); - uassert(ErrorCodes::InvalidUUID, - "Expected cached metadata for resharding temporary collection to have a UUID", - originalCm.getUUID()); auto indexShardId = originalCm.getMinKeyShardIdWithSimpleCollation(); return MigrationDestinationManager::getCollectionIndexes( opCtx, - NamespaceStringOrUUID(originalNss.db().toString(), *originalCm.getUUID()), + NamespaceStringOrUUID(originalNss.db().toString(), existingUUID), indexShardId, originalCm, fetchTimestamp); @@ -111,7 +99,6 @@ void createTemporaryReshardingCollectionLocally(OperationContext* opCtx, // Set the temporary resharding collection's UUID to the resharding UUID. Note that // BSONObj::addFields() replaces any fields that already exist. - auto reshardingUUID = reshardingCm.getReshardingFields()->getUuid(); collOptions = collOptions.addFields(BSON("uuid" << reshardingUUID)); CollectionOptionsAndIndexes optionsAndIndexes = {reshardingUUID, indexes, idIndex, collOptions}; @@ -216,8 +203,14 @@ void ReshardingRecipientService::RecipientStateMachine:: return; } - // TODO SERVER-51217: Call - // resharding_recipient_service_util::createTemporaryReshardingCollectionLocally() + { + auto opCtx = cc().makeOperationContext(); + resharding::createTemporaryReshardingCollectionLocally(opCtx.get(), + _recipientDoc.getNss(), + _recipientDoc.get_id(), + _recipientDoc.getExistingUUID(), + *_recipientDoc.getFetchTimestamp()); + } _transitionState(RecipientStateEnum::kCloning); } diff --git a/src/mongo/db/s/resharding/resharding_recipient_service.h b/src/mongo/db/s/resharding/resharding_recipient_service.h index 9c53b20ca8a..da04ed475cf 100644 --- a/src/mongo/db/s/resharding/resharding_recipient_service.h +++ b/src/mongo/db/s/resharding/resharding_recipient_service.h @@ -44,7 +44,9 @@ namespace resharding { * respectively. */ void createTemporaryReshardingCollectionLocally(OperationContext* opCtx, - const NamespaceString& reshardingNss, + const NamespaceString& originalNss, + const UUID& reshardingUUID, + const UUID& existingUUID, Timestamp fetchTimestamp); } // namespace resharding diff --git a/src/mongo/db/s/resharding/resharding_recipient_service_test.cpp b/src/mongo/db/s/resharding/resharding_recipient_service_test.cpp index debfc34017a..341b8c96c26 100644 --- a/src/mongo/db/s/resharding/resharding_recipient_service_test.cpp +++ b/src/mongo/db/s/resharding/resharding_recipient_service_test.cpp @@ -251,7 +251,7 @@ TEST_F(ReshardingRecipientServiceTest, CreateLocalReshardingCollectionBasic) { }); resharding::createTemporaryReshardingCollectionLocally( - operationContext(), kReshardingNss, kDefaultFetchTimestamp); + operationContext(), kOrigNss, kReshardingUUID, kOrigUUID, kDefaultFetchTimestamp); future.default_timed_get(); @@ -301,7 +301,7 @@ TEST_F(ReshardingRecipientServiceTest, }); resharding::createTemporaryReshardingCollectionLocally( - operationContext(), kReshardingNss, kDefaultFetchTimestamp); + operationContext(), kOrigNss, kReshardingUUID, kOrigUUID, kDefaultFetchTimestamp); future.default_timed_get(); @@ -362,7 +362,7 @@ TEST_F(ReshardingRecipientServiceTest, }); resharding::createTemporaryReshardingCollectionLocally( - operationContext(), kReshardingNss, kDefaultFetchTimestamp); + operationContext(), kOrigNss, kReshardingUUID, kOrigUUID, kDefaultFetchTimestamp); future.default_timed_get(); @@ -425,7 +425,7 @@ TEST_F(ReshardingRecipientServiceTest, }); resharding::createTemporaryReshardingCollectionLocally( - operationContext(), kReshardingNss, kDefaultFetchTimestamp); + operationContext(), kOrigNss, kReshardingUUID, kOrigUUID, kDefaultFetchTimestamp); future.default_timed_get(); @@ -478,7 +478,7 @@ TEST_F(ReshardingRecipientServiceTest, }); resharding::createTemporaryReshardingCollectionLocally( - operationContext(), kReshardingNss, kDefaultFetchTimestamp); + operationContext(), kOrigNss, kReshardingUUID, kOrigUUID, kDefaultFetchTimestamp); future.default_timed_get(); |