diff options
author | Esha Maharishi <esha.maharishi@mongodb.com> | 2017-11-16 12:52:42 -0500 |
---|---|---|
committer | Esha Maharishi <esha.maharishi@mongodb.com> | 2017-11-21 11:47:33 -0500 |
commit | db79fdfb42475212dcc902a00420bf693b980d17 (patch) | |
tree | 87d7718230d0f973ea7cf7f0dcca6d3d3f663aed /src | |
parent | 808fca641401392a5f45102ad6d018266cec1bc2 (diff) | |
download | mongo-db79fdfb42475212dcc902a00420bf693b980d17.tar.gz |
SERVER-31909 Recipient shard should fail migration if it already has the collection with a different UUID
(cherry picked from commit 545c11242d737bf1a8ec19c2f2d7a7f14cc08f46)
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/s/migration_destination_manager.cpp | 78 |
1 files changed, 54 insertions, 24 deletions
diff --git a/src/mongo/db/s/migration_destination_manager.cpp b/src/mongo/db/s/migration_destination_manager.cpp index 9efdc9c0c4c..95d6db11dc4 100644 --- a/src/mongo/db/s/migration_destination_manager.cpp +++ b/src/mongo/db/s/migration_destination_manager.cpp @@ -39,6 +39,7 @@ #include "mongo/db/auth/authorization_manager_global.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/catalog/collection.h" +#include "mongo/db/catalog/collection_catalog_entry.h" #include "mongo/db/catalog/database.h" #include "mongo/db/catalog/document_validation.h" #include "mongo/db/catalog/index_create.h" @@ -208,6 +209,7 @@ MONGO_FP_DECLARE(migrateThreadHangAtStep4); MONGO_FP_DECLARE(migrateThreadHangAtStep5); MONGO_FP_DECLARE(migrateThreadHangAtStep6); +MONGO_FP_DECLARE(failMigrationLeaveOrphans); MONGO_FP_DECLARE(failMigrationReceivedOutOfRangeOperation); } // namespace @@ -447,7 +449,7 @@ void MigrationDestinationManager::_migrateThread(BSONObj min, setStateFail("migrate failed with unknown exception: UNKNOWN ERROR"); } - if (getState() != DONE) { + if (getState() != DONE && !MONGO_FAIL_POINT(failMigrationLeaveOrphans)) { _forgetPending(opCtx.get(), _nss, epoch, ChunkRange(min, max)); } @@ -493,9 +495,9 @@ void MigrationDestinationManager::_migrateDriver(OperationContext* opCtx, DisableDocumentValidation validationDisabler(opCtx); - std::vector<BSONObj> indexSpecs; - BSONObj idIndexSpec; - BSONObj options; + std::vector<BSONObj> donorIndexSpecs; + BSONObj donorIdIndexSpec; + BSONObj donorOptions; { // 0. Get the collection indexes and options from the donor shard. @@ -505,11 +507,11 @@ void MigrationDestinationManager::_migrateDriver(OperationContext* opCtx, // Get indexes by calling listIndexes against the donor. auto indexes = conn->getIndexSpecs(_nss.ns()); for (auto&& spec : indexes) { - indexSpecs.push_back(spec); + donorIndexSpecs.push_back(spec); if (auto indexNameElem = spec[IndexDescriptor::kIndexNameFieldName]) { if (indexNameElem.type() == BSONType::String && indexNameElem.valueStringData() == "_id_"_sd) { - idIndexSpec = spec; + donorIdIndexSpec = spec; } } } @@ -532,10 +534,10 @@ void MigrationDestinationManager::_migrateDriver(OperationContext* opCtx, // The entire options include both the settable options under the 'options' field in the // listCollections response, and the UUID under the 'info' field. - BSONObjBuilder optionsBob; + BSONObjBuilder donorOptionsBob; if (entry["options"].isABSONObj()) { - optionsBob.appendElements(entry["options"].Obj()); + donorOptionsBob.appendElements(entry["options"].Obj()); } if (serverGlobalParams.featureCompatibility.isSchemaVersion36()) { @@ -555,9 +557,9 @@ void MigrationDestinationManager::_migrateDriver(OperationContext* opCtx, "compatibility version across the cluster."); return; } - optionsBob.append(info["uuid"]); + donorOptionsBob.append(info["uuid"]); } - options = optionsBob.obj(); + donorOptions = donorOptionsBob.obj(); } { @@ -581,44 +583,66 @@ void MigrationDestinationManager::_migrateDriver(OperationContext* opCtx, } Database* const db = ctx.db(); - if (!db->getCollection(opCtx, _nss)) { + Collection* collection = db->getCollection(opCtx, _nss); + if (collection) { + // We have an entry for a collection by this name. Check that our collection's UUID + // matches the donor's. + boost::optional<UUID> donorUUID; + if (!donorOptions["uuid"].eoo()) { + donorUUID.emplace(UUID::parse(donorOptions)); + } + + if (!collection->getCatalogEntry()->isEqualToMetadataUUID(opCtx, donorUUID)) { + setStateFailWarn( + str::stream() + << "Cannot receive chunk " + << ChunkRange(min, max).toString() + << " for collection " + << _nss.ns() + << " because we already have an identically named collection with UUID " + << (collection->uuid() ? collection->uuid()->toString() : "(none)") + << ", which differs from the donor's UUID " + << (donorUUID ? donorUUID->toString() : "(none)") + << ". Manually drop the collection on this shard if it contains data from a " + "previous incarnation of " + << _nss.ns()); + return; + } + } else { + // We do not have a collection by this name. Create the collection with the donor's + // options. WriteUnitOfWork wuow(opCtx); const bool createDefaultIndexes = true; Status status = userCreateNS(opCtx, db, _nss.ns(), - options, + donorOptions, CollectionOptions::parseForStorage, createDefaultIndexes, - idIndexSpec); + donorIdIndexSpec); if (!status.isOK()) { warning() << "failed to create collection [" << _nss << "] " - << " with options " << options << ": " << redact(status); + << " with options " << donorOptions << ": " << redact(status); } wuow.commit(); - } - - Collection* const collection = db->getCollection(opCtx, _nss); - if (!collection) { - setStateFailWarn(str::stream() << "collection dropped during migration: " << _nss.ns()); - return; + collection = db->getCollection(opCtx, _nss); } MultiIndexBlock indexer(opCtx, collection); - indexer.removeExistingIndexes(&indexSpecs); + indexer.removeExistingIndexes(&donorIndexSpecs); - if (!indexSpecs.empty()) { + if (!donorIndexSpecs.empty()) { // Only copy indexes if the collection does not have any documents. if (collection->numRecords(opCtx) > 0) { setStateFailWarn(str::stream() << "aborting migration, shard is missing " - << indexSpecs.size() + << donorIndexSpecs.size() << " indexes and " << "collection is not empty. Non-trivial " << "index creation should be scheduled manually"); return; } - auto indexInfoObjs = indexer.init(indexSpecs); + auto indexInfoObjs = indexer.init(donorIndexSpecs); if (!indexInfoObjs.isOK()) { setStateFailWarn(str::stream() << "failed to create index before migrating data. " << " error: " @@ -752,6 +776,12 @@ void MigrationDestinationManager::_migrateDriver(OperationContext* opCtx, timing.done(3); MONGO_FAIL_POINT_PAUSE_WHILE_SET(migrateThreadHangAtStep3); + + if (MONGO_FAIL_POINT(failMigrationLeaveOrphans)) { + setStateFail(str::stream() << "failing migration after cloning " << _numCloned + << " docs due to failMigrationLeaveOrphans failpoint"); + return; + } } // If running on a replicated system, we'll need to flush the docs we cloned to the |