summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorKevin Albertson <kevin.albertson@10gen.com>2016-07-05 11:15:18 -0400
committerKevin Albertson <kevin.albertson@10gen.com>2016-07-19 13:57:58 -0400
commitd059552b998bd9f3ff0275016dff2df89a137b02 (patch)
treece58b37687b6f99b6b66740dcedc3325997b9bae /src/mongo
parente9201e5c46124472b941a1932dc8827a36487e5e (diff)
downloadmongo-d059552b998bd9f3ff0275016dff2df89a137b02.tar.gz
SERVER-24631: Add TTL collection namespace cache
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/SConscript12
-rw-r--r--src/mongo/db/catalog/collection_info_cache.cpp25
-rw-r--r--src/mongo/db/catalog/collection_info_cache.h4
-rw-r--r--src/mongo/db/ttl.cpp136
-rw-r--r--src/mongo/db/ttl_collection_cache.cpp63
-rw-r--r--src/mongo/db/ttl_collection_cache.h56
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