summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorADAM David Alan Martin <adam.martin@10gen.com>2017-04-20 15:58:20 -0400
committerADAM David Alan Martin <adam.martin@10gen.com>2017-04-20 15:59:53 -0400
commit7e6025227bfbf41d6ac1ad90ef985dcb7afe668f (patch)
tree39ddf930efdffc0ea1b289eeaa8b81b91da015bd
parent47b98a643bc1844cf7a3cc9362748f125efa875c (diff)
downloadmongo-7e6025227bfbf41d6ac1ad90ef985dcb7afe668f.tar.gz
SERVER-28832 Slice `catalog/collection_info_cache`
The `CollectionInfoCache` class is used outside of the catalog library in many places. This change slices that dependency by providing an initializable vtable based pimpl class. This should permit a further reduction in the number of cyclic dependencies in the graph.
-rw-r--r--src/mongo/db/catalog/SConscript11
-rw-r--r--src/mongo/db/catalog/collection_info_cache.cpp261
-rw-r--r--src/mongo/db/catalog/collection_info_cache.h193
-rw-r--r--src/mongo/db/catalog/collection_info_cache_impl.cpp250
-rw-r--r--src/mongo/db/catalog/collection_info_cache_impl.h140
5 files changed, 557 insertions, 298 deletions
diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript
index f0c598de768..dd2e2ddebd7 100644
--- a/src/mongo/db/catalog/SConscript
+++ b/src/mongo/db/catalog/SConscript
@@ -92,6 +92,14 @@ env.Library(
LIBDEPS=[
],
)
+env.Library(
+ target='collection_info_cache',
+ source=[
+ 'collection_info_cache.cpp',
+ ],
+ LIBDEPS=[
+ ],
+)
env.Library(
target='catalog',
@@ -101,7 +109,7 @@ env.Library(
"coll_mod.cpp",
"collection_compact.cpp",
"collection_impl.cpp",
- "collection_info_cache.cpp",
+ "collection_info_cache_impl.cpp",
"create_collection.cpp",
"cursor_manager.cpp",
"database_impl.cpp",
@@ -116,6 +124,7 @@ env.Library(
],
LIBDEPS=[
'collection',
+ 'collection_info_cache',
'collection_options',
'database',
'index_catalog',
diff --git a/src/mongo/db/catalog/collection_info_cache.cpp b/src/mongo/db/catalog/collection_info_cache.cpp
index 41ce9048ff5..4bd1e4715f7 100644
--- a/src/mongo/db/catalog/collection_info_cache.cpp
+++ b/src/mongo/db/catalog/collection_info_cache.cpp
@@ -1,30 +1,30 @@
/**
-* Copyright (C) 2013 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.
-*/
+ * 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
@@ -32,208 +32,21 @@
#include "mongo/db/catalog/collection_info_cache.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/util/clock_source.h"
-#include "mongo/util/debug_util.h"
-#include "mongo/util/log.h"
-
namespace mongo {
+namespace {
+stdx::function<CollectionInfoCache::factory_function_type> factory;
+} // namespace
-CollectionInfoCache::CollectionInfoCache(Collection* collection, const NamespaceString& ns)
- : _collection(collection),
- _ns(ns),
- _keysComputed(false),
- _planCache(new PlanCache(ns.ns())),
- _querySettings(new QuerySettings()),
- _indexUsageTracker(getGlobalServiceContext()->getPreciseClockSource()) {}
-
-CollectionInfoCache::~CollectionInfoCache() {
- // 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& CollectionInfoCache::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 CollectionInfoCache::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 CollectionInfoCache::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 CollectionInfoCache::clearQueryCache() {
- LOG(1) << _collection->ns().ns() << ": clearing plan cache - collection info cache reset";
- if (NULL != _planCache.get()) {
- _planCache->clear();
- }
-}
-
-PlanCache* CollectionInfoCache::getPlanCache() const {
- return _planCache.get();
-}
-
-QuerySettings* CollectionInfoCache::getQuerySettings() const {
- return _querySettings.get();
-}
-
-void CollectionInfoCache::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 CollectionInfoCache::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());
- }
+CollectionInfoCache::Impl::~Impl() = default;
- rebuildIndexData(opCtx);
+void CollectionInfoCache::registerFactory(decltype(factory) newFactory) {
+ factory = std::move(newFactory);
}
-void CollectionInfoCache::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 CollectionInfoCache::droppedIndex(OperationContext* opCtx, StringData indexName) {
- // Requires exclusive collection lock.
- invariant(opCtx->lockState()->isCollectionLockedForMode(_collection->ns().ns(), MODE_X));
-
- rebuildIndexData(opCtx);
- _indexUsageTracker.unregisterIndex(indexName);
+auto CollectionInfoCache::makeImpl(Collection* const collection, const NamespaceString& ns)
+ -> std::unique_ptr<Impl> {
+ return factory(collection, ns);
}
-void CollectionInfoCache::rebuildIndexData(OperationContext* opCtx) {
- clearQueryCache();
-
- _keysComputed = false;
- computeIndexKeys(opCtx);
- updatePlanCacheIndexEntries(opCtx);
-}
-
-CollectionIndexUsageMap CollectionInfoCache::getIndexUsageStats() const {
- return _indexUsageTracker.getUsageStats();
-}
-}
+void CollectionInfoCache::TUHook::hook() noexcept {}
+} // namespace mongo
diff --git a/src/mongo/db/catalog/collection_info_cache.h b/src/mongo/db/catalog/collection_info_cache.h
index b198cdf6eda..9bc07e80c61 100644
--- a/src/mongo/db/catalog/collection_info_cache.h
+++ b/src/mongo/db/catalog/collection_info_cache.h
@@ -1,30 +1,30 @@
/**
-* Copyright (C) 2013 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.
-*/
+ * 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.
+ */
#pragma once
@@ -32,37 +32,83 @@
#include "mongo/db/query/plan_cache.h"
#include "mongo/db/query/query_settings.h"
#include "mongo/db/update_index_data.h"
+#include "mongo/stdx/functional.h"
namespace mongo {
-
class Collection;
class IndexDescriptor;
class OperationContext;
/**
* this is for storing things that you want to cache about a single collection
- * life cycle is managed for you from inside Collection
+ * life cycle is managed for you from inside Collection.
*/
class CollectionInfoCache {
public:
- explicit CollectionInfoCache(Collection* collection, const NamespaceString& ns);
+ class Impl {
+ public:
+ virtual ~Impl() = 0;
+
+ virtual PlanCache* getPlanCache() const = 0;
+
+ virtual QuerySettings* getQuerySettings() const = 0;
+
+ virtual const UpdateIndexData& getIndexKeys(OperationContext* opCtx) const = 0;
+
+ virtual CollectionIndexUsageMap getIndexUsageStats() const = 0;
+
+ virtual void init(OperationContext* opCtx) = 0;
+
+ virtual void addedIndex(OperationContext* opCtx, const IndexDescriptor* desc) = 0;
+
+ virtual void droppedIndex(OperationContext* opCtx, StringData indexName) = 0;
+
+ virtual void clearQueryCache() = 0;
+
+ virtual void notifyOfQuery(OperationContext* opCtx,
+ const std::set<std::string>& indexesUsed) = 0;
+ };
+
+private:
+ static std::unique_ptr<Impl> makeImpl(Collection* collection, const NamespaceString& ns);
+
+public:
+ using factory_function_type = decltype(makeImpl);
+
+ static void registerFactory(stdx::function<factory_function_type> factory);
- ~CollectionInfoCache();
+ explicit inline CollectionInfoCache(Collection* const collection, const NamespaceString& ns)
+ : _pimpl(makeImpl(collection, ns)) {}
+
+ inline ~CollectionInfoCache() = default;
+
+ /**
+ * Builds internal cache state based on the current state of the Collection's IndexCatalog.
+ */
+ inline void init(OperationContext* const opCtx) {
+ return this->_impl().init(opCtx);
+ }
/**
* Get the PlanCache for this collection.
*/
- PlanCache* getPlanCache() const;
+ inline PlanCache* getPlanCache() const {
+ return this->_impl().getPlanCache();
+ }
/**
* Get the QuerySettings for this collection.
*/
- QuerySettings* getQuerySettings() const;
+ inline QuerySettings* getQuerySettings() const {
+ return this->_impl().getQuerySettings();
+ }
/* get set of index keys for this namespace. handy to quickly check if a given
field is indexed (Note it might be a secondary component of a compound index.)
*/
- const UpdateIndexData& getIndexKeys(OperationContext* opCtx) const;
+ inline const UpdateIndexData& getIndexKeys(OperationContext* const opCtx) const {
+ return this->_impl().getIndexKeys(opCtx);
+ }
/**
* Returns cached index usage statistics for this collection. The map returned will contain
@@ -71,12 +117,9 @@ public:
*
* Note for performance that this method returns a copy of a StringMap.
*/
- CollectionIndexUsageMap getIndexUsageStats() const;
-
- /**
- * Builds internal cache state based on the current state of the Collection's IndexCatalog
- */
- void init(OperationContext* opCtx);
+ inline CollectionIndexUsageMap getIndexUsageStats() const {
+ return this->_impl().getIndexUsageStats();
+ }
/**
* Register a newly-created index with the cache. Must be called whenever an index is
@@ -84,7 +127,9 @@ public:
*
* Must be called under exclusive collection lock.
*/
- void addedIndex(OperationContext* opCtx, const IndexDescriptor* desc);
+ inline void addedIndex(OperationContext* const opCtx, const IndexDescriptor* const desc) {
+ return this->_impl().addedIndex(opCtx, desc);
+ }
/**
* Deregister a newly-dropped index with the cache. Must be called whenever an index is
@@ -92,47 +137,49 @@ public:
*
* Must be called under exclusive collection lock.
*/
- void droppedIndex(OperationContext* opCtx, StringData indexName);
+ inline void droppedIndex(OperationContext* const opCtx, const StringData indexName) {
+ return this->_impl().droppedIndex(opCtx, indexName);
+ }
/**
* Removes all cached query plans.
*/
- void clearQueryCache();
+ inline void clearQueryCache() {
+ return this->_impl().clearQueryCache();
+ }
/**
* Signal to the cache that a query operation has completed. 'indexesUsed' should list the
* set of indexes used by the winning plan, if any.
*/
- void notifyOfQuery(OperationContext* opCtx, const std::set<std::string>& indexesUsed);
-
-private:
- void computeIndexKeys(OperationContext* opCtx);
- void updatePlanCacheIndexEntries(OperationContext* opCtx);
-
- /**
- * Rebuilds cached information that is dependent on index composition. Must be called
- * when index composition changes.
- */
- void rebuildIndexData(OperationContext* opCtx);
-
- Collection* _collection; // not owned
- const NamespaceString _ns;
-
- // --- index keys cache
- bool _keysComputed;
- UpdateIndexData _indexedPaths;
-
- // A cache for query plans.
- std::unique_ptr<PlanCache> _planCache;
-
- // Query settings.
- // Includes index filters.
- std::unique_ptr<QuerySettings> _querySettings;
-
- // Tracks index usage statistics for this collection.
- CollectionIndexUsageTracker _indexUsageTracker;
-
- bool _hasTTLIndex = false;
+ inline void notifyOfQuery(OperationContext* const opCtx,
+ const std::set<std::string>& indexesUsed) {
+ return this->_impl().notifyOfQuery(opCtx, indexesUsed);
+ }
+
+ std::unique_ptr<Impl> _pimpl;
+
+ // This structure exists to give us a customization point to decide how to force users of this
+ // class to depend upon the corresponding `collection_info_cache.cpp` Translation Unit (TU).
+ // All public forwarding functions call `_impl(), and `_impl` creates an instance of this
+ // structure.
+ struct TUHook {
+ static void hook() noexcept;
+
+ explicit inline TUHook() noexcept {
+ if (kDebugBuild)
+ this->hook();
+ }
+ };
+
+ inline const Impl& _impl() const {
+ TUHook{};
+ return *this->_pimpl;
+ }
+
+ inline Impl& _impl() {
+ TUHook{};
+ return *this->_pimpl;
+ }
};
-
} // namespace mongo
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
diff --git a/src/mongo/db/catalog/collection_info_cache_impl.h b/src/mongo/db/catalog/collection_info_cache_impl.h
new file mode 100644
index 00000000000..58be19ed045
--- /dev/null
+++ b/src/mongo/db/catalog/collection_info_cache_impl.h
@@ -0,0 +1,140 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include "mongo/db/catalog/collection_info_cache.h"
+
+#include "mongo/db/collection_index_usage_tracker.h"
+#include "mongo/db/query/plan_cache.h"
+#include "mongo/db/query/query_settings.h"
+#include "mongo/db/update_index_data.h"
+
+namespace mongo {
+
+class Collection;
+class IndexDescriptor;
+class OperationContext;
+
+/**
+ * this is for storing things that you want to cache about a single collection
+ * life cycle is managed for you from inside Collection
+ */
+class CollectionInfoCacheImpl : public CollectionInfoCache::Impl {
+public:
+ explicit CollectionInfoCacheImpl(Collection* collection, const NamespaceString& ns);
+
+ ~CollectionInfoCacheImpl();
+
+ /**
+ * Get the PlanCache for this collection.
+ */
+ PlanCache* getPlanCache() const;
+
+ /**
+ * Get the QuerySettings for this collection.
+ */
+ QuerySettings* getQuerySettings() const;
+
+ /* get set of index keys for this namespace. handy to quickly check if a given
+ field is indexed (Note it might be a secondary component of a compound index.)
+ */
+ const UpdateIndexData& getIndexKeys(OperationContext* opCtx) const;
+
+ /**
+ * Returns cached index usage statistics for this collection. The map returned will contain
+ * entry for each index in the collection along with both a usage counter and a timestamp
+ * representing the date/time the counter is valid from.
+ *
+ * Note for performance that this method returns a copy of a StringMap.
+ */
+ CollectionIndexUsageMap getIndexUsageStats() const;
+
+ /**
+ * Builds internal cache state based on the current state of the Collection's IndexCatalog
+ */
+ void init(OperationContext* opCtx);
+
+ /**
+ * Register a newly-created index with the cache. Must be called whenever an index is
+ * built on the associated collection.
+ *
+ * Must be called under exclusive collection lock.
+ */
+ void addedIndex(OperationContext* opCtx, const IndexDescriptor* desc);
+
+ /**
+ * Deregister a newly-dropped index with the cache. Must be called whenever an index is
+ * dropped on the associated collection.
+ *
+ * Must be called under exclusive collection lock.
+ */
+ void droppedIndex(OperationContext* opCtx, StringData indexName);
+
+ /**
+ * Removes all cached query plans.
+ */
+ void clearQueryCache();
+
+ /**
+ * Signal to the cache that a query operation has completed. 'indexesUsed' should list the
+ * set of indexes used by the winning plan, if any.
+ */
+ void notifyOfQuery(OperationContext* opCtx, const std::set<std::string>& indexesUsed);
+
+private:
+ void computeIndexKeys(OperationContext* opCtx);
+ void updatePlanCacheIndexEntries(OperationContext* opCtx);
+
+ /**
+ * Rebuilds cached information that is dependent on index composition. Must be called
+ * when index composition changes.
+ */
+ void rebuildIndexData(OperationContext* opCtx);
+
+ Collection* _collection; // not owned
+ const NamespaceString _ns;
+
+ // --- index keys cache
+ bool _keysComputed;
+ UpdateIndexData _indexedPaths;
+
+ // A cache for query plans.
+ std::unique_ptr<PlanCache> _planCache;
+
+ // Query settings.
+ // Includes index filters.
+ std::unique_ptr<QuerySettings> _querySettings;
+
+ // Tracks index usage statistics for this collection.
+ CollectionIndexUsageTracker _indexUsageTracker;
+
+ bool _hasTTLIndex = false;
+};
+
+} // namespace mongo