This is an automated email from the ASF dual-hosted git repository.
JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new e7db7cf851 [core] Avoid retaining rejected cache pages (#9658)
e7db7cf851 is described below
commit e7db7cf851880c7ccbd4ce8d760ddf9ce6cf66ce
Author: yuzelin <[email protected]>
AuthorDate: Sun Sep 6 16:07:17 2026 +0800
[core] Avoid retaining rejected cache pages (#9658)
---
.../java/org/apache/paimon/io/cache/Cache.java | 3 ++
.../org/apache/paimon/io/cache/CacheManager.java | 8 +++++
.../org/apache/paimon/io/cache/CaffeineCache.java | 5 ++++
.../java/org/apache/paimon/sst/BlockCache.java | 4 ++-
.../apache/paimon/io/cache/CacheManagerTest.java | 35 ++++++++++++++++++++++
5 files changed, 54 insertions(+), 1 deletion(-)
diff --git a/paimon-common/src/main/java/org/apache/paimon/io/cache/Cache.java
b/paimon-common/src/main/java/org/apache/paimon/io/cache/Cache.java
index d1def1fec5..8f96cdfba4 100644
--- a/paimon-common/src/main/java/org/apache/paimon/io/cache/Cache.java
+++ b/paimon-common/src/main/java/org/apache/paimon/io/cache/Cache.java
@@ -27,11 +27,14 @@ import java.util.function.Function;
/** Cache interface in Paimon. */
public interface Cache {
+
@Nullable
CacheValue get(CacheKey key, Function<CacheKey, CacheValue> supplier);
void put(CacheKey key, CacheValue value);
+ boolean contains(CacheKey key);
+
void invalidate(CacheKey key);
void invalidateAll();
diff --git
a/paimon-common/src/main/java/org/apache/paimon/io/cache/CacheManager.java
b/paimon-common/src/main/java/org/apache/paimon/io/cache/CacheManager.java
index e8cd314725..4fe040e1c5 100644
--- a/paimon-common/src/main/java/org/apache/paimon/io/cache/CacheManager.java
+++ b/paimon-common/src/main/java/org/apache/paimon/io/cache/CacheManager.java
@@ -102,6 +102,14 @@ public class CacheManager implements AutoCloseable {
return checkNotNull(value, "Cache result for key(%s) is null",
key).segment;
}
+ public boolean contains(CacheKey key) {
+ if (key.isIndex()) {
+ return indexCache.contains(key);
+ } else {
+ return dataCache.contains(key);
+ }
+ }
+
public void invalidPage(CacheKey key) {
if (key.isIndex()) {
indexCache.invalidate(key);
diff --git
a/paimon-common/src/main/java/org/apache/paimon/io/cache/CaffeineCache.java
b/paimon-common/src/main/java/org/apache/paimon/io/cache/CaffeineCache.java
index 8fda391fcc..3bb47f82ee 100644
--- a/paimon-common/src/main/java/org/apache/paimon/io/cache/CaffeineCache.java
+++ b/paimon-common/src/main/java/org/apache/paimon/io/cache/CaffeineCache.java
@@ -47,6 +47,11 @@ public class CaffeineCache implements Cache {
this.cache.put(key, value);
}
+ @Override
+ public boolean contains(CacheKey key) {
+ return this.cache.policy().getIfPresentQuietly(key) != null;
+ }
+
@Override
public void invalidate(CacheKey key) {
this.cache.invalidate(key);
diff --git a/paimon-common/src/main/java/org/apache/paimon/sst/BlockCache.java
b/paimon-common/src/main/java/org/apache/paimon/sst/BlockCache.java
index 65a6838db7..32e341bbd6 100644
--- a/paimon-common/src/main/java/org/apache/paimon/sst/BlockCache.java
+++ b/paimon-common/src/main/java/org/apache/paimon/sst/BlockCache.java
@@ -79,7 +79,9 @@ public class BlockCache implements Closeable {
},
blocks::remove);
container = new SegmentContainer(segment);
- blocks.put(cacheKey, container);
+ if (cacheManager.contains(cacheKey)) {
+ blocks.put(cacheKey, container);
+ }
}
return container.access();
}
diff --git
a/paimon-common/src/test/java/org/apache/paimon/io/cache/CacheManagerTest.java
b/paimon-common/src/test/java/org/apache/paimon/io/cache/CacheManagerTest.java
index e8ca35271e..662978a3f5 100644
---
a/paimon-common/src/test/java/org/apache/paimon/io/cache/CacheManagerTest.java
+++
b/paimon-common/src/test/java/org/apache/paimon/io/cache/CacheManagerTest.java
@@ -18,8 +18,10 @@
package org.apache.paimon.io.cache;
+import org.apache.paimon.fs.ByteArraySeekableStream;
import org.apache.paimon.memory.MemorySegment;
import org.apache.paimon.options.MemorySize;
+import org.apache.paimon.sst.BlockCache;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
@@ -29,6 +31,7 @@ import java.io.File;
import java.io.RandomAccessFile;
import java.nio.file.Path;
import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat;
@@ -67,6 +70,38 @@ public class CacheManagerTest {
}
}
+ @Test
+ void testRejectedPageNotRetainedByBlockCache() throws Exception {
+ int pageSize = 1024;
+ int hotPages = 64;
+ int totalPages = 10_000;
+ byte[] data = new byte[pageSize * totalPages];
+ org.apache.paimon.fs.Path file = new org.apache.paimon.fs.Path("file");
+ AtomicInteger invalidatedPages = new AtomicInteger();
+ CacheManager cacheManager =
+ new CacheManager(MemorySize.ofKibiBytes(64), 0) {
+ @Override
+ public void invalidPage(CacheKey key) {
+ invalidatedPages.incrementAndGet();
+ super.invalidPage(key);
+ }
+ };
+ BlockCache blockCache =
+ new BlockCache(file, new ByteArraySeekableStream(data),
cacheManager);
+
+ for (int round = 0; round < 100; round++) {
+ for (int page = 0; page < hotPages; page++) {
+ blockCache.getBlock(page * pageSize, pageSize, bytes -> bytes,
false);
+ }
+ }
+ for (int page = hotPages; page < totalPages; page++) {
+ blockCache.getBlock(page * pageSize, pageSize, bytes -> bytes,
false);
+ }
+
+ blockCache.close();
+ assertThat(invalidatedPages).hasValue(hotPages);
+ }
+
@Test
void testOffHeapCache() throws Exception {
File file = new File(tempDir.toFile(), "test.off-heap");