diff options
-rw-r--r-- | jstests/noPassthrough/ttl_monitor_does_not_unregister_index_during_collection_creation.js | 63 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_catalog.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_catalog.h | 6 | ||||
-rw-r--r-- | src/mongo/db/ttl.cpp | 19 | ||||
-rw-r--r-- | src/mongo/db/ttl_collection_cache.cpp | 17 |
5 files changed, 102 insertions, 9 deletions
diff --git a/jstests/noPassthrough/ttl_monitor_does_not_unregister_index_during_collection_creation.js b/jstests/noPassthrough/ttl_monitor_does_not_unregister_index_during_collection_creation.js new file mode 100644 index 00000000000..fd1f45902bf --- /dev/null +++ b/jstests/noPassthrough/ttl_monitor_does_not_unregister_index_during_collection_creation.js @@ -0,0 +1,63 @@ +/** + * Ensures that the TTLMonitor does not remove the cached index information from the + * TTLCollectionCache object for a newly created index before the implicitly created collection is + * registered and visible in the CollectionCatalog. + * Removing this cached index information prevents the TTLMonitor from removing expired documents + * for that collection. + */ +(function() { +'use strict'; + +const conn = MongoRunner.runMongod({setParameter: 'ttlMonitorSleepSecs=1'}); + +const dbName = "test"; +const collName = "ttlMonitor"; + +const db = conn.getDB(dbName); +const coll = db.getCollection(collName); + +TestData.dbName = dbName; +TestData.collName = collName; + +coll.drop(); + +const failPoint = "hangTTLCollectionCacheAfterRegisteringInfo"; +assert.commandWorked(db.adminCommand({configureFailPoint: failPoint, mode: "alwaysOn"})); + +// Create an index on a non-existent collection. This will implicitly create the collection. +let awaitEnsureIndex = startParallelShell(() => { + const testDB = db.getSiblingDB(TestData.dbName); + assert.commandWorked( + testDB.getCollection(TestData.collName).ensureIndex({x: 1}, {expireAfterSeconds: 0})); +}, db.getMongo().port); + +// Wait for the TTL monitor to run and register the index in the TTL collection cache. +checkLog.containsJson(db.getMongo(), 4664000); + +// Let the TTL monitor run once. It should not remove the index from the cached TTL information +// until the collection is committed. +let ttlPass = assert.commandWorked(db.serverStatus()).metrics.ttl.passes; +assert.soon(function() { + return coll.getDB().serverStatus().metrics.ttl.passes >= ttlPass + 1; +}, "TTL monitor didn't run."); + +// Finish the index build. +assert.commandWorked(db.adminCommand({configureFailPoint: failPoint, mode: "off"})); +awaitEnsureIndex(); + +// Insert documents, which should expire immediately and be removed on the next TTL pass. +const now = new Date(); +for (let i = 0; i < 10; i++) { + assert.commandWorked(coll.insert({x: now})); +} + +// Let the TTL monitor run once to remove the expired documents. +ttlPass = assert.commandWorked(db.serverStatus()).metrics.ttl.passes; +assert.soon(function() { + return coll.getDB().serverStatus().metrics.ttl.passes >= ttlPass + 1; +}, "TTL monitor didn't run."); + +assert.eq(0, coll.find({}).count()); + +MongoRunner.stopMongod(conn); +}()); diff --git a/src/mongo/db/catalog/collection_catalog.cpp b/src/mongo/db/catalog/collection_catalog.cpp index ec80653054a..bdb5018016f 100644 --- a/src/mongo/db/catalog/collection_catalog.cpp +++ b/src/mongo/db/catalog/collection_catalog.cpp @@ -268,6 +268,12 @@ void CollectionCatalog::makeCollectionVisible(CollectionUUID uuid) { coll->setCommitted(true); } +bool CollectionCatalog::isCollectionAwaitingVisibility(CollectionUUID uuid) const { + stdx::lock_guard<Latch> lock(_catalogLock); + auto coll = _lookupCollectionByUUID(lock, uuid); + return coll && !coll->isCommitted(); +} + Collection* CollectionCatalog::_lookupCollectionByUUID(WithLock, CollectionUUID uuid) const { auto foundIt = _catalog.find(uuid); return foundIt == _catalog.end() ? nullptr : foundIt->second.get(); diff --git a/src/mongo/db/catalog/collection_catalog.h b/src/mongo/db/catalog/collection_catalog.h index d9ddac71fb1..23d0bf88b25 100644 --- a/src/mongo/db/catalog/collection_catalog.h +++ b/src/mongo/db/catalog/collection_catalog.h @@ -146,6 +146,12 @@ public: void makeCollectionVisible(CollectionUUID uuid); /** + * Returns true if the collection has been registered in the CollectionCatalog but not yet made + * visible. + */ + bool isCollectionAwaitingVisibility(CollectionUUID uuid) const; + + /** * This function gets the Collection pointer that corresponds to the NamespaceString. * The required locks must be obtained prior to calling this function, or else the found * Collection pointer may no longer be valid when the call returns. diff --git a/src/mongo/db/ttl.cpp b/src/mongo/db/ttl.cpp index 3dc621bb092..24d8a58bd89 100644 --- a/src/mongo/db/ttl.cpp +++ b/src/mongo/db/ttl.cpp @@ -79,8 +79,6 @@ public: return "TTLMonitor"; } - static std::string secondsExpireField; - virtual void run() { ThreadClient tc(name(), _serviceContext); AuthorizationSession::get(cc())->grantInternalAuthorization(&cc()); @@ -147,7 +145,15 @@ private: auto uuid = ttlInfo.first; auto indexName = ttlInfo.second; - auto nss = CollectionCatalog::get(opCtxPtr.get()).lookupNSSByUUID(&opCtx, uuid); + // Skip collections that have not been made visible yet. The TTLCollectionCache already + // has the index information available, so we want to avoid removing it until the + // collection is visible. + const CollectionCatalog& collectionCatalog = CollectionCatalog::get(opCtxPtr.get()); + if (collectionCatalog.isCollectionAwaitingVisibility(uuid)) { + continue; + } + + auto nss = collectionCatalog.lookupNSSByUUID(&opCtx, uuid); if (!nss) { ttlCollectionCache.deregisterTTLInfo(ttlInfo); continue; @@ -168,7 +174,7 @@ private: BSONObj spec = DurableCatalog::get(opCtxPtr.get()) ->getIndexSpec(&opCtx, coll->getCatalogId(), indexName); - if (!spec.hasField(secondsExpireField)) { + if (!spec.hasField(IndexDescriptor::kExpireAfterSecondsFieldName)) { ttlCollectionCache.deregisterTTLInfo(ttlInfo); continue; } @@ -271,13 +277,13 @@ private: return; } - BSONElement secondsExpireElt = idx[secondsExpireField]; + BSONElement secondsExpireElt = idx[IndexDescriptor::kExpireAfterSecondsFieldName]; if (!secondsExpireElt.isNumber()) { LOGV2_ERROR( 22542, "ttl indexes require the {secondsExpireField} field to be numeric but received a " "type of {typeName_secondsExpireElt_type}, skipping ttl job for: {idx}", - "secondsExpireField"_attr = secondsExpireField, + "secondsExpireField"_attr = IndexDescriptor::kExpireAfterSecondsFieldName, "typeName_secondsExpireElt_type"_attr = typeName(secondsExpireElt.type()), "idx"_attr = idx); return; @@ -349,5 +355,4 @@ void startTTLBackgroundJob(ServiceContext* serviceContext) { ttlMonitor->go(); } -std::string TTLMonitor::secondsExpireField = "expireAfterSeconds"; } // namespace mongo diff --git a/src/mongo/db/ttl_collection_cache.cpp b/src/mongo/db/ttl_collection_cache.cpp index d4d9ffe7694..e959c5096c3 100644 --- a/src/mongo/db/ttl_collection_cache.cpp +++ b/src/mongo/db/ttl_collection_cache.cpp @@ -27,6 +27,8 @@ * it in the license file. */ +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage + #include "mongo/platform/basic.h" #include "mongo/db/ttl_collection_cache.h" @@ -34,9 +36,13 @@ #include <algorithm> #include "mongo/db/service_context.h" +#include "mongo/logv2/log.h" +#include "mongo/util/fail_point.h" namespace mongo { +MONGO_FAIL_POINT_DEFINE(hangTTLCollectionCacheAfterRegisteringInfo); + namespace { const auto getTTLCollectionCache = ServiceContext::declareDecoration<TTLCollectionCache>(); } @@ -46,8 +52,15 @@ TTLCollectionCache& TTLCollectionCache::get(ServiceContext* ctx) { } void TTLCollectionCache::registerTTLInfo(std::pair<UUID, std::string>&& ttlInfo) { - stdx::lock_guard<Latch> lock(_ttlInfosLock); - _ttlInfos.push_back(std::move(ttlInfo)); + { + stdx::lock_guard<Latch> lock(_ttlInfosLock); + _ttlInfos.push_back(std::move(ttlInfo)); + } + + if (MONGO_unlikely(hangTTLCollectionCacheAfterRegisteringInfo.shouldFail())) { + LOGV2(4664000, "Hanging due to hangTTLCollectionCacheAfterRegisteringInfo fail point"); + hangTTLCollectionCacheAfterRegisteringInfo.pauseWhileSet(); + } } void TTLCollectionCache::deregisterTTLInfo(const std::pair<UUID, std::string>& ttlInfo) { |