diff options
Diffstat (limited to 'src/mongo/db/catalog/collection_info_cache_impl.cpp')
-rw-r--r-- | src/mongo/db/catalog/collection_info_cache_impl.cpp | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/src/mongo/db/catalog/collection_info_cache_impl.cpp b/src/mongo/db/catalog/collection_info_cache_impl.cpp new file mode 100644 index 00000000000..05805fdd3b2 --- /dev/null +++ b/src/mongo/db/catalog/collection_info_cache_impl.cpp @@ -0,0 +1,250 @@ +/** + * Copyright (C) 2017 10gen Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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 "mongo/db/catalog/collection_info_cache_impl.h" + +#include "mongo/base/init.h" +#include "mongo/db/catalog/collection.h" +#include "mongo/db/concurrency/d_concurrency.h" +#include "mongo/db/fts/fts_spec.h" +#include "mongo/db/index/index_descriptor.h" +#include "mongo/db/index_legacy.h" +#include "mongo/db/query/plan_cache.h" +#include "mongo/db/query/planner_ixselect.h" +#include "mongo/db/service_context.h" +#include "mongo/db/ttl_collection_cache.h" +#include "mongo/stdx/memory.h" +#include "mongo/util/clock_source.h" +#include "mongo/util/debug_util.h" +#include "mongo/util/log.h" + +namespace mongo { +namespace { +MONGO_INITIALIZER(InitializeCollectionInfoCacheFactory)(InitializerContext* const) { + CollectionInfoCache::registerFactory( + [](Collection* const collection, const NamespaceString& ns) { + return stdx::make_unique<CollectionInfoCacheImpl>(collection, ns); + }); + return Status::OK(); +} +} // namespace + +CollectionInfoCacheImpl::CollectionInfoCacheImpl(Collection* collection, const NamespaceString& ns) + : _collection(collection), + _ns(ns), + _keysComputed(false), + _planCache(stdx::make_unique<PlanCache>(ns.ns())), + _querySettings(stdx::make_unique<QuerySettings>()), + _indexUsageTracker(getGlobalServiceContext()->getPreciseClockSource()) {} + +CollectionInfoCacheImpl::~CollectionInfoCacheImpl() { + // Necessary because the collection cache will not explicitly get updated upon database drop. + if (_hasTTLIndex) { + TTLCollectionCache& ttlCollectionCache = TTLCollectionCache::get(getGlobalServiceContext()); + ttlCollectionCache.unregisterCollection(_ns); + } +} + +const UpdateIndexData& CollectionInfoCacheImpl::getIndexKeys(OperationContext* opCtx) const { + // This requires "some" lock, and MODE_IS is an expression for that, for now. + dassert(opCtx->lockState()->isCollectionLockedForMode(_collection->ns().ns(), MODE_IS)); + invariant(_keysComputed); + return _indexedPaths; +} + +void CollectionInfoCacheImpl::computeIndexKeys(OperationContext* opCtx) { + _indexedPaths.clear(); + + bool hadTTLIndex = _hasTTLIndex; + _hasTTLIndex = false; + + IndexCatalog::IndexIterator i = _collection->getIndexCatalog()->getIndexIterator(opCtx, true); + while (i.more()) { + IndexDescriptor* descriptor = i.next(); + + if (descriptor->getAccessMethodName() != IndexNames::TEXT) { + BSONObj key = descriptor->keyPattern(); + const BSONObj& infoObj = descriptor->infoObj(); + if (infoObj.hasField("expireAfterSeconds")) { + _hasTTLIndex = true; + } + BSONObjIterator j(key); + while (j.more()) { + BSONElement e = j.next(); + _indexedPaths.addPath(e.fieldName()); + } + } else { + fts::FTSSpec ftsSpec(descriptor->infoObj()); + + if (ftsSpec.wildcard()) { + _indexedPaths.allPathsIndexed(); + } else { + for (size_t i = 0; i < ftsSpec.numExtraBefore(); ++i) { + _indexedPaths.addPath(ftsSpec.extraBefore(i)); + } + for (fts::Weights::const_iterator it = ftsSpec.weights().begin(); + it != ftsSpec.weights().end(); + ++it) { + _indexedPaths.addPath(it->first); + } + for (size_t i = 0; i < ftsSpec.numExtraAfter(); ++i) { + _indexedPaths.addPath(ftsSpec.extraAfter(i)); + } + // Any update to a path containing "language" as a component could change the + // language of a subdocument. Add the override field as a path component. + _indexedPaths.addPathComponent(ftsSpec.languageOverrideField()); + } + } + + // handle partial indexes + const IndexCatalogEntry* entry = i.catalogEntry(descriptor); + const MatchExpression* filter = entry->getFilterExpression(); + if (filter) { + unordered_set<std::string> paths; + QueryPlannerIXSelect::getFields(filter, "", &paths); + for (auto it = paths.begin(); it != paths.end(); ++it) { + _indexedPaths.addPath(*it); + } + } + } + + TTLCollectionCache& ttlCollectionCache = TTLCollectionCache::get(getGlobalServiceContext()); + + if (_hasTTLIndex != hadTTLIndex) { + if (_hasTTLIndex) { + ttlCollectionCache.registerCollection(_collection->ns()); + } else { + ttlCollectionCache.unregisterCollection(_collection->ns()); + } + } + + _keysComputed = true; +} + +void CollectionInfoCacheImpl::notifyOfQuery(OperationContext* opCtx, + const std::set<std::string>& indexesUsed) { + // Record indexes used to fulfill query. + for (auto it = indexesUsed.begin(); it != indexesUsed.end(); ++it) { + // This index should still exist, since the PlanExecutor would have been killed if the + // index was dropped (and we would not get here). + dassert(NULL != _collection->getIndexCatalog()->findIndexByName(opCtx, *it)); + + _indexUsageTracker.recordIndexAccess(*it); + } +} + +void CollectionInfoCacheImpl::clearQueryCache() { + LOG(1) << _collection->ns().ns() << ": clearing plan cache - collection info cache reset"; + if (NULL != _planCache.get()) { + _planCache->clear(); + } +} + +PlanCache* CollectionInfoCacheImpl::getPlanCache() const { + return _planCache.get(); +} + +QuerySettings* CollectionInfoCacheImpl::getQuerySettings() const { + return _querySettings.get(); +} + +void CollectionInfoCacheImpl::updatePlanCacheIndexEntries(OperationContext* opCtx) { + std::vector<IndexEntry> indexEntries; + + // TODO We shouldn't need to include unfinished indexes, but we must here because the index + // catalog may be in an inconsistent state. SERVER-18346. + const bool includeUnfinishedIndexes = true; + IndexCatalog::IndexIterator ii = + _collection->getIndexCatalog()->getIndexIterator(opCtx, includeUnfinishedIndexes); + while (ii.more()) { + const IndexDescriptor* desc = ii.next(); + const IndexCatalogEntry* ice = ii.catalogEntry(desc); + indexEntries.emplace_back(desc->keyPattern(), + desc->getAccessMethodName(), + desc->isMultikey(opCtx), + ice->getMultikeyPaths(opCtx), + desc->isSparse(), + desc->unique(), + desc->indexName(), + ice->getFilterExpression(), + desc->infoObj(), + ice->getCollator()); + } + + _planCache->notifyOfIndexEntries(indexEntries); +} + +void CollectionInfoCacheImpl::init(OperationContext* opCtx) { + // Requires exclusive collection lock. + invariant(opCtx->lockState()->isCollectionLockedForMode(_collection->ns().ns(), MODE_X)); + + const bool includeUnfinishedIndexes = false; + IndexCatalog::IndexIterator ii = + _collection->getIndexCatalog()->getIndexIterator(opCtx, includeUnfinishedIndexes); + while (ii.more()) { + const IndexDescriptor* desc = ii.next(); + _indexUsageTracker.registerIndex(desc->indexName(), desc->keyPattern()); + } + + rebuildIndexData(opCtx); +} + +void CollectionInfoCacheImpl::addedIndex(OperationContext* opCtx, const IndexDescriptor* desc) { + // Requires exclusive collection lock. + invariant(opCtx->lockState()->isCollectionLockedForMode(_collection->ns().ns(), MODE_X)); + invariant(desc); + + rebuildIndexData(opCtx); + + _indexUsageTracker.registerIndex(desc->indexName(), desc->keyPattern()); +} + +void CollectionInfoCacheImpl::droppedIndex(OperationContext* opCtx, StringData indexName) { + // Requires exclusive collection lock. + invariant(opCtx->lockState()->isCollectionLockedForMode(_collection->ns().ns(), MODE_X)); + + rebuildIndexData(opCtx); + _indexUsageTracker.unregisterIndex(indexName); +} + +void CollectionInfoCacheImpl::rebuildIndexData(OperationContext* opCtx) { + clearQueryCache(); + + _keysComputed = false; + computeIndexKeys(opCtx); + updatePlanCacheIndexEntries(opCtx); +} + +CollectionIndexUsageMap CollectionInfoCacheImpl::getIndexUsageStats() const { + return _indexUsageTracker.getUsageStats(); +} +} // namespace mongo |