summaryrefslogtreecommitdiff
path: root/src
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-08-26 13:06:53 +0000
commitcc3ae631bce7943fbda5182ff3b9d93d1125be40 (patch)
treeff8dd8dc8b6f0926d54c2b551c9d3fa99c27198f /src
parentacf81f84dabf486f74a84b99f04cd17efa35424f (diff)
downloadmongo-cc3ae631bce7943fbda5182ff3b9d93d1125be40.tar.gz
SERVER-68477 include 'expireAfterSeconds' type information when registering TTL indexes with the TTLCollectionCache
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/catalog/coll_mod.cpp3
-rw-r--r--src/mongo/db/catalog/coll_mod_index.cpp29
-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.cpp30
-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
9 files changed, 158 insertions, 41 deletions
diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp
index 6e8196aef0f..51f1b960220 100644
--- a/src/mongo/db/catalog/coll_mod.cpp
+++ b/src/mongo/db/catalog/coll_mod.cpp
@@ -593,7 +593,8 @@ void _setClusteredExpireAfterSeconds(
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{}});
});
}
diff --git a/src/mongo/db/catalog/coll_mod_index.cpp b/src/mongo/db/catalog/coll_mod_index.cpp
index 432c4dff255..a7f0097052d 100644
--- a/src/mongo/db/catalog/coll_mod_index.cpp
+++ b/src/mongo/db/catalog/coll_mod_index.cpp
@@ -69,8 +69,10 @@ void _processCollModIndexRequestExpireAfterSeconds(OperationContext* opCtx,
// Do not refer to 'idx' within this commit handler as it may be be invalidated by
// IndexCatalog::refreshEntry().
opCtx->recoveryUnit()->onCommit(
- [ttlCache, uuid = coll->uuid(), indexName = idx->indexName()](auto _) {
- ttlCache->registerTTLInfo(uuid, indexName);
+ [ttlCache, uuid = coll->uuid(), indexName = idx->indexName(), indexExpireAfterSeconds](
+ auto _) {
+ ttlCache->registerTTLInfo(
+ uuid, TTLCollectionCache::Info{indexName, /*isExpireAfterSecondsNaN=*/false});
});
// Change the value of "expireAfterSeconds" on disk.
@@ -79,6 +81,29 @@ void _processCollModIndexRequestExpireAfterSeconds(OperationContext* opCtx,
return;
}
+ // If the current `expireAfterSeconds` is NaN, it can never be equal to
+ // 'indexExpireAfterSeconds'.
+ if (oldExpireSecsElement.isNaN()) {
+ // Setting *oldExpireSecs is mostly for informational purposes.
+ // We could also use index_key_validate::kExpireAfterSecondsForInactiveTTLIndex but
+ // 0 is more consistent with the previous safeNumberLong() behavior and avoids potential
+ // showing the same value for the new and old values in the collMod response.
+ *oldExpireSecs = 0;
+
+ // Change the value of "expireAfterSeconds" on disk.
+ autoColl->getWritableCollection(opCtx)->updateTTLSetting(
+ opCtx, idx->indexName(), indexExpireAfterSeconds);
+
+ // 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());
+ const auto& coll = autoColl->getCollection();
+ opCtx->recoveryUnit()->onCommit(
+ [ttlCache, uuid = coll->uuid(), indexName = idx->indexName(), indexExpireAfterSeconds](
+ auto _) { ttlCache->unsetTTLIndexExpireAfterSecondsNaN(uuid, indexName); });
+ return;
+ }
+
// This collection is already TTL. Compare the requested value against the existing setting
// before updating the catalog.
*oldExpireSecs = oldExpireSecsElement.safeNumberLong();
diff --git a/src/mongo/db/catalog/collection_impl.cpp b/src/mongo/db/catalog/collection_impl.cpp
index 57ac56ebc4b..1f37bb989a0 100644
--- a/src/mongo/db/catalog/collection_impl.cpp
+++ b/src/mongo/db/catalog/collection_impl.cpp
@@ -409,11 +409,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 feaa50eacd4..e1b628d8842 100644
--- a/src/mongo/db/catalog/index_build_block.cpp
+++ b/src/mongo/db/catalog/index_build_block.cpp
@@ -278,7 +278,10 @@ void IndexBuildBlock::success(OperationContext* opCtx, Collection* collection) {
// Note that TTL deletion is supported on capped clustered collections via bounded
// collection scan, which does not use an index.
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 52dd1d224a0..402b7c82969 100644
--- a/src/mongo/db/catalog/index_catalog_impl.cpp
+++ b/src/mongo/db/catalog/index_catalog_impl.cpp
@@ -211,7 +211,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 ab31744bd54..010703fb93d 100644
--- a/src/mongo/db/ttl.cpp
+++ b/src/mongo/db/ttl.cpp
@@ -143,7 +143,7 @@ const IndexDescriptor* getValidTTLIndex(OperationContext* opCtx,
const BSONObj& spec,
std::string indexName) {
if (!spec.hasField(IndexDescriptor::kExpireAfterSecondsFieldName)) {
- ttlCollectionCache->deregisterTTLInfo(collection->uuid(), indexName);
+ ttlCollectionCache->deregisterTTLIndexByName(collection->uuid(), indexName);
return nullptr;
}
@@ -354,7 +354,11 @@ bool TTLMonitor::_doTTLIndexDelete(OperationContext* opCtx,
// 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());
+ }
return false;
}
@@ -407,17 +411,12 @@ bool TTLMonitor::_doTTLIndexDelete(OperationContext* opCtx,
ResourceConsumption::ScopedMetricsCollector scopedMetrics(opCtx, nss->db().toString());
const auto& collection = coll.getCollection();
- return stdx::visit(OverloadedVisitor{[&](const TTLCollectionCache::ClusteredId&) {
- return _deleteExpiredWithCollscan(
- opCtx, ttlCollectionCache, collection);
- },
- [&](const TTLCollectionCache::IndexName& indexName) {
- return _deleteExpiredWithIndex(opCtx,
- ttlCollectionCache,
- collection,
- indexName);
- }},
- info);
+ if (info.isClustered()) {
+ return _deleteExpiredWithCollscan(opCtx, ttlCollectionCache, collection);
+ } else {
+ return _deleteExpiredWithIndex(
+ opCtx, ttlCollectionCache, collection, info.getIndexName());
+ }
} catch (const ExceptionForCat<ErrorCategory::Interruption>&) {
// The exception is relevant to the entire TTL monitoring process, not just the specific TTL
// index. Let the exception escape so it can be addressed at the higher monitoring layer.
@@ -468,7 +467,7 @@ bool TTLMonitor::_deleteExpiredWithIndex(OperationContext* opCtx,
const CollectionPtr& collection,
std::string indexName) {
if (!collection->isIndexPresent(indexName)) {
- ttlCollectionCache->deregisterTTLInfo(collection->uuid(), indexName);
+ ttlCollectionCache->deregisterTTLIndexByName(collection->uuid(), indexName);
return false;
}
@@ -572,8 +571,7 @@ bool TTLMonitor::_deleteExpiredWithCollscan(OperationContext* opCtx,
auto expireAfterSeconds = collOptions.expireAfterSeconds;
if (!expireAfterSeconds) {
- ttlCollectionCache->deregisterTTLInfo(collection->uuid(),
- TTLCollectionCache::ClusteredId{});
+ ttlCollectionCache->deregisterTTLClusteredIndex(collection->uuid());
return false;
}
diff --git a/src/mongo/db/ttl_collection_cache.cpp b/src/mongo/db/ttl_collection_cache.cpp
index 7343afddfea..43d55a46301 100644
--- a/src/mongo/db/ttl_collection_cache.cpp
+++ b/src/mongo/db/ttl_collection_cache.cpp
@@ -65,13 +65,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()) {
@@ -79,6 +96,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 8c70ce9f8d8..a8f00b6c6a6 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);
}