diff options
author | Kevin Albertson <kevin.albertson@10gen.com> | 2016-07-05 11:15:18 -0400 |
---|---|---|
committer | Kevin Albertson <kevin.albertson@10gen.com> | 2016-07-19 13:57:58 -0400 |
commit | d059552b998bd9f3ff0275016dff2df89a137b02 (patch) | |
tree | ce58b37687b6f99b6b66740dcedc3325997b9bae /src/mongo | |
parent | e9201e5c46124472b941a1932dc8827a36487e5e (diff) | |
download | mongo-d059552b998bd9f3ff0275016dff2df89a137b02.tar.gz |
SERVER-24631: Add TTL collection namespace cache
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/SConscript | 12 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_info_cache.cpp | 25 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_info_cache.h | 4 | ||||
-rw-r--r-- | src/mongo/db/ttl.cpp | 136 | ||||
-rw-r--r-- | src/mongo/db/ttl_collection_cache.cpp | 63 | ||||
-rw-r--r-- | src/mongo/db/ttl_collection_cache.h | 56 |
6 files changed, 205 insertions, 91 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 86575fed86e..d9c68f389db 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -652,6 +652,7 @@ serveronlyLibdeps = [ "$BUILD_DIR/mongo/util/net/network", "$BUILD_DIR/mongo/db/storage/mmap_v1/file_allocator", "$BUILD_DIR/third_party/shim_snappy", + '$BUILD_DIR/mongo/db/ttl_collection_cache', "auth/authmongod", "catalog/catalog", "catalog/collection_options", @@ -760,3 +761,14 @@ env.Library( '$BUILD_DIR/mongo/s/client/sharding_client', ], ) + +env.Library( + target='ttl_collection_cache', + source=[ + 'ttl_collection_cache.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/service_context', + ], +) diff --git a/src/mongo/db/catalog/collection_info_cache.cpp b/src/mongo/db/catalog/collection_info_cache.cpp index fe2a15499dc..d5431bdb0eb 100644 --- a/src/mongo/db/catalog/collection_info_cache.cpp +++ b/src/mongo/db/catalog/collection_info_cache.cpp @@ -42,6 +42,7 @@ #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" @@ -55,6 +56,13 @@ CollectionInfoCache::CollectionInfoCache(Collection* collection) _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(_collection->ns()); + } +} const UpdateIndexData& CollectionInfoCache::getIndexKeys(OperationContext* txn) const { // This requires "some" lock, and MODE_IS is an expression for that, for now. @@ -66,12 +74,19 @@ const UpdateIndexData& CollectionInfoCache::getIndexKeys(OperationContext* txn) void CollectionInfoCache::computeIndexKeys(OperationContext* txn) { _indexedPaths.clear(); + bool hadTTLIndex = _hasTTLIndex; + _hasTTLIndex = false; + IndexCatalog::IndexIterator i = _collection->getIndexCatalog()->getIndexIterator(txn, 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(); @@ -112,6 +127,16 @@ void CollectionInfoCache::computeIndexKeys(OperationContext* txn) { } } + TTLCollectionCache& ttlCollectionCache = TTLCollectionCache::get(getGlobalServiceContext()); + + if (_hasTTLIndex != hadTTLIndex) { + if (_hasTTLIndex) { + ttlCollectionCache.registerCollection(_collection->ns()); + } else { + ttlCollectionCache.unregisterCollection(_collection->ns()); + } + } + _keysComputed = true; } diff --git a/src/mongo/db/catalog/collection_info_cache.h b/src/mongo/db/catalog/collection_info_cache.h index 4c5b67ed880..ea832633cd3 100644 --- a/src/mongo/db/catalog/collection_info_cache.h +++ b/src/mongo/db/catalog/collection_info_cache.h @@ -49,6 +49,8 @@ class CollectionInfoCache { public: CollectionInfoCache(Collection* collection); + ~CollectionInfoCache(); + /** * Get the PlanCache for this collection. */ @@ -130,6 +132,8 @@ private: * when index composition changes. */ void rebuildIndexData(OperationContext* txn); + + bool _hasTTLIndex = false; }; } // namespace mongo diff --git a/src/mongo/db/ttl.cpp b/src/mongo/db/ttl.cpp index 49a767e3b2d..862f59eefea 100644 --- a/src/mongo/db/ttl.cpp +++ b/src/mongo/db/ttl.cpp @@ -54,6 +54,7 @@ #include "mongo/db/query/internal_plans.h" #include "mongo/db/repl/replication_coordinator_global.h" #include "mongo/db/server_parameters.h" +#include "mongo/db/ttl_collection_cache.h" #include "mongo/util/background.h" #include "mongo/util/exit.h" #include "mongo/util/log.h" @@ -129,128 +130,83 @@ private: !repl::getGlobalReplicationCoordinator()->getMemberState().readable()) return; - set<string> dbs; - dbHolder().getAllShortNames(dbs); + TTLCollectionCache& ttlCollectionCache = TTLCollectionCache::get(getGlobalServiceContext()); + std::vector<std::string> ttlCollections = ttlCollectionCache.getCollections(); + std::vector<BSONObj> ttlIndexes; ttlPasses.increment(); - for (set<string>::const_iterator i = dbs.begin(); i != dbs.end(); ++i) { - string db = *i; - - vector<BSONObj> indexes; - getTTLIndexesForDB(&txn, db, &indexes); - - for (vector<BSONObj>::const_iterator it = indexes.begin(); it != indexes.end(); ++it) { - BSONObj idx = *it; - try { - if (!doTTLForIndex(&txn, db, idx)) { - break; // stop processing TTL indexes on this database - } - } catch (const DBException& dbex) { - error() << "Error processing ttl index: " << idx << " -- " << dbex.toString(); - // continue on to the next index - continue; - } - } - } - } - - /** - * Acquire an IS-mode lock on the specified database and for each - * collection in the database, append the specification of all - * TTL indexes on those collections to the supplied vector. - * - * The index specifications are grouped by the collection to which - * they belong. - */ - void getTTLIndexesForDB(OperationContext* txn, const string& dbName, vector<BSONObj>* indexes) { - invariant(indexes && indexes->empty()); - ScopedTransaction transaction(txn, MODE_IS); - Lock::DBLock dbLock(txn->lockState(), dbName, MODE_IS); - - Database* db = dbHolder().get(txn, dbName); - if (!db) { - return; // skip since database no longer exists - } - - const DatabaseCatalogEntry* dbEntry = db->getDatabaseCatalogEntry(); - - list<string> namespaces; - dbEntry->getCollectionNamespaces(&namespaces); - - for (list<string>::const_iterator it = namespaces.begin(); it != namespaces.end(); ++it) { - string ns = *it; - Lock::CollectionLock collLock(txn->lockState(), ns, MODE_IS); - CollectionCatalogEntry* coll = dbEntry->getCollectionCatalogEntry(ns); - + // Get all TTL indexes from every collection. + for (const std::string& collectionNS : ttlCollections) { + NamespaceString collectionNSS(collectionNS); + AutoGetCollection autoGetCollection(&txn, collectionNSS, MODE_IS); + Collection* coll = autoGetCollection.getCollection(); if (!coll) { - continue; // skip since collection not found in catalog + // Skip since collection has been dropped. + continue; } - vector<string> indexNames; - coll->getAllIndexes(txn, &indexNames); - for (size_t i = 0; i < indexNames.size(); i++) { - const string& name = indexNames[i]; - BSONObj spec = coll->getIndexSpec(txn, name); - + CollectionCatalogEntry* collEntry = coll->getCatalogEntry(); + std::vector<std::string> indexNames; + collEntry->getAllIndexes(&txn, &indexNames); + for (const std::string& name : indexNames) { + BSONObj spec = collEntry->getIndexSpec(&txn, name); if (spec.hasField(secondsExpireField)) { - indexes->push_back(spec.getOwned()); + ttlIndexes.push_back(spec.getOwned()); } } } + + for (const BSONObj& idx : ttlIndexes) { + try { + doTTLForIndex(&txn, idx); + } catch (const DBException& dbex) { + error() << "Error processing ttl index: " << idx << " -- " << dbex.toString(); + // Continue on to the next index. + continue; + } + } } /** * Remove documents from the collection using the specified TTL index * after a sufficient amount of time has passed according to its expiry * specification. - * - * @return true if caller should continue processing TTL indexes of collections - * on the specified database, and false otherwise */ - bool doTTLForIndex(OperationContext* txn, const string& dbName, BSONObj idx) { - const string ns = idx["ns"].String(); - NamespaceString nss(ns); - if (!userAllowedWriteNS(nss).isOK()) { - error() << "namespace '" << ns + void doTTLForIndex(OperationContext* txn, BSONObj idx) { + const NamespaceString collectionNSS(idx["ns"].String()); + if (!userAllowedWriteNS(collectionNSS).isOK()) { + error() << "namespace '" << collectionNSS << "' doesn't allow deletes, skipping ttl job for: " << idx; - return true; + return; } - BSONObj key = idx["key"].Obj(); + const BSONObj key = idx["key"].Obj(); if (key.nFields() != 1) { error() << "key for ttl index can only have 1 field, skipping ttl job for: " << idx; - return true; - } - - LOG(1) << "TTL -- ns: " << ns << " key: " << key; - - ScopedTransaction scopedXact(txn, MODE_IX); - AutoGetDb autoDb(txn, dbName, MODE_IX); - Database* db = autoDb.getDb(); - if (!db) { - return false; + return; } - Lock::CollectionLock collLock(txn->lockState(), ns, MODE_IX); + LOG(1) << "TTL -- ns: " << collectionNSS << " key: " << key; - Collection* collection = db->getCollection(ns); + AutoGetCollection autoGetCollection(txn, collectionNSS, MODE_IX); + Collection* collection = autoGetCollection.getCollection(); if (!collection) { // Collection was dropped. - return true; + return; } - if (!repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(nss)) { + if (!repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(collectionNSS)) { // We've stepped down since we started this function, so we should stop working // as we only do deletes on the primary. - return false; + return; } IndexDescriptor* desc = collection->getIndexCatalog()->findIndexByKeyPattern(txn, key); if (!desc) { LOG(1) << "index not found (index build in progress? index dropped?), skipping " << "ttl job for: " << idx; - return true; + return; } // Re-read 'idx' from the descriptor, in case the collection or index definition @@ -259,7 +215,7 @@ private: if (IndexType::INDEX_BTREE != IndexNames::nameToType(desc->getAccessMethodName())) { error() << "special index can't be used as a ttl index, skipping ttl job for: " << idx; - return true; + return; } BSONElement secondsExpireElt = idx[secondsExpireField]; @@ -267,7 +223,7 @@ private: error() << "ttl indexes require the " << secondsExpireField << " field to be " << "numeric but received a type of " << typeName(secondsExpireElt.type()) << ", skipping ttl job for: " << idx; - return true; + return; } const Date_t kDawnOfTime = @@ -288,7 +244,7 @@ private: const char* keyFieldName = key.firstElement().fieldName(); BSONObj query = BSON(keyFieldName << BSON("$gte" << kDawnOfTime << "$lte" << expirationTime)); - auto qr = stdx::make_unique<QueryRequest>(nss); + auto qr = stdx::make_unique<QueryRequest>(collectionNSS); qr->setFilter(query); auto canonicalQuery = CanonicalQuery::canonicalize( txn, std::move(qr), ExtensionsCallbackDisallowExtensions()); @@ -312,14 +268,12 @@ private: Status result = exec->executePlan(); if (!result.isOK()) { error() << "ttl query execution for index " << idx << " failed with status: " << result; - return true; + return; } const long long numDeleted = DeleteStage::getNumDeleted(*exec); ttlDeletedDocuments.increment(numDeleted); LOG(1) << "\tTTL deleted: " << numDeleted << endl; - - return true; } }; diff --git a/src/mongo/db/ttl_collection_cache.cpp b/src/mongo/db/ttl_collection_cache.cpp new file mode 100644 index 00000000000..c97f249bb10 --- /dev/null +++ b/src/mongo/db/ttl_collection_cache.cpp @@ -0,0 +1,63 @@ +/** + * Copyright (C) 2016 MongoDB 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/db/ttl_collection_cache.h" + +#include <algorithm> + +#include "mongo/db/service_context.h" + +namespace mongo { + +namespace { +const auto getTTLCollectionCache = ServiceContext::declareDecoration<TTLCollectionCache>(); +} + +TTLCollectionCache& TTLCollectionCache::get(ServiceContext* ctx) { + return getTTLCollectionCache(ctx); +} + +void TTLCollectionCache::registerCollection(const NamespaceString& collectionNS) { + stdx::lock_guard<stdx::mutex> lock(_ttlCollectionsLock); + _ttlCollections.push_back(collectionNS.ns()); +} + +void TTLCollectionCache::unregisterCollection(const NamespaceString& collectionNS) { + stdx::lock_guard<stdx::mutex> lock(_ttlCollectionsLock); + auto collIter = std::find(_ttlCollections.begin(), _ttlCollections.end(), collectionNS.ns()); + fassert(40220, collIter != _ttlCollections.end()); + _ttlCollections.erase(collIter); +} + +std::vector<std::string> TTLCollectionCache::getCollections() { + stdx::lock_guard<stdx::mutex> lock(_ttlCollectionsLock); + return _ttlCollections; +} +}; // namespace mongo diff --git a/src/mongo/db/ttl_collection_cache.h b/src/mongo/db/ttl_collection_cache.h new file mode 100644 index 00000000000..a909947b069 --- /dev/null +++ b/src/mongo/db/ttl_collection_cache.h @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2016 MongoDB 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 <string> +#include <vector> + +#include "mongo/db/namespace_string.h" +#include "mongo/db/service_context.h" +#include "mongo/stdx/mutex.h" + +/** + * Caches the set of collections containing a TTL index. + * This class is thread safe. + */ +namespace mongo { + +class TTLCollectionCache { +public: + static TTLCollectionCache& get(ServiceContext* ctx); + // Caller is responsible for ensuring no duplicates are registered. + void registerCollection(const NamespaceString& collectionNS); + void unregisterCollection(const NamespaceString& collectionNS); + std::vector<std::string> getCollections(); + +private: + std::vector<std::string> _ttlCollections; + stdx::mutex _ttlCollectionsLock; +}; +} // namespace mongo |