Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/mongo/db/ttl/ttl_collection_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,17 @@ TTLCollectionCache& TTLCollectionCache::get(ServiceContext* ctx) {
}

void TTLCollectionCache::registerTTLInfo(UUID uuid, const Info& info) {
{
std::lock_guard<std::mutex> lock(_ttlInfosLock);
_ttlInfos[uuid].push_back(info);
}
std::lock_guard<std::mutex> lock(_ttlInfosLock);
_deregisterTTLInfo_inlock(uuid, info);
_ttlInfos[uuid].push_back(info);
}

void TTLCollectionCache::_deregisterTTLInfo(UUID uuid, const Info& info) {
std::lock_guard<std::mutex> lock(_ttlInfosLock);
_deregisterTTLInfo_inlock(uuid, info);
}

void TTLCollectionCache::_deregisterTTLInfo_inlock(UUID uuid, const Info& info) {
auto infoIt = _ttlInfos.find(uuid);
if (infoIt == _ttlInfos.end()) {
LOGV2_DEBUG(9150100,
Expand Down
2 changes: 1 addition & 1 deletion src/mongo/db/ttl/ttl_collection_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ class TTLCollectionCache {
ExpireAfterSecondsType _expireAfterSecondsType;
};

// Caller is responsible for ensuring no duplicates are registered.
void registerTTLInfo(UUID uuid, const Info& info);
void deregisterTTLIndexByName(UUID uuid, const IndexName& indexName);
void deregisterTTLClusteredIndex(UUID uuid);
Expand All @@ -109,6 +108,7 @@ class TTLCollectionCache {
* Shared implementation for deregistering TTL infos.
*/
void _deregisterTTLInfo(UUID uuid, const Info& info);
void _deregisterTTLInfo_inlock(UUID uuid, const Info& info);

std::mutex _ttlInfosLock;
InfoMap _ttlInfos;
Expand Down
68 changes: 68 additions & 0 deletions src/mongo/db/ttl/ttl_collection_cache_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,74 @@ TEST(TTLCollectionCacheTest, Basic) {
EXPECT_EQ(infos.size(), 0U);
}

// Test that registering the same TTL index more than once does not create duplicate infos.
// This guards against SERVER-129760, where repeatedly creating and dropping the same TTL index
// caused the TTL job to run multiple times within a single cycle.
TEST(TTLCollectionCacheTest, RegisterSameIndexTwiceDoesNotDuplicate) {
TTLCollectionCache cache;
EXPECT_EQ(cache.getTTLInfos().size(), 0);

auto uuid = UUID::gen();
auto infoInvalid = TTLCollectionCache::Info{
"collA_ttl_1", TTLCollectionCache::Info::ExpireAfterSecondsType::kInvalid};
auto infoInt = TTLCollectionCache::Info{
"collA_ttl_1", TTLCollectionCache::Info::ExpireAfterSecondsType::kInt};

cache.registerTTLInfo(uuid, infoInvalid);
// Re-registering the same index name replaces the existing info rather than duplicating it.
cache.registerTTLInfo(uuid, infoInt);

auto infos = cache.getTTLInfos();
EXPECT_EQ(infos.size(), 1U);
ASSERT_TRUE(infos.contains(uuid));
ASSERT_EQ(infos[uuid].size(), 1U);
EXPECT_EQ(infos[uuid][0].getIndexName(), "collA_ttl_1");
// The most recent registration's expireAfterSecondsType wins.
EXPECT_FALSE(infos[uuid][0].isExpireAfterSecondsInvalid());
}

// Test that registering a clustered TTL info more than once does not create duplicate infos.
TEST(TTLCollectionCacheTest, RegisterSameClusteredIndexTwiceDoesNotDuplicate) {
TTLCollectionCache cache;
EXPECT_EQ(cache.getTTLInfos().size(), 0);

auto uuid = UUID::gen();
auto infoClustered = TTLCollectionCache::Info{TTLCollectionCache::ClusteredId{}};

cache.registerTTLInfo(uuid, infoClustered);
cache.registerTTLInfo(uuid, infoClustered);

auto infos = cache.getTTLInfos();
EXPECT_EQ(infos.size(), 1U);
ASSERT_TRUE(infos.contains(uuid));
ASSERT_EQ(infos[uuid].size(), 1U);
EXPECT_TRUE(infos[uuid][0].isClustered());
}

// Test that registering distinct TTL indexes on the same UUID keeps all of them.
TEST(TTLCollectionCacheTest, RegisterDistinctIndexesKeepsAll) {
TTLCollectionCache cache;
EXPECT_EQ(cache.getTTLInfos().size(), 0);

auto uuid = UUID::gen();
auto infoIndex1 = TTLCollectionCache::Info{
"collA_ttl_1", TTLCollectionCache::Info::ExpireAfterSecondsType::kInt};
auto infoIndex2 = TTLCollectionCache::Info{
"collA_ttl_2", TTLCollectionCache::Info::ExpireAfterSecondsType::kInt};
auto infoClustered = TTLCollectionCache::Info{TTLCollectionCache::ClusteredId{}};

cache.registerTTLInfo(uuid, infoIndex1);
cache.registerTTLInfo(uuid, infoIndex2);
cache.registerTTLInfo(uuid, infoClustered);
// Re-registering an existing index should not affect the others.
cache.registerTTLInfo(uuid, infoIndex1);

auto infos = cache.getTTLInfos();
EXPECT_EQ(infos.size(), 1U);
ASSERT_TRUE(infos.contains(uuid));
ASSERT_EQ(infos[uuid].size(), 3U);
}

TEST(TTLCollectionCacheTest, DeregisterUntrackedUUIDDoesNotThrow) {
TTLCollectionCache cache;
EXPECT_EQ(cache.getTTLInfos().size(), 0);
Expand Down