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 4242ff2fb9 [common] Fail on a short remote read instead of caching 
zero padding (#9593)
4242ff2fb9 is described below

commit 4242ff2fb94d6d154dad540d14a81554756a1bb4
Author: jackylee <[email protected]>
AuthorDate: Fri Sep 4 15:21:39 2026 +0800

    [common] Fail on a short remote read instead of caching zero padding (#9593)
---
 .../fs/cache/CachingSeekableInputStream.java       | 21 +++++------------
 .../apache/paimon/fs/cache/CachingFileIOTest.java  | 26 +++++++++++++++++++++-
 2 files changed, 30 insertions(+), 17 deletions(-)

diff --git 
a/paimon-common/src/main/java/org/apache/paimon/fs/cache/CachingSeekableInputStream.java
 
b/paimon-common/src/main/java/org/apache/paimon/fs/cache/CachingSeekableInputStream.java
index 48032c8a70..447912330f 100644
--- 
a/paimon-common/src/main/java/org/apache/paimon/fs/cache/CachingSeekableInputStream.java
+++ 
b/paimon-common/src/main/java/org/apache/paimon/fs/cache/CachingSeekableInputStream.java
@@ -195,7 +195,11 @@ public class CachingSeekableInputStream extends 
SeekableInputStream implements V
         }
         synchronized (stream) {
             stream.seek(offset);
-            return readFully(stream, size);
+            // must not tolerate a short read: the block is handed to 
putBlock, so a zero-padded
+            // tail would be cached and returned as file content for every 
later read
+            byte[] buf = new byte[size];
+            IOUtils.readFully(stream, buf, 0, size);
+            return buf;
         }
     }
 
@@ -236,21 +240,6 @@ public class CachingSeekableInputStream extends 
SeekableInputStream implements V
         }
     }
 
-    private static byte[] readFully(SeekableInputStream in, int size) throws 
IOException {
-        byte[] buf = new byte[size];
-        int remaining = size;
-        int off = 0;
-        while (remaining > 0) {
-            int n = in.read(buf, off, remaining);
-            if (n < 0) {
-                break;
-            }
-            off += n;
-            remaining -= n;
-        }
-        return buf;
-    }
-
     @Override
     public void close() throws IOException {
         // takes no lock, so it never waits behind an in-flight remote open
diff --git 
a/paimon-common/src/test/java/org/apache/paimon/fs/cache/CachingFileIOTest.java 
b/paimon-common/src/test/java/org/apache/paimon/fs/cache/CachingFileIOTest.java
index 35e9ec7668..ded23667a9 100644
--- 
a/paimon-common/src/test/java/org/apache/paimon/fs/cache/CachingFileIOTest.java
+++ 
b/paimon-common/src/test/java/org/apache/paimon/fs/cache/CachingFileIOTest.java
@@ -138,6 +138,23 @@ class CachingFileIOTest {
         return new CachingFileIO(delegate, cache, whitelist);
     }
 
+    @Test
+    void testShortRemoteReadIsNotCachedAsZeroPaddedBlock() throws IOException {
+        byte[] data = "truncated".getBytes();
+        MockFileIO delegate = new MockFileIO();
+        // the status says 8 bytes more than the stream can hand out
+        delegate.addTruncatedFile("snapshot-1", data, data.length + 8);
+
+        LocalDiskCacheManager cache = new LocalDiskCacheManager(cacheDir, 
Long.MAX_VALUE, 64);
+        CachingFileIO cachingIO = newCachingFileIO(delegate, cache, 
EnumSet.of(FileType.META), 64);
+
+        try (SeekableInputStream s = cachingIO.newInputStream(new 
Path("snapshot-1"))) {
+            assertThatThrownBy(() -> readAll(s, data.length + 8))
+                    .isInstanceOf(IOException.class)
+                    .hasMessageContaining("Premature EOF");
+        }
+    }
+
     @Test
     void testMetaFileIsCached() throws IOException {
         byte[] data = "snapshot data".getBytes();
@@ -976,6 +993,7 @@ class CachingFileIOTest {
                 new ConcurrentHashMap<>();
 
         private final Map<String, byte[]> files = new HashMap<>();
+        private final Map<String, Long> reportedLengths = new HashMap<>();
         // concurrent so the thread-safety tests below can count from several 
reader threads
         private final Map<String, Integer> fileStatusCalls = new 
ConcurrentHashMap<>();
         private final Map<String, Integer> newInputStreamCalls = new 
ConcurrentHashMap<>();
@@ -1024,6 +1042,12 @@ class CachingFileIOTest {
             files.put(name, data);
         }
 
+        /** Reports a length beyond the bytes on hand, the way a truncated 
remote file does. */
+        void addTruncatedFile(String name, byte[] data, long reportedLength) {
+            files.put(name, data);
+            reportedLengths.put(name, reportedLength);
+        }
+
         int getFileStatusCallCount(String name) {
             return fileStatusCalls.getOrDefault(name, 0);
         }
@@ -1084,7 +1108,7 @@ class CachingFileIOTest {
             return new FileStatus() {
                 @Override
                 public long getLen() {
-                    return data.length;
+                    return reportedLengths.getOrDefault(name, (long) 
data.length);
                 }
 
                 @Override

Reply via email to