summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenety Goh <benety@mongodb.com>2022-08-26 08:08:11 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-10-10 20:47:25 +0000
commitba31c2d4c78fe0f0261b0b82c259e511dce9447c (patch)
treefa302c9500d28a00638af27db6590e5202dca3b4
parentfbb8779057e34e3a696134f2ff3960f5328c579a (diff)
downloadmongo-ba31c2d4c78fe0f0261b0b82c259e511dce9447c.tar.gz
SERVER-68477 include 'expireAfterSeconds' type information when registering TTL indexes with the TTLCollectionCache
(cherry picked from commit cc3ae631bce7943fbda5182ff3b9d93d1125be40)
-rw-r--r--src/mongo/db/catalog/coll_mod.cpp40
-rw-r--r--src/mongo/db/catalog/collection_impl.cpp6
-rw-r--r--src/mongo/db/catalog/index_build_block.cpp5
-rw-r--r--src/mongo/db/catalog/index_catalog_impl.cpp5
-rw-r--r--src/mongo/db/ttl.cpp27
-rw-r--r--src/mongo/db/ttl_collection_cache.cpp46
-rw-r--r--src/mongo/db/ttl_collection_cache.h40
-rw-r--r--src/mongo/db/ttl_collection_cache_test.cpp35
8 files changed, 162 insertions, 42 deletions
diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp
index f2ba54f7999..a2b4505a04f 100644
--- a/src/mongo/db/catalog/coll_mod.cpp
+++ b/src/mongo/db/catalog/coll_mod.cpp
@@ -430,7 +430,8 @@ void _setClusteredExpireAfterSeconds(OperationContext* opCtx,
if (!oldExpireAfterSeconds) {
auto ttlCache = &TTLCollectionCache::get(opCtx->getServiceContext());
opCtx->recoveryUnit()->onCommit([ttlCache, uuid = coll->uuid()](auto _) {
- ttlCache->registerTTLInfo(uuid, TTLCollectionCache::ClusteredId());
+ ttlCache->registerTTLInfo(uuid,
+ TTLCollectionCache::Info{TTLCollectionCache::ClusteredId{}});
});
}
@@ -577,12 +578,39 @@ Status _collModInternal(OperationContext* opCtx,
// If this collection was not previously TTL, inform the TTL monitor when we commit.
if (oldExpireSecs.eoo()) {
auto ttlCache = &TTLCollectionCache::get(opCtx->getServiceContext());
- opCtx->recoveryUnit()->onCommit([ttlCache, uuid = coll->uuid(), &idx](auto _) {
- ttlCache->registerTTLInfo(uuid, idx->indexName());
- });
+ opCtx->recoveryUnit()->onCommit(
+ [ttlCache, uuid = coll->uuid(), indexName = idx->indexName()](auto _) {
+ ttlCache->registerTTLInfo(
+ uuid,
+ TTLCollectionCache::Info{indexName,
+ /*isExpireAfterSecondsNaN=*/false});
+ });
}
- if (SimpleBSONElementComparator::kInstance.evaluate(oldExpireSecs !=
- newExpireSecs)) {
+
+ // If the current `expireAfterSeconds` is NaN, it can never be equal to
+ // 'indexExpireAfterSeconds'.
+ if (oldExpireSecs.isNaN()) {
+ // Setting *oldExpireSecs is mostly for informational purposes.
+ // In 5.0, we leave `oldExpireSecs` unchanged.
+ // Unlike 6.0+ where 'oldExpireSecs' is declared as an optional long long,
+ // 'oldExpireSecs' is a BSONElement. It should be fine to keep `oldExpireSecs`
+ // as NaN in the command result. For the oplog entry, we already use
+ // BSONElement::safeNumberLong() to coerce the NaN into zero. See the
+ // IndexCollModInfo struct.
+
+ // Change the value of "expireAfterSeconds" on disk.
+ coll.getWritableCollection()->updateTTLSetting(
+ opCtx, idx->indexName(), newExpireSecs.safeNumberLong());
+
+ // Keep the TTL information maintained by the TTLCollectionCache in sync so that
+ // we don't try to fix up the TTL index during the next step-up.
+ auto ttlCache = &TTLCollectionCache::get(opCtx->getServiceContext());
+ opCtx->recoveryUnit()->onCommit(
+ [ttlCache, uuid = coll->uuid(), indexName = idx->indexName()](auto _) {
+ ttlCache->unsetTTLIndexExpireAfterSecondsNaN(uuid, indexName);
+ });
+ } else if (SimpleBSONElementComparator::kInstance.evaluate(oldExpireSecs !=
+ newExpireSecs)) {
// Change the value of "expireAfterSeconds" on disk.
coll.getWritableCollection()->updateTTLSetting(
opCtx, idx->indexName(), newExpireSecs.safeNumberLong());
diff --git a/src/mongo/db/catalog/collection_impl.cpp b/src/mongo/db/catalog/collection_impl.cpp
index 01e0282d150..f99cb4019b7 100644
--- a/src/mongo/db/catalog/collection_impl.cpp
+++ b/src/mongo/db/catalog/collection_impl.cpp
@@ -426,11 +426,11 @@ void CollectionImpl::init(OperationContext* opCtx) {
if (opCtx->lockState()->inAWriteUnitOfWork()) {
opCtx->recoveryUnit()->onCommit([svcCtx, uuid](auto ts) {
TTLCollectionCache::get(svcCtx).registerTTLInfo(
- uuid, TTLCollectionCache::ClusteredId{});
+ uuid, TTLCollectionCache::Info{TTLCollectionCache::ClusteredId{}});
});
} else {
- TTLCollectionCache::get(svcCtx).registerTTLInfo(uuid,
- TTLCollectionCache::ClusteredId{});
+ TTLCollectionCache::get(svcCtx).registerTTLInfo(
+ uuid, TTLCollectionCache::Info{TTLCollectionCache::ClusteredId{}});
}
}
}
diff --git a/src/mongo/db/catalog/index_build_block.cpp b/src/mongo/db/catalog/index_build_block.cpp
index ac29eccbc29..d5b410ee03b 100644
--- a/src/mongo/db/catalog/index_build_block.cpp
+++ b/src/mongo/db/catalog/index_build_block.cpp
@@ -268,7 +268,10 @@ void IndexBuildBlock::success(OperationContext* opCtx, Collection* collection) {
// Add the index to the TTLCollectionCache upon successfully committing the index build.
// TTL indexes are not compatible with capped collections.
if (spec.hasField(IndexDescriptor::kExpireAfterSecondsFieldName) && !coll->isCapped()) {
- TTLCollectionCache::get(svcCtx).registerTTLInfo(coll->uuid(), indexName);
+ TTLCollectionCache::get(svcCtx).registerTTLInfo(
+ coll->uuid(),
+ TTLCollectionCache::Info{
+ indexName, spec[IndexDescriptor::kExpireAfterSecondsFieldName].isNaN()});
}
});
}
diff --git a/src/mongo/db/catalog/index_catalog_impl.cpp b/src/mongo/db/catalog/index_catalog_impl.cpp
index 3fccb483ca3..0879f90b354 100644
--- a/src/mongo/db/catalog/index_catalog_impl.cpp
+++ b/src/mongo/db/catalog/index_catalog_impl.cpp
@@ -159,7 +159,10 @@ Status IndexCatalogImpl::init(OperationContext* opCtx, Collection* collection) {
if (spec.hasField(IndexDescriptor::kExpireAfterSecondsFieldName) &&
!collection->isCapped()) {
TTLCollectionCache::get(opCtx->getServiceContext())
- .registerTTLInfo(collection->uuid(), indexName);
+ .registerTTLInfo(
+ collection->uuid(),
+ TTLCollectionCache::Info{
+ indexName, spec[IndexDescriptor::kExpireAfterSecondsFieldName].isNaN()});
}
bool ready = collection->isIndexReady(indexName);
diff --git a/src/mongo/db/ttl.cpp b/src/mongo/db/ttl.cpp
index 24d321b7a5f..241c9b5002e 100644
--- a/src/mongo/db/ttl.cpp
+++ b/src/mongo/db/ttl.cpp
@@ -205,7 +205,11 @@ private:
// The collection was dropped.
auto nss = collectionCatalog->lookupNSSByUUID(opCtx, uuid);
if (!nss) {
- ttlCollectionCache.deregisterTTLInfo(uuid, info);
+ if (info.isClustered()) {
+ ttlCollectionCache.deregisterTTLClusteredIndex(uuid);
+ } else {
+ ttlCollectionCache.deregisterTTLIndexByName(uuid, info.getIndexName());
+ }
continue;
}
@@ -285,15 +289,11 @@ private:
ResourceConsumption::ScopedMetricsCollector scopedMetrics(opCtx, nss.db().toString());
const auto& collection = coll.getCollection();
- stdx::visit(
- visit_helper::Overloaded{
- [&](const TTLCollectionCache::ClusteredId&) {
- deleteExpiredWithCollscan(opCtx, ttlCollectionCache, collection);
- },
- [&](const TTLCollectionCache::IndexName& indexName) {
- deleteExpiredWithIndex(opCtx, ttlCollectionCache, collection, indexName);
- }},
- info);
+ if (info.isClustered()) {
+ deleteExpiredWithCollscan(opCtx, ttlCollectionCache, collection);
+ } else {
+ deleteExpiredWithIndex(opCtx, ttlCollectionCache, collection, info.getIndexName());
+ }
}
/**
@@ -326,13 +326,13 @@ private:
const CollectionPtr& collection,
std::string indexName) {
if (!collection->isIndexPresent(indexName)) {
- ttlCollectionCache->deregisterTTLInfo(collection->uuid(), indexName);
+ ttlCollectionCache->deregisterTTLIndexByName(collection->uuid(), indexName);
return;
}
BSONObj spec = collection->getIndexSpec(indexName);
if (!spec.hasField(IndexDescriptor::kExpireAfterSecondsFieldName)) {
- ttlCollectionCache->deregisterTTLInfo(collection->uuid(), indexName);
+ ttlCollectionCache->deregisterTTLIndexByName(collection->uuid(), indexName);
return;
}
@@ -455,8 +455,7 @@ private:
auto expireAfterSeconds = collOptions.expireAfterSeconds;
if (!expireAfterSeconds) {
- ttlCollectionCache->deregisterTTLInfo(collection->uuid(),
- TTLCollectionCache::ClusteredId{});
+ ttlCollectionCache->deregisterTTLClusteredIndex(collection->uuid());
return;
}
diff --git a/src/mongo/db/ttl_collection_cache.cpp b/src/mongo/db/ttl_collection_cache.cpp
index 27d1335510c..86fcc682068 100644
--- a/src/mongo/db/ttl_collection_cache.cpp
+++ b/src/mongo/db/ttl_collection_cache.cpp
@@ -63,13 +63,30 @@ void TTLCollectionCache::registerTTLInfo(UUID uuid, const Info& info) {
}
}
-void TTLCollectionCache::deregisterTTLInfo(UUID uuid, const Info& info) {
+void TTLCollectionCache::_deregisterTTLInfo(UUID uuid, const Info& info) {
stdx::lock_guard<Latch> lock(_ttlInfosLock);
auto infoIt = _ttlInfos.find(uuid);
fassert(5400705, infoIt != _ttlInfos.end());
auto& [_, infoVec] = *infoIt;
- auto iter = std::find(infoVec.begin(), infoVec.end(), info);
+ auto iter = infoVec.begin();
+ if (info.isClustered()) {
+ // For clustered collections, we cannot have more than one clustered info per UUID.
+ // All we have to do here is ensure that the 'info' to search for is also 'clustered'.
+ iter = std::find_if(infoVec.begin(), infoVec.end(), [](const auto& infoVecItem) {
+ return infoVecItem.isClustered();
+ });
+ } else {
+ // For TTL indexes, we search non-clustered TTL info items on the index name only.
+ auto indexName = info.getIndexName();
+ iter = std::find_if(infoVec.begin(), infoVec.end(), [&indexName](const auto& infoVecItem) {
+ if (infoVecItem.isClustered()) {
+ return false;
+ }
+ return indexName == infoVecItem.getIndexName();
+ });
+ }
+
fassert(40220, iter != infoVec.end());
infoVec.erase(iter);
if (infoVec.empty()) {
@@ -77,6 +94,31 @@ void TTLCollectionCache::deregisterTTLInfo(UUID uuid, const Info& info) {
}
}
+void TTLCollectionCache::deregisterTTLIndexByName(UUID uuid, const IndexName& indexName) {
+ _deregisterTTLInfo(std::move(uuid), TTLCollectionCache::Info{indexName, /*unusedSpec=*/{}});
+}
+
+void TTLCollectionCache::deregisterTTLClusteredIndex(UUID uuid) {
+ _deregisterTTLInfo(std::move(uuid),
+ TTLCollectionCache::Info{TTLCollectionCache::ClusteredId{}});
+}
+
+void TTLCollectionCache::unsetTTLIndexExpireAfterSecondsNaN(UUID uuid, const IndexName& indexName) {
+ stdx::lock_guard<Latch> lock(_ttlInfosLock);
+ auto infoIt = _ttlInfos.find(uuid);
+ if (infoIt == _ttlInfos.end()) {
+ return;
+ }
+
+ auto&& infoVec = infoIt->second;
+ for (auto&& info : infoVec) {
+ if (!info.isClustered() && info.getIndexName() == indexName) {
+ info.unsetExpireAfterSecondsNaN();
+ break;
+ }
+ }
+}
+
TTLCollectionCache::InfoMap TTLCollectionCache::getTTLInfos() {
stdx::lock_guard<Latch> lock(_ttlInfosLock);
return _ttlInfos;
diff --git a/src/mongo/db/ttl_collection_cache.h b/src/mongo/db/ttl_collection_cache.h
index 053b9fc48a2..b343c6f627d 100644
--- a/src/mongo/db/ttl_collection_cache.h
+++ b/src/mongo/db/ttl_collection_cache.h
@@ -54,16 +54,52 @@ public:
using IndexName = std::string;
// Specifies how a collection should expire data with TTL.
- using Info = stdx::variant<ClusteredId, IndexName>;
+ class Info {
+ public:
+ explicit Info(ClusteredId) : _isClustered(true), _isExpireAfterSecondsNaN(false) {}
+ Info(IndexName indexName, bool isExpireAfterSecondsNaN)
+ : _isClustered(false),
+ _indexName(std::move(indexName)),
+ _isExpireAfterSecondsNaN(isExpireAfterSecondsNaN) {}
+ bool isClustered() const {
+ return _isClustered;
+ }
+ IndexName getIndexName() const {
+ return _indexName;
+ }
+ bool isExpireAfterSecondsNaN() const {
+ return _isExpireAfterSecondsNaN;
+ }
+ void unsetExpireAfterSecondsNaN() {
+ _isExpireAfterSecondsNaN = false;
+ }
+
+ private:
+ bool _isClustered;
+ IndexName _indexName;
+ bool _isExpireAfterSecondsNaN;
+ };
// Caller is responsible for ensuring no duplicates are registered.
void registerTTLInfo(UUID uuid, const Info& info);
- void deregisterTTLInfo(UUID uuid, const Info& info);
+ void deregisterTTLIndexByName(UUID uuid, const IndexName& indexName);
+ void deregisterTTLClusteredIndex(UUID uuid);
+
+ /**
+ * Resets expireAfterSeconds flag on TTL index.
+ * For idempotency, this has no effect if index is not found.
+ */
+ void unsetTTLIndexExpireAfterSecondsNaN(UUID uuid, const IndexName& indexName);
using InfoMap = stdx::unordered_map<UUID, std::vector<Info>, UUID::Hash>;
InfoMap getTTLInfos();
private:
+ /**
+ * Shared implementation for deregistering TTL infos.
+ */
+ void _deregisterTTLInfo(UUID uuid, const Info& info);
+
Mutex _ttlInfosLock = MONGO_MAKE_LATCH("TTLCollectionCache::_ttlInfosLock");
InfoMap _ttlInfos;
};
diff --git a/src/mongo/db/ttl_collection_cache_test.cpp b/src/mongo/db/ttl_collection_cache_test.cpp
index 4f598b3736c..747d1371887 100644
--- a/src/mongo/db/ttl_collection_cache_test.cpp
+++ b/src/mongo/db/ttl_collection_cache_test.cpp
@@ -39,9 +39,9 @@ TEST(TTLCollectionCacheTest, Basic) {
auto uuidCollA = UUID::gen();
auto uuidCollB = UUID::gen();
- auto infoIndexA1 = TTLCollectionCache::Info{"collA_ttl_1"};
+ auto infoIndexA1 = TTLCollectionCache::Info{"collA_ttl_1", /*isExpireAfterSecondsNaN=*/false};
auto infoClusteredA = TTLCollectionCache::Info{TTLCollectionCache::ClusteredId{}};
- auto infoIndexB1 = TTLCollectionCache::Info("collB_ttl_1");
+ auto infoIndexB1 = TTLCollectionCache::Info("collB_ttl_1", /*isExpireAfterSecondsNaN=*/true);
// Confirm registerTTLInfo() behavior using getTTLInfo().
cache.registerTTLInfo(uuidCollA, infoIndexA1);
@@ -52,36 +52,45 @@ TEST(TTLCollectionCacheTest, Basic) {
ASSERT_EQ(infos.size(), 2U);
ASSERT_EQ(infos.count(uuidCollA), 1U);
ASSERT_EQ(infos[uuidCollA].size(), 2U);
- auto indexNameA = stdx::get_if<TTLCollectionCache::IndexName>(&infos[uuidCollA][0]);
- ASSERT(indexNameA);
- ASSERT_EQ(*indexNameA, "collA_ttl_1");
- ASSERT(stdx::get_if<TTLCollectionCache::ClusteredId>(&infos[uuidCollA][1]));
+ ASSERT_FALSE(infos[uuidCollA][0].isClustered());
+ ASSERT_EQ(infos[uuidCollA][0].getIndexName(), "collA_ttl_1");
+ ASSERT_FALSE(infos[uuidCollA][0].isExpireAfterSecondsNaN());
+ ASSERT(infos[uuidCollA][1].isClustered());
ASSERT_EQ(infos.count(uuidCollB), 1U);
ASSERT_EQ(infos[uuidCollB].size(), 1U);
- auto indexNameB = stdx::get_if<TTLCollectionCache::IndexName>(&infos[uuidCollB][0]);
- ASSERT(indexNameB);
- ASSERT_EQ(*indexNameB, "collB_ttl_1");
+ ASSERT_FALSE(infos[uuidCollB][0].isClustered());
+ ASSERT_EQ(infos[uuidCollB][0].getIndexName(), "collB_ttl_1");
+ ASSERT(infos[uuidCollB][0].isExpireAfterSecondsNaN());
+
+ // Check that we can reset '_isExpireAfterSecondsNaN()' on the TTL index.
+ cache.unsetTTLIndexExpireAfterSecondsNaN(uuidCollB, infoIndexB1.getIndexName());
+ infos = cache.getTTLInfos();
+ ASSERT_EQ(infos.size(), 2U);
+ ASSERT_EQ(infos.count(uuidCollB), 1U);
+ ASSERT_EQ(infos[uuidCollB].size(), 1U);
+ ASSERT_EQ(infos[uuidCollB][0].getIndexName(), "collB_ttl_1");
+ ASSERT_FALSE(infos[uuidCollB][0].isExpireAfterSecondsNaN());
// Check deregisterTTLInfo(). TTLCollectionCache should clean up
// UUIDs that no longer have any TTL infos registered.
- cache.deregisterTTLInfo(uuidCollB, infoIndexB1);
+ cache.deregisterTTLIndexByName(uuidCollB, infoIndexB1.getIndexName());
infos = cache.getTTLInfos();
ASSERT_EQ(infos.size(), 1U);
ASSERT_EQ(infos.count(uuidCollA), 1U);
ASSERT_EQ(infos[uuidCollA].size(), 2U);
// Remove info for TTL index on collection A.
- cache.deregisterTTLInfo(uuidCollA, infoIndexA1);
+ cache.deregisterTTLIndexByName(uuidCollA, infoIndexA1.getIndexName());
infos = cache.getTTLInfos();
ASSERT_EQ(infos.size(), 1U);
ASSERT_EQ(infos.count(uuidCollA), 1U);
ASSERT_EQ(infos[uuidCollA].size(), 1U);
- ASSERT(stdx::get_if<TTLCollectionCache::ClusteredId>(&infos[uuidCollA][0]));
+ ASSERT(infos[uuidCollA][0].isClustered());
// Remove clustered info for collection A.
- cache.deregisterTTLInfo(uuidCollA, infoClusteredA);
+ cache.deregisterTTLClusteredIndex(uuidCollA);
infos = cache.getTTLInfos();
ASSERT_EQ(infos.size(), 0U);
}