This is an automated email from the ASF dual-hosted git repository.

JingsongLi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/paimon-rust.git


The following commit(s) were added to refs/heads/main by this push:
     new e99eb851 perf: reuse known BTree file sizes when opening cached 
readers (#1227)
e99eb851 is described below

commit e99eb851beef2c7486ec2ecb5e793dd0767af1a3
Author: XiaoHongbo <[email protected]>
AuthorDate: Tue Oct 27 16:09:44 2026 +0800

    perf: reuse known BTree file sizes when opening cached readers (#1227)
---
 crates/paimon/src/io/file_io.rs                    | 58 +++++++++++++++++++++-
 .../src/table/global_index_scanner/reader.rs       |  2 +-
 2 files changed, 58 insertions(+), 2 deletions(-)

diff --git a/crates/paimon/src/io/file_io.rs b/crates/paimon/src/io/file_io.rs
index 14e481dd..3e889450 100644
--- a/crates/paimon/src/io/file_io.rs
+++ b/crates/paimon/src/io/file_io.rs
@@ -1579,6 +1579,15 @@ impl InputFile {
     }
 
     pub async fn reader(&self) -> crate::Result<impl FileRead> {
+        self.reader_with_size_hint(None).await
+    }
+
+    /// Open an immutable file using the length recorded in its manifest.
+    pub(crate) async fn reader_with_size(&self, size: u64) -> 
crate::Result<impl FileRead> {
+        self.reader_with_size_hint(Some(size)).await
+    }
+
+    async fn reader_with_size_hint(&self, size: Option<u64>) -> 
crate::Result<impl FileRead> {
         let (op, relative_path, cache_path) = 
self.source.resolve(&self.path).await?;
         let reader = op.reader(&relative_path).await?;
         let (Some(cache), Some(cache_path)) = (&self.cache, 
cache_path.as_ref()) else {
@@ -1590,7 +1599,9 @@ impl InputFile {
             });
         };
         let read_token = cache.read_token(cache_path);
-        let size = if let Some(size) = cache.file_size(cache_path, 
&read_token).await {
+        let size = if let Some(size) = size {
+            size
+        } else if let Some(size) = cache.file_size(cache_path, 
&read_token).await {
             size
         } else {
             let size = op.stat(&relative_path).await?.content_length();
@@ -1913,6 +1924,51 @@ mod file_action_test {
         FileIOBuilder::new("memory").build().unwrap()
     }
 
+    #[tokio::test]
+    async fn test_known_size_shared_reader_preserves_invalidation() {
+        use crate::common::Options;
+        use crate::io::cache::LocalCacheConfig;
+        let dir = tempfile::tempdir().unwrap();
+        let mut options = Options::new();
+        options.set(CatalogOptions::LOCAL_CACHE_ENABLED, "true");
+        options.set("local-cache.shared", "true");
+        options.set("local-cache.max-size", "16 mb");
+        options.set(
+            CatalogOptions::LOCAL_CACHE_DIR,
+            dir.path().join("cache").to_string_lossy(),
+        );
+        options.set(CatalogOptions::LOCAL_CACHE_BLOCK_SIZE, "4");
+        let cache = Arc::new(
+            
LocalCache::new(LocalCacheConfig::from_options(&options).unwrap().unwrap()).unwrap(),
+        );
+        let io = FileIOBuilder::new("file")
+            .with_local_cache(cache)
+            .build()
+            .unwrap();
+        let source_path = dir.path().join("snapshot-1");
+        let path = local_file_path(&source_path);
+        std::fs::write(&source_path, b"abcd").unwrap();
+        let input = io.new_input(&path).unwrap();
+        assert!(
+            input.cache.is_some(),
+            "the regression must exercise caching"
+        );
+        let reader = input.reader_with_size(4).await.unwrap();
+        assert_eq!(reader.read(0..4).await.unwrap().as_ref(), b"abcd");
+        // A hot read must not stat the source again.
+        std::fs::rename(&source_path, dir.path().join("offline")).unwrap();
+        assert!(input.reader().await.is_err());
+        let reader = input.reader_with_size(4).await.unwrap();
+        assert_eq!(reader.read(1..3).await.unwrap().as_ref(), b"bc");
+        io.new_output(&path)
+            .unwrap()
+            .write(Bytes::from_static(b"wxyz"))
+            .await
+            .unwrap();
+        let reader = input.reader_with_size(4).await.unwrap();
+        assert_eq!(reader.read(1..3).await.unwrap().as_ref(), b"xy");
+    }
+
     fn setup_fs_file_io() -> FileIO {
         FileIOBuilder::new("file").build().unwrap()
     }
diff --git a/crates/paimon/src/table/global_index_scanner/reader.rs 
b/crates/paimon/src/table/global_index_scanner/reader.rs
index 3e999a67..ba3c4299 100644
--- a/crates/paimon/src/table/global_index_scanner/reader.rs
+++ b/crates/paimon/src/table/global_index_scanner/reader.rs
@@ -258,7 +258,7 @@ impl GlobalIndexScanner {
         } else {
             input.metadata().await?.size
         };
-        let file_reader = input.reader().await?;
+        let file_reader = input.reader_with_size(file_size).await?;
 
         let cmp = make_key_comparator(data_type);
         BTreeIndexReader::open_with_data_block_cache(

Reply via email to