diff options
author | Kim Tao <kimberly.tao@mongodb.com> | 2018-12-13 12:06:13 -0500 |
---|---|---|
committer | Kim Tao <kimberly.tao@mongodb.com> | 2019-01-11 16:27:59 -0500 |
commit | f7d9727c865b3603a9754e18f0bbfad92131f666 (patch) | |
tree | 1181ff4334bc065761cf657bfe8ae03315e88641 | |
parent | a9177f25ba33a938b5d1bfcc7cc9c17443cdad45 (diff) | |
download | mongo-f7d9727c865b3603a9754e18f0bbfad92131f666.tar.gz |
SERVER-38285: only acquire database X lock on the recipient if the collection does not exist
-rw-r--r-- | src/mongo/db/s/migration_destination_manager.cpp | 115 |
1 files changed, 66 insertions, 49 deletions
diff --git a/src/mongo/db/s/migration_destination_manager.cpp b/src/mongo/db/s/migration_destination_manager.cpp index 69f62df8751..13488bfd5b3 100644 --- a/src/mongo/db/s/migration_destination_manager.cpp +++ b/src/mongo/db/s/migration_destination_manager.cpp @@ -590,25 +590,13 @@ void MigrationDestinationManager::cloneCollectionIndexesAndOptions(OperationCont // 1. Create the collection (if it doesn't already exist) and create any indexes we are // missing (auto-heal indexes). - // Hold the DBLock in X mode across creating the collection and indexes, so that a - // concurrent dropIndex cannot run between creating the collection and indexes and fail with - // IndexNotFound, though the index will get created. - // We could take the DBLock in IX mode while checking if the collection already exists and - // then upgrade it to X mode while creating the collection and indexes, but there is no way - // to upgrade a DBLock once it's taken without releasing it, so we pre-emptively take it in - // mode X. - AutoGetOrCreateDb autoCreateDb(opCtx, nss.db(), MODE_X); - uassert(ErrorCodes::NotMaster, - str::stream() << "Unable to create collection " << nss.ns() - << " because the node is not primary", - repl::ReplicationCoordinator::get(opCtx)->canAcceptWritesFor(opCtx, nss)); - - Database* const db = autoCreateDb.getDb(); + // Checks that the collection's UUID matches the donor's. + auto checkUUIDsMatch = [&](const Collection* collection) { + uassert(ErrorCodes::NotMaster, + str::stream() << "Unable to create collection " << nss.ns() + << " because the node is not primary", + repl::ReplicationCoordinator::get(opCtx)->canAcceptWritesFor(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)); @@ -626,6 +614,46 @@ void MigrationDestinationManager::cloneCollectionIndexesAndOptions(OperationCont "a previous incarnation of " << nss.ns(), collection->uuid() == donorUUID); + }; + + // Gets the missing indexes and checks if the collection is empty (auto-healing is + // possible). + auto checkEmptyOrGetMissingIndexesFromDonor = [&](Collection* collection) { + auto indexCatalog = collection->getIndexCatalog(); + auto indexSpecs = indexCatalog->removeExistingIndexes(opCtx, donorIndexSpecs); + if (!indexSpecs.empty()) { + // Only allow indexes to be copied if the collection does not have any documents. + uassert(ErrorCodes::CannotCreateCollection, + str::stream() << "aborting, shard is missing " << indexSpecs.size() + << " indexes and " + << "collection is not empty. Non-trivial " + << "index creation should be scheduled manually", + collection->numRecords(opCtx) == 0); + } + return indexSpecs; + }; + + { + AutoGetCollection autoGetCollection(opCtx, nss, MODE_IS); + + auto collection = autoGetCollection.getCollection(); + if (collection) { + checkUUIDsMatch(collection); + auto indexSpecs = checkEmptyOrGetMissingIndexesFromDonor(collection); + if (indexSpecs.empty()) { + return; + } + } + } + + // Take the exclusive database lock if the collection does not exist or indexes are missing + // (needs auto-heal). + AutoGetOrCreateDb autoCreateDb(opCtx, nss.db(), MODE_X); + auto db = autoCreateDb.getDb(); + + auto collection = db->getCollection(opCtx, nss); + if (collection) { + checkUUIDsMatch(collection); } else { // We do not have a collection by this name. Create the collection with the donor's // options. @@ -637,42 +665,31 @@ void MigrationDestinationManager::cloneCollectionIndexesAndOptions(OperationCont uassertStatusOK(db->userCreateNS( opCtx, nss, collectionOptions, createDefaultIndexes, donorIdIndexSpec)); wuow.commit(); - collection = db->getCollection(opCtx, nss); } + auto indexSpecs = checkEmptyOrGetMissingIndexesFromDonor(collection); + if (!indexSpecs.empty()) { + WriteUnitOfWork wunit(opCtx); + + for (const auto& spec : indexSpecs) { + // Make sure to create index on secondaries as well. Oplog entry must be written + // before the index is added to the index catalog for correct rollback operation. + // See SERVER-35780 and SERVER-35070. + serviceContext->getOpObserver()->onCreateIndex( + opCtx, collection->ns(), *(collection->uuid()), spec, true /* fromMigrate */); + + // Since the collection is empty, we can add and commit the index catalog entry + // within a single WUOW. + auto indexCatalog = collection->getIndexCatalog(); + uassertStatusOKWithContext(indexCatalog->createIndexOnEmptyCollection(opCtx, spec), + str::stream() + << "failed to create index before migrating data: " + << redact(spec)); + } - auto indexCatalog = collection->getIndexCatalog(); - auto indexSpecs = indexCatalog->removeExistingIndexes(opCtx, donorIndexSpecs); - if (indexSpecs.empty()) { - return; - } - - // Only copy indexes if the collection does not have any documents. - uassert(ErrorCodes::CannotCreateCollection, - str::stream() << "aborting, shard is missing " << indexSpecs.size() - << " indexes and " - << "collection is not empty. Non-trivial " - << "index creation should be scheduled manually", - collection->numRecords(opCtx) == 0); - - WriteUnitOfWork wunit(opCtx); - - for (const auto& spec : indexSpecs) { - // Make sure to create index on secondaries as well. Oplog entry must be written before - // the index is added to the index catalog for correct rollback operation. - // See SERVER-35780 and SERVER-35070. - serviceContext->getOpObserver()->onCreateIndex( - opCtx, collection->ns(), *(collection->uuid()), spec, true /* fromMigrate */); - - // Since the collection is empty, we can add and commit the index catalog entry within - // a single WUOW. - uassertStatusOKWithContext( - indexCatalog->createIndexOnEmptyCollection(opCtx, spec), - str::stream() << "failed to create index before migrating data: " << redact(spec)); + wunit.commit(); } - - wunit.commit(); } } |