Repository: apache/kafka
File: storage/src/test/java/org/apache/kafka/storage/internals/log/RemoteIndexCacheTest.java (Java)

Code before:
                .filter(t -> t.isAlive() && t.getName().startsWith(REMOTE_LOG_INDEX_CACHE_CLEANER_THREAD))
                .collect(Collectors.toSet());
    }

    @Test
    public void testCacheTtlCanBeDisabled() throws IOException {
        // Test that TTL can be disabled by setting it to -1
        long ttlMs = -1L;
        FakeTicker fakeTicker = new FakeTicker();
        RemoteIndexCache ttlCache = new RemoteIndexCache(1024 * 1024L, ttlMs, rsm, logDir.toString(), fakeTicker);
        try {
            RemoteIndexCache.Entry entry = ttlCache.getIndexEntry(rlsMetadata);
            assertNotNull(entry);

            fakeTicker.advance(TimeUnit.HOURS.toNanos(24));
            ttlCache.internalCache().cleanUp();

            RemoteIndexCache.Entry cachedEntry = ttlCache.internalCache().getIfPresent(rlsMetadata.remoteLogSegmentId().id());
            assertNotNull(cachedEntry, "Entry should remain cached when TTL disabled");
        } finally {
            Utils.closeQuietly(ttlCache, "RemoteIndexCache");
        }
    }

    @Test
    public void testCacheTtlEviction() throws IOException {
        long ttlMs = TimeUnit.SECONDS.toMillis(10);
        FakeTicker fakeTicker = new FakeTicker();
        RemoteIndexCache ttlCache = new RemoteIndexCache(1024 * 1024L, ttlMs, rsm, logDir.toString(), fakeTicker);
        try {
            RemoteIndexCache.Entry entry = ttlCache.getIndexEntry(rlsMetadata);
            assertNotNull(entry);

            fakeTicker.advance(TimeUnit.MILLISECONDS.toNanos(ttlMs - 1000));
            ttlCache.internalCache().cleanUp();
            assertEquals(1, ttlCache.internalCache().estimatedSize());

            fakeTicker.advance(TimeUnit.MILLISECONDS.toNanos(2000));
            ttlCache.internalCache().cleanUp();
            assertEquals(0, ttlCache.internalCache().estimatedSize());
        } finally {
            Utils.closeQuietly(ttlCache, "RemoteIndexCache");
        }
    }

    @Test
    public void testCacheTtlRefreshOnAccess() throws IOException {
        long ttlMs = TimeUnit.SECONDS.toMillis(10);
        FakeTicker fakeTicker = new FakeTicker();
        RemoteIndexCache ttlCache = new RemoteIndexCache(1024 * 1024L, ttlMs, rsm, logDir.toString(), fakeTicker);
        try {

Review comment (performance):
Nice tests! Thanks for adding them! Could we add one more test to verify that both size-based and time-based eviction strategy work well? Currently we have `testCacheEntryExpiry` for size-based eviction test, and time-based eviction tests here. So, maybe mix them together with scenario like:
1. create a RemoteIndexCache with sizeLimit and ttlMs set.
2. add entries over sizeLimit, and make sure eviction happened
3. ticker advanced over ttlMs, and make sure eviction happened

something like that. Thanks.

Code after:
                .filter(t -> t.isAlive() && t.getName().startsWith(REMOTE_LOG_INDEX_CACHE_CLEANER_THREAD))
                .collect(Collectors.toSet());
    }

    @Test
    public void testCacheTtlCanBeDisabled() throws IOException {
        // Test that TTL can be disabled by setting it to -1
        long ttlMs = -1L;
        FakeTicker fakeTicker = new FakeTicker();
        RemoteIndexCache ttlCache = new RemoteIndexCache(1024 * 1024L, ttlMs, false, rsm, logDir.toString(), fakeTicker);
        try {
            RemoteIndexCache.Entry entry = ttlCache.getIndexEntry(rlsMetadata);
            assertNotNull(entry);

            fakeTicker.advance(TimeUnit.MINUTES.toNanos(15));
            ttlCache.internalCache().cleanUp();

            RemoteIndexCache.Entry cachedEntry = ttlCache.internalCache().getIfPresent(rlsMetadata.remoteLogSegmentId().id());
            assertNotNull(cachedEntry, "Entry should remain cached when TTL disabled");
        } finally {
            Utils.closeQuietly(ttlCache, "RemoteIndexCache");
        }
    }

    @Test
    public void testCacheTtlEviction() throws IOException {
        long ttlMs = TimeUnit.SECONDS.toMillis(10);
        FakeTicker fakeTicker = new FakeTicker();
        RemoteIndexCache ttlCache = new RemoteIndexCache(1024 * 1024L, ttlMs, false, rsm, logDir.toString(), fakeTicker);
        try {
            RemoteIndexCache.Entry entry = ttlCache.getIndexEntry(rlsMetadata);
            assertNotNull(entry);

            fakeTicker.advance(TimeUnit.MILLISECONDS.toNanos(ttlMs - 1000));
            ttlCache.internalCache().cleanUp();
            assertEquals(1, ttlCache.internalCache().estimatedSize());

            fakeTicker.advance(TimeUnit.MILLISECONDS.toNanos(2000));
            ttlCache.internalCache().cleanUp();
            assertEquals(0, ttlCache.internalCache().estimatedSize());
        } finally {
            Utils.closeQuietly(ttlCache, "RemoteIndexCache");
        }
    }

    @Test
    public void testCacheTtlRefreshOnAccess() throws IOException {
        long ttlMs = TimeUnit.SECONDS.toMillis(10);
        FakeTicker fakeTicker = new FakeTicker();
        RemoteIndexCache ttlCache = new RemoteIndexCache(1024 * 1024L, ttlMs, false, rsm, logDir.toString(), fakeTicker);
        try {