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 50f8951f8d [common] Close the file-index stream when reader
construction fails (#9636)
50f8951f8d is described below
commit 50f8951f8dfd89ef48a181d106e3486633a75eb2
Author: YangJie <[email protected]>
AuthorDate: Thu Sep 10 02:53:36 2026 -0400
[common] Close the file-index stream when reader construction fails (#9636)
---
.../apache/paimon/fileindex/FileIndexFormat.java | 5 ++++-
.../paimon/fileindex/FileIndexPredicate.java | 12 +++--------
.../fileindex/FileIndexFormatFormatTest.java | 25 ++++++++++++++++++++++
.../fileindex/FileIndexPredicateCloseTest.java | 3 ++-
4 files changed, 34 insertions(+), 11 deletions(-)
diff --git
a/paimon-common/src/main/java/org/apache/paimon/fileindex/FileIndexFormat.java
b/paimon-common/src/main/java/org/apache/paimon/fileindex/FileIndexFormat.java
index e10a95c50c..c2d27ec9f0 100644
---
a/paimon-common/src/main/java/org/apache/paimon/fileindex/FileIndexFormat.java
+++
b/paimon-common/src/main/java/org/apache/paimon/fileindex/FileIndexFormat.java
@@ -318,7 +318,10 @@ public final class FileIndexFormat {
}
}
}
- } catch (IOException e) {
+ } catch (IOException | RuntimeException e) {
+ // Callers wrap the constructor in try-with-resources on the
stream,
+ // but a throwing constructor never assigns the resource, so
both
+ // checked and unchecked validation failures must close here.
IOUtils.closeQuietly(seekableInputStream);
throw new RuntimeException(
"Exception happens while construct file index
reader.", e);
diff --git
a/paimon-common/src/main/java/org/apache/paimon/fileindex/FileIndexPredicate.java
b/paimon-common/src/main/java/org/apache/paimon/fileindex/FileIndexPredicate.java
index 829b2ecd9f..82d14cf7ca 100644
---
a/paimon-common/src/main/java/org/apache/paimon/fileindex/FileIndexPredicate.java
+++
b/paimon-common/src/main/java/org/apache/paimon/fileindex/FileIndexPredicate.java
@@ -32,7 +32,6 @@ import org.apache.paimon.predicate.PredicateVisitor;
import org.apache.paimon.predicate.SortValue;
import org.apache.paimon.predicate.TopN;
import org.apache.paimon.types.RowType;
-import org.apache.paimon.utils.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -69,14 +68,9 @@ public class FileIndexPredicate implements Closeable {
}
public FileIndexPredicate(SeekableInputStream inputStream, RowType
fileRowType) {
- try {
- this.reader = FileIndexFormat.createReader(inputStream,
fileRowType);
- } catch (RuntimeException e) {
- // nothing else holds a reference to inputStream yet, so this is
the only chance to
- // release it: createReader rejects a file whose magic or version
does not match.
- IOUtils.closeQuietly(inputStream);
- throw e;
- }
+ // createReader itself closes the stream when the header fails
validation, so
+ // there is no stream to release here anymore.
+ this.reader = FileIndexFormat.createReader(inputStream, fileRowType);
}
public FileIndexResult evaluate(@Nullable Predicate predicate) {
diff --git
a/paimon-common/src/test/java/org/apache/paimon/fileindex/FileIndexFormatFormatTest.java
b/paimon-common/src/test/java/org/apache/paimon/fileindex/FileIndexFormatFormatTest.java
index 0f5f6e299e..c6ce94c89a 100644
---
a/paimon-common/src/test/java/org/apache/paimon/fileindex/FileIndexFormatFormatTest.java
+++
b/paimon-common/src/test/java/org/apache/paimon/fileindex/FileIndexFormatFormatTest.java
@@ -20,6 +20,7 @@ package org.apache.paimon.fileindex;
import org.apache.paimon.fileindex.empty.EmptyFileIndexReader;
import org.apache.paimon.fs.ByteArraySeekableStream;
+import org.apache.paimon.fs.SeekableInputStream;
import org.apache.paimon.types.DataTypes;
import org.apache.paimon.types.RowType;
@@ -35,6 +36,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
+import java.util.concurrent.atomic.AtomicBoolean;
import static org.apache.paimon.utils.RandomUtil.randomBytes;
import static org.apache.paimon.utils.RandomUtil.randomString;
@@ -45,6 +47,29 @@ public class FileIndexFormatFormatTest {
private static final Random RANDOM = new Random();
+ @Test
+ public void testCreateReaderClosesStreamOnBadMagic() {
+ byte[] notIndexFile = "this is definitely not a file index".getBytes();
+ AtomicBoolean closed = new AtomicBoolean();
+ SeekableInputStream counting =
+ new ByteArraySeekableStream(notIndexFile) {
+ @Override
+ public void close() throws IOException {
+ closed.set(true);
+ super.close();
+ }
+ };
+ Throwable thrown =
+ Assertions.catchThrowable(
+ () -> FileIndexFormat.createReader(counting,
RowType.builder().build()));
+ // A throwing constructor never assigns the caller's
try-with-resources resource,
+ // so closing the stream is the constructor's job.
+ Assertions.assertThat(closed.get()).isTrue();
+ Assertions.assertThat(thrown)
+ .isInstanceOf(RuntimeException.class)
+ .hasRootCauseMessage("This file is not file index file.");
+ }
+
@Test
public void testWriteRead() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
diff --git
a/paimon-common/src/test/java/org/apache/paimon/fileindex/FileIndexPredicateCloseTest.java
b/paimon-common/src/test/java/org/apache/paimon/fileindex/FileIndexPredicateCloseTest.java
index 98b6cf9370..9e84dc7d62 100644
---
a/paimon-common/src/test/java/org/apache/paimon/fileindex/FileIndexPredicateCloseTest.java
+++
b/paimon-common/src/test/java/org/apache/paimon/fileindex/FileIndexPredicateCloseTest.java
@@ -47,7 +47,8 @@ public class FileIndexPredicateCloseTest {
assertThatThrownBy(() -> new
FileIndexPredicate(tracking(notAnIndexFile, closed), ROW_TYPE))
.isInstanceOf(RuntimeException.class)
- .hasMessageContaining("not file index file");
+ .hasMessageContaining("Exception happens while construct file
index reader.")
+ .hasRootCauseMessage("This file is not file index file.");
assertThat(closed).hasValue(1);
}