diff options
author | Geert Bosch <geert@mongodb.com> | 2019-04-29 14:11:12 -0400 |
---|---|---|
committer | Geert Bosch <geert@mongodb.com> | 2019-05-07 13:17:24 -0400 |
commit | ab689c15777a5c2f7d35f602d125fdc7f2fb344c (patch) | |
tree | f10a2d6bfad8cc009e722ed366e635dfecbb7ee1 /src/mongo/db/catalog/collection_catalog.cpp | |
parent | 042f4e1f2164f3a801acfd379fafc217817b392c (diff) | |
download | mongo-ab689c15777a5c2f7d35f602d125fdc7f2fb344c.tar.gz |
SERVER-40892 Rename UUIDCatalog to CollectionCatalog
Diffstat (limited to 'src/mongo/db/catalog/collection_catalog.cpp')
-rw-r--r-- | src/mongo/db/catalog/collection_catalog.cpp | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/src/mongo/db/catalog/collection_catalog.cpp b/src/mongo/db/catalog/collection_catalog.cpp new file mode 100644 index 00000000000..1bfb90dba4e --- /dev/null +++ b/src/mongo/db/catalog/collection_catalog.cpp @@ -0,0 +1,607 @@ +/** + * Copyright (C) 2018-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage + +#include "mongo/platform/basic.h" + +#include "collection_catalog.h" + +#include "mongo/db/catalog/database.h" +#include "mongo/db/concurrency/lock_manager_defs.h" +#include "mongo/db/storage/recovery_unit.h" +#include "mongo/util/assert_util.h" +#include "mongo/util/log.h" +#include "mongo/util/uuid.h" + +namespace mongo { +namespace { +const ServiceContext::Decoration<CollectionCatalog> getCatalog = + ServiceContext::declareDecoration<CollectionCatalog>(); +} // namespace + +class CollectionCatalog::FinishDropChange : public RecoveryUnit::Change { +public: + FinishDropChange(CollectionCatalog& catalog, + std::unique_ptr<Collection> coll, + CollectionUUID uuid) + : _catalog(catalog), _coll(std::move(coll)), _uuid(uuid) {} + + void commit(boost::optional<Timestamp>) override { + _coll.reset(); + } + + void rollback() override { + _catalog.registerCollectionObject(_uuid, std::move(_coll)); + } + +private: + CollectionCatalog& _catalog; + std::unique_ptr<Collection> _coll; + CollectionUUID _uuid; +}; + +CollectionCatalog::iterator::iterator(StringData dbName, + uint64_t genNum, + const CollectionCatalog& catalog) + : _dbName(dbName), _genNum(genNum), _catalog(&catalog) { + auto minUuid = UUID::parse("00000000-0000-0000-0000-000000000000").getValue(); + + stdx::lock_guard<stdx::mutex> lock(_catalog->_catalogLock); + _mapIter = _catalog->_orderedCollections.lower_bound(std::make_pair(_dbName, minUuid)); + + // The entry _mapIter points to is valid if it's not at the end of _orderedCollections and + // the entry's database is the same as dbName. + while (_mapIter != _catalog->_orderedCollections.end() && _mapIter->first.first == _dbName && + _mapIter->second->collectionPtr == nullptr) { + _mapIter++; + } + if (_mapIter != _catalog->_orderedCollections.end() && _mapIter->first.first == _dbName) { + _uuid = _mapIter->first.second; + } +} + +CollectionCatalog::iterator::iterator( + std::map<std::pair<std::string, CollectionUUID>, CollectionInfo*>::const_iterator mapIter) + : _mapIter(mapIter) {} + +CollectionCatalog::iterator::pointer CollectionCatalog::iterator::operator->() { + stdx::lock_guard<stdx::mutex> lock(_catalog->_catalogLock); + _repositionIfNeeded(); + if (_exhausted()) { + return nullptr; + } + + return &_mapIter->second->collectionPtr; +} + +CollectionCatalog::iterator::reference CollectionCatalog::iterator::operator*() { + stdx::lock_guard<stdx::mutex> lock(_catalog->_catalogLock); + _repositionIfNeeded(); + if (_exhausted()) { + return _nullCollection; + } + + return _mapIter->second->collectionPtr; +} + +boost::optional<CollectionUUID> CollectionCatalog::iterator::uuid() { + return _uuid; +} + +CollectionCatalog::iterator CollectionCatalog::iterator::operator++() { + stdx::lock_guard<stdx::mutex> lock(_catalog->_catalogLock); + + // Skip over CollectionInfo that has CatalogEntry but has no Collection object. + do { + if (!_repositionIfNeeded()) { + _mapIter++; // If the position was not updated, increment iterator to next element. + } + + if (_exhausted()) { + // If the iterator is at the end of the map or now points to an entry that does not + // correspond to the correct database. + _mapIter = _catalog->_orderedCollections.end(); + _uuid = boost::none; + return *this; + } + } while (_mapIter->second->collectionPtr == nullptr); + + _uuid = _mapIter->first.second; + return *this; +} + +CollectionCatalog::iterator CollectionCatalog::iterator::operator++(int) { + auto oldPosition = *this; + ++(*this); + return oldPosition; +} + +bool CollectionCatalog::iterator::operator==(const iterator& other) { + stdx::lock_guard<stdx::mutex> lock(_catalog->_catalogLock); + + if (other._mapIter == _catalog->_orderedCollections.end()) { + return _uuid == boost::none; + } + + return _uuid == other._uuid; +} + +bool CollectionCatalog::iterator::operator!=(const iterator& other) { + return !(*this == other); +} + +bool CollectionCatalog::iterator::_repositionIfNeeded() { + if (_genNum == _catalog->_generationNumber) { + return false; + } + + _genNum = _catalog->_generationNumber; + // If the map has been modified, find the entry the iterator was on, or the one right after it. + _mapIter = _catalog->_orderedCollections.lower_bound(std::make_pair(_dbName, *_uuid)); + + // It is possible that the collection object is gone while catalog entry is + // still in the map. Skip that type of entries. + while (!_exhausted() && _mapIter->second->collectionPtr == nullptr) { + _mapIter++; + } + + if (_exhausted()) { + return true; + } + + invariant(_mapIter->second->collectionPtr); + + // If the old pair matches the previous DB name and UUID, the iterator was not repositioned. + auto dbUuidPair = _mapIter->first; + bool repositioned = !(dbUuidPair.first == _dbName && dbUuidPair.second == _uuid); + _uuid = dbUuidPair.second; + + return repositioned; +} + +bool CollectionCatalog::iterator::_exhausted() { + return _mapIter == _catalog->_orderedCollections.end() || _mapIter->first.first != _dbName; +} + +CollectionCatalog& CollectionCatalog::get(ServiceContext* svcCtx) { + return getCatalog(svcCtx); +} +CollectionCatalog& CollectionCatalog::get(OperationContext* opCtx) { + return getCatalog(opCtx->getServiceContext()); +} + +void CollectionCatalog::onCreateCollection(OperationContext* opCtx, + std::unique_ptr<Collection> coll, + CollectionUUID uuid) { + registerCollectionObject(uuid, std::move(coll)); + opCtx->recoveryUnit()->onRollback([this, uuid] { deregisterCollectionObject(uuid); }); +} + +void CollectionCatalog::onDropCollection(OperationContext* opCtx, CollectionUUID uuid) { + auto coll = deregisterCollectionObject(uuid); + opCtx->recoveryUnit()->registerChange(new FinishDropChange(*this, std::move(coll), uuid)); +} + +void CollectionCatalog::setCollectionNamespace(OperationContext* opCtx, + Collection* coll, + const NamespaceString& fromCollection, + const NamespaceString& toCollection) { + // Rather than maintain, in addition to the UUID -> Collection* mapping, an auxiliary + // data structure with the UUID -> namespace mapping, the CollectionCatalog relies on + // Collection::ns() to provide UUID to namespace lookup. In addition, the CollectionCatalog + // does not require callers to hold locks. + // + // This means that Collection::ns() may be called while only '_catalogLock' (and no lock + // manager locks) are held. The purpose of this function is ensure that we write to the + // Collection's namespace string under '_catalogLock'. + invariant(coll); + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + CollectionCatalogEntry* catalogEntry = + _collections[fromCollection]->collectionCatalogEntry.get(); + + coll->setNs(toCollection); + catalogEntry->setNs(toCollection); + + _collections[toCollection] = _collections[fromCollection]; + _collections.erase(fromCollection); + + ResourceId oldRid = ResourceId(RESOURCE_COLLECTION, fromCollection.ns()); + ResourceId newRid = ResourceId(RESOURCE_COLLECTION, toCollection.ns()); + + removeResource(oldRid, fromCollection.ns()); + addResource(newRid, toCollection.ns()); + + opCtx->recoveryUnit()->onRollback([this, coll, fromCollection, toCollection, catalogEntry] { + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + coll->setNs(std::move(fromCollection)); + catalogEntry->setNs(fromCollection); + + _collections[fromCollection] = _collections[toCollection]; + _collections.erase(toCollection); + + ResourceId oldRid = ResourceId(RESOURCE_COLLECTION, fromCollection.ns()); + ResourceId newRid = ResourceId(RESOURCE_COLLECTION, toCollection.ns()); + + removeResource(newRid, toCollection.ns()); + addResource(oldRid, fromCollection.ns()); + }); +} + +void CollectionCatalog::onCloseDatabase(OperationContext* opCtx, std::string dbName) { + invariant(opCtx->lockState()->isW()); + for (auto it = begin(dbName); it != end(); ++it) { + deregisterCollectionObject(it.uuid().get()); + } + + auto rid = ResourceId(RESOURCE_DATABASE, dbName); + removeResource(rid, dbName); +} + +void CollectionCatalog::onCloseCatalog(OperationContext* opCtx) { + invariant(opCtx->lockState()->isW()); + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + invariant(!_shadowCatalog); + _shadowCatalog.emplace(); + for (auto& entry : _catalog) + _shadowCatalog->insert({entry.first, entry.second.collection->ns()}); +} + +void CollectionCatalog::onOpenCatalog(OperationContext* opCtx) { + invariant(opCtx->lockState()->isW()); + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + invariant(_shadowCatalog); + _shadowCatalog.reset(); +} + +Collection* CollectionCatalog::lookupCollectionByUUID(CollectionUUID uuid) const { + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + auto foundIt = _catalog.find(uuid); + return foundIt == _catalog.end() || foundIt->second.collectionPtr == nullptr + ? nullptr + : foundIt->second.collection.get(); +} + +Collection* CollectionCatalog::lookupCollectionByNamespace(const NamespaceString& nss) const { + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + auto it = _collections.find(nss); + return it == _collections.end() || it->second->collectionPtr == nullptr + ? nullptr + : it->second->collection.get(); +} + +CollectionCatalogEntry* CollectionCatalog::lookupCollectionCatalogEntryByUUID( + CollectionUUID uuid) const { + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + auto foundIt = _catalog.find(uuid); + return foundIt == _catalog.end() ? nullptr : foundIt->second.collectionCatalogEntry.get(); +} + +CollectionCatalogEntry* CollectionCatalog::lookupCollectionCatalogEntryByNamespace( + const NamespaceString& nss) const { + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + auto it = _collections.find(nss); + return it == _collections.end() ? nullptr : it->second->collectionCatalogEntry.get(); +} + +boost::optional<NamespaceString> CollectionCatalog::lookupNSSByUUID(CollectionUUID uuid) const { + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + auto foundIt = _catalog.find(uuid); + if (foundIt != _catalog.end()) { + NamespaceString ns = foundIt->second.collectionCatalogEntry->ns(); + invariant(!ns.isEmpty()); + return ns; + } + + // Only in the case that the catalog is closed and a UUID is currently unknown, resolve it + // using the pre-close state. This ensures that any tasks reloading the catalog can see their + // own updates. + if (_shadowCatalog) { + auto shadowIt = _shadowCatalog->find(uuid); + if (shadowIt != _shadowCatalog->end()) + return shadowIt->second; + } + return boost::none; +} + +boost::optional<CollectionUUID> CollectionCatalog::lookupUUIDByNSS( + const NamespaceString& nss) const { + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + auto minUuid = UUID::parse("00000000-0000-0000-0000-000000000000").getValue(); + auto it = _orderedCollections.lower_bound(std::make_pair(nss.db().toString(), minUuid)); + + // The entry _mapIter points to is valid if it's not at the end of _orderedCollections and + // the entry's database is the same as dbName. + while (it != _orderedCollections.end() && it->first.first == nss.db()) { + if (it->second->collectionCatalogEntry->ns() == nss) { + return it->first.second; + } + ++it; + } + return boost::none; +} + +std::vector<CollectionUUID> CollectionCatalog::getAllCollectionUUIDsFromDb( + StringData dbName) const { + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + auto minUuid = UUID::parse("00000000-0000-0000-0000-000000000000").getValue(); + auto it = _orderedCollections.lower_bound(std::make_pair(dbName.toString(), minUuid)); + + std::vector<CollectionUUID> ret; + while (it != _orderedCollections.end() && it->first.first == dbName) { + ret.push_back(it->first.second); + ++it; + } + return ret; +} + +std::vector<NamespaceString> CollectionCatalog::getAllCollectionNamesFromDb( + OperationContext* opCtx, StringData dbName) const { + invariant(opCtx->lockState()->isDbLockedForMode(dbName, MODE_S)); + + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + auto minUuid = UUID::parse("00000000-0000-0000-0000-000000000000").getValue(); + + std::vector<NamespaceString> ret; + for (auto it = _orderedCollections.lower_bound(std::make_pair(dbName.toString(), minUuid)); + it != _orderedCollections.end() && it->first.first == dbName; + ++it) { + ret.push_back(it->second->collectionCatalogEntry->ns()); + } + return ret; +} + +std::vector<std::string> CollectionCatalog::getAllDbNames() const { + std::vector<std::string> ret; + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + auto maxUuid = UUID::parse("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF").getValue(); + auto iter = _orderedCollections.upper_bound(std::make_pair("", maxUuid)); + while (iter != _orderedCollections.end()) { + auto dbName = iter->first.first; + ret.push_back(dbName); + iter = _orderedCollections.upper_bound(std::make_pair(dbName, maxUuid)); + } + return ret; +} + +void CollectionCatalog::registerCatalogEntry( + CollectionUUID uuid, std::unique_ptr<CollectionCatalogEntry> collectionCatalogEntry) { + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + + LOG(0) << "Registering catalog entry " << collectionCatalogEntry->ns() << " with UUID " << uuid; + + auto ns = collectionCatalogEntry->ns(); + auto dbName = ns.db().toString(); + auto dbIdPair = std::make_pair(dbName, uuid); + + // Make sure no entry related to this uuid. + invariant(_catalog.find(uuid) == _catalog.end()); + invariant(_collections.find(ns) == _collections.end()); + invariant(_orderedCollections.find(dbIdPair) == _orderedCollections.end()); + + CollectionInfo collectionInfo = {nullptr, /* std::unique_ptr<Collection> */ + nullptr, + std::move(collectionCatalogEntry)}; + + _catalog[uuid] = std::move(collectionInfo); + _collections[ns] = &_catalog[uuid]; + _orderedCollections[dbIdPair] = &_catalog[uuid]; +} + +void CollectionCatalog::registerCollectionObject(CollectionUUID uuid, + std::unique_ptr<Collection> coll) { + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + + LOG(0) << "Registering collection object " << coll->ns() << " with UUID " << uuid; + + auto ns = coll->ns(); + auto dbName = ns.db().toString(); + auto dbIdPair = std::make_pair(dbName, uuid); + + // Make sure catalog entry associated with this uuid already exists. + invariant(_catalog.find(uuid) != _catalog.end()); + invariant(_collections.find(ns) != _collections.end()); + invariant(_orderedCollections.find(dbIdPair) != _orderedCollections.end()); + invariant(_catalog[uuid].collectionCatalogEntry); + invariant(_collections[ns]->collectionCatalogEntry); + invariant(_orderedCollections[dbIdPair]->collectionCatalogEntry); + + // Make sure collection object does not exist. + invariant(_catalog[uuid].collection == nullptr); + invariant(_collections[ns]->collection == nullptr); + invariant(_orderedCollections[dbIdPair]->collection == nullptr); + + + _catalog[uuid].collection = std::move(coll); + _catalog[uuid].collectionPtr = _catalog[uuid].collection.get(); + + auto dbRid = ResourceId(RESOURCE_DATABASE, dbName); + addResource(dbRid, dbName); + + auto collRid = ResourceId(RESOURCE_COLLECTION, ns.ns()); + addResource(collRid, ns.ns()); +} + +std::unique_ptr<Collection> CollectionCatalog::deregisterCollectionObject(CollectionUUID uuid) { + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + + invariant(_catalog.find(uuid) != _catalog.end()); + invariant(_catalog[uuid].collection); + + auto coll = std::move(_catalog[uuid].collection); + auto ns = coll->ns(); + auto dbName = ns.db().toString(); + auto dbIdPair = std::make_pair(dbName, uuid); + + LOG(0) << "Deregistering collection object " << ns << " with UUID " << uuid; + + // Make sure collection object exists. + invariant(_collections.find(ns) != _collections.end()); + invariant(_orderedCollections.find(dbIdPair) != _orderedCollections.end()); + + _catalog[uuid].collection = nullptr; + _catalog[uuid].collectionPtr = nullptr; + + // Make sure collection catalog entry still exists. + invariant(_catalog[uuid].collectionCatalogEntry); + + auto collRid = ResourceId(RESOURCE_COLLECTION, ns.ns()); + removeResource(collRid, ns.ns()); + + // Removal from an ordered map will invalidate iterators and potentially references to the + // references to the erased element. + _generationNumber++; + + return coll; +} + +std::unique_ptr<CollectionCatalogEntry> CollectionCatalog::deregisterCatalogEntry( + CollectionUUID uuid) { + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + + invariant(_catalog.find(uuid) != _catalog.end()); + invariant(_catalog[uuid].collectionCatalogEntry); + + auto catalogEntry = std::move(_catalog[uuid].collectionCatalogEntry); + auto ns = catalogEntry->ns(); + auto dbName = ns.db().toString(); + auto dbIdPair = std::make_pair(dbName, uuid); + + LOG(0) << "Deregistering catalog entry " << ns << " with UUID " << uuid; + + // Make sure collection object is already gone. + invariant(_catalog[uuid].collection == nullptr); + invariant(_catalog[uuid].collectionPtr == nullptr); + + // Make sure catalog entry exist. + invariant(_collections.find(ns) != _collections.end()); + invariant(_orderedCollections.find(dbIdPair) != _orderedCollections.end()); + + _orderedCollections.erase(dbIdPair); + _collections.erase(ns); + _catalog.erase(uuid); + + // Removal from an ordered map will invalidate iterators and potentially references to the + // references to the erased element. + _generationNumber++; + + return catalogEntry; +} + +void CollectionCatalog::deregisterAllCatalogEntriesAndCollectionObjects() { + stdx::lock_guard<stdx::mutex> lock(_catalogLock); + + LOG(0) << "Deregistering all the catalog entries and collection objects"; + for (auto& entry : _catalog) { + auto uuid = entry.first; + auto ns = entry.second.collectionCatalogEntry->ns(); + auto dbName = ns.db().toString(); + auto dbIdPair = std::make_pair(dbName, uuid); + + LOG(0) << "Deregistering collection " << ns << " with UUID " << uuid; + + entry.second.collection.reset(); + entry.second.collectionCatalogEntry.reset(); + } + + _collections.clear(); + _orderedCollections.clear(); + _catalog.clear(); + + stdx::lock_guard<stdx::mutex> resourceLock(_resourceLock); + _resourceInformation.clear(); + + _generationNumber++; +} + +CollectionCatalog::iterator CollectionCatalog::begin(StringData db) const { + return iterator(db, _generationNumber, *this); +} + +CollectionCatalog::iterator CollectionCatalog::end() const { + return iterator(_orderedCollections.end()); +} + +boost::optional<std::string> CollectionCatalog::lookupResourceName(const ResourceId& rid) { + invariant(rid.getType() == RESOURCE_DATABASE || rid.getType() == RESOURCE_COLLECTION); + stdx::lock_guard<stdx::mutex> lock(_resourceLock); + + auto search = _resourceInformation.find(rid); + if (search == _resourceInformation.end()) { + return boost::none; + } + + std::set<std::string>& namespaces = search->second; + + // When there are multiple namespaces mapped to the same ResourceId, return boost::none as the + // ResourceId does not identify a single namespace. + if (namespaces.size() > 1) { + return boost::none; + } + + return *namespaces.begin(); +} + +void CollectionCatalog::removeResource(const ResourceId& rid, const std::string& entry) { + invariant(rid.getType() == RESOURCE_DATABASE || rid.getType() == RESOURCE_COLLECTION); + stdx::lock_guard<stdx::mutex> lock(_resourceLock); + + auto search = _resourceInformation.find(rid); + if (search == _resourceInformation.end()) { + return; + } + + std::set<std::string>& namespaces = search->second; + namespaces.erase(entry); + + // Remove the map entry if this is the last namespace in the set for the ResourceId. + if (namespaces.size() == 0) { + _resourceInformation.erase(search); + } +} + +void CollectionCatalog::addResource(const ResourceId& rid, const std::string& entry) { + invariant(rid.getType() == RESOURCE_DATABASE || rid.getType() == RESOURCE_COLLECTION); + stdx::lock_guard<stdx::mutex> lock(_resourceLock); + + auto search = _resourceInformation.find(rid); + if (search == _resourceInformation.end()) { + std::set<std::string> newSet = {entry}; + _resourceInformation.insert(std::make_pair(rid, newSet)); + return; + } + + std::set<std::string>& namespaces = search->second; + if (namespaces.count(entry) > 0) { + return; + } + + namespaces.insert(entry); +} + +} // namespace mongo |