summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/noPassthrough/ttl_monitor_does_not_unregister_index_during_collection_creation.js63
-rw-r--r--src/mongo/db/catalog/collection_catalog.cpp6
-rw-r--r--src/mongo/db/catalog/collection_catalog.h6
-rw-r--r--src/mongo/db/ttl.cpp19
-rw-r--r--src/mongo/db/ttl_collection_cache.cpp17
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) {