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

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


The following commit(s) were added to refs/heads/main by this push:
     new 6bf0636a perf(fs): skip getFileStatus on Jindo open when the file 
length is known (#332)
6bf0636a is described below

commit 6bf0636aa59fc1657925db74b454a63190dc324e
Author: Yonghao Fang <[email protected]>
AuthorDate: Mon Sep 14 16:51:51 2026 +0800

    perf(fs): skip getFileStatus on Jindo open when the file length is known 
(#332)
---
 src/paimon/common/fs/file_system_test.cpp          |  7 ++++++
 src/paimon/fs/jindo/jindo_file_system.cpp          | 12 ++++++++++
 src/paimon/fs/jindo/jindo_file_system.h            |  1 +
 src/paimon/fs/jindo/jindo_file_system_test.cpp     | 28 ++++++++++++++++++++++
 .../fs/jindo/jindo_file_system_unit_test.cpp       | 10 ++++++++
 .../jindosdk-nextarch/include/JdoFileSystem.hpp    |  7 ++++++
 .../jindosdk-nextarch/src/JdoFileSystem.cpp        | 28 ++++++++++++++++++++++
 7 files changed, 93 insertions(+)

diff --git a/src/paimon/common/fs/file_system_test.cpp 
b/src/paimon/common/fs/file_system_test.cpp
index b4527dd2..f7dc5f5c 100644
--- a/src/paimon/common/fs/file_system_test.cpp
+++ b/src/paimon/common/fs/file_system_test.cpp
@@ -292,6 +292,13 @@ TEST_P(FileSystemTest, TestOpenWithKnownFileSize) {
     ASSERT_OK_AND_ASSIGN(auto input_stream, fs_->Open(file_status));
     ASSERT_OK_AND_ASSIGN(int64_t file_size, input_stream->Length());
     ASSERT_EQ(file_size, content.size());
+    // Reading through the trusted-length open must serve the object's real 
bytes, not just
+    // echo the length the caller passed in.
+    std::string read_content(content.size(), '\0');
+    ASSERT_OK_AND_ASSIGN(int64_t read_len,
+                         input_stream->Read(read_content.data(), 
read_content.size()));
+    ASSERT_EQ(read_len, static_cast<int64_t>(content.size()));
+    ASSERT_EQ(content, read_content);
     ASSERT_OK(input_stream->Close());
 
     ASSERT_TRUE(fs_->Open(FileStatus(file_path, 
/*length=*/-1)).status().IsInvalid());
diff --git a/src/paimon/fs/jindo/jindo_file_system.cpp 
b/src/paimon/fs/jindo/jindo_file_system.cpp
index 3312f8be..793f2b2f 100644
--- a/src/paimon/fs/jindo/jindo_file_system.cpp
+++ b/src/paimon/fs/jindo/jindo_file_system.cpp
@@ -88,6 +88,18 @@ Result<std::unique_ptr<InputStream>> 
JindoFileSystem::Open(const std::string& pa
     return std::make_unique<JindoInputStream>(impl_, std::move(reader));
 }
 
+Result<std::unique_ptr<InputStream>> JindoFileSystem::Open(const FileStatus& 
file_status) const {
+    const int64_t file_length = file_status.GetLen();
+    PAIMON_RETURN_NOT_OK(ValidateValueNonNegative(file_length, "file size"));
+    // The trusted length lets the store skip the getFileStatus it otherwise 
issues on open.
+    // The status is not re-validated here; a stale or incorrect length 
surfaces as a read
+    // error later rather than at open time.
+    std::unique_ptr<JdoReader> reader;
+    PAIMON_RETURN_NOT_OK_FROM_JINDO(
+        impl_->GetFileSystem()->openReader(file_status.GetPath(), file_length, 
&reader));
+    return std::make_unique<JindoInputStream>(impl_, std::move(reader));
+}
+
 Result<std::unique_ptr<OutputStream>> JindoFileSystem::Create(const 
std::string& path,
                                                               bool overwrite) 
const {
     PAIMON_ASSIGN_OR_RAISE(bool exist, Exists(path));
diff --git a/src/paimon/fs/jindo/jindo_file_system.h 
b/src/paimon/fs/jindo/jindo_file_system.h
index fed08bcb..d102904d 100644
--- a/src/paimon/fs/jindo/jindo_file_system.h
+++ b/src/paimon/fs/jindo/jindo_file_system.h
@@ -41,6 +41,7 @@ class JindoFileSystem : public FileSystem {
     using FileSystem::Open;
 
     Result<std::unique_ptr<InputStream>> Open(const std::string& path) const 
override;
+    Result<std::unique_ptr<InputStream>> Open(const FileStatus& file_status) 
const override;
     Result<std::unique_ptr<OutputStream>> Create(const std::string& path,
                                                  bool overwrite) const 
override;
     Status Mkdirs(const std::string& path) const override;
diff --git a/src/paimon/fs/jindo/jindo_file_system_test.cpp 
b/src/paimon/fs/jindo/jindo_file_system_test.cpp
index 54c34a19..e8b26dc6 100644
--- a/src/paimon/fs/jindo/jindo_file_system_test.cpp
+++ b/src/paimon/fs/jindo/jindo_file_system_test.cpp
@@ -204,4 +204,32 @@ TEST(JindoFileSystemAsyncReadTest, 
TestConcurrentReadAsyncAndReadFromOss) {
     }
 }
 
+TEST_F(JindoFileSystemTest, TestOpenWithKnownFileSizeReadsBackContent) {
+    const std::string content = "abcdefghijk";
+    const std::string file_path = test_dir_ + "file.data";
+    ASSERT_OK(fs_->WriteFile(file_path, content, /*overwrite=*/true));
+
+    // The read path hands Open a length it already trusts; the stream must 
still serve the
+    // object's real bytes, not just echo the length back.
+    ASSERT_OK_AND_ASSIGN(auto in_stream,
+                         fs_->Open(FileStatus(file_path, 
static_cast<int64_t>(content.size()))));
+    ASSERT_OK_AND_ASSIGN(int64_t length, in_stream->Length());
+    ASSERT_EQ(length, static_cast<int64_t>(content.size()));
+    std::string read_content(content.size(), '\0');
+    ASSERT_OK_AND_ASSIGN(int64_t read_len,
+                         in_stream->Read(read_content.data(), 
read_content.size()));
+    ASSERT_EQ(read_len, static_cast<int64_t>(content.size()));
+    ASSERT_EQ(content, read_content);
+    ASSERT_OK(in_stream->Close());
+
+    // A stale length shorter than the object must never yield bytes beyond 
what it vouches
+    // for: the wrapper read is all-or-nothing, so over-reading the declared 
length fails with
+    // an EOF error instead of silently returning truncated data.
+    const int64_t short_length = static_cast<int64_t>(content.size()) - 3;
+    ASSERT_OK_AND_ASSIGN(auto short_stream, fs_->Open(FileStatus(file_path, 
short_length)));
+    std::string short_read(content.size(), '\0');
+    ASSERT_NOK_WITH_MSG(short_stream->Read(short_read.data(), 
short_read.size()), "EOF reached");
+    ASSERT_OK(short_stream->Close());
+}
+
 }  // namespace paimon::jindo::test
diff --git a/src/paimon/fs/jindo/jindo_file_system_unit_test.cpp 
b/src/paimon/fs/jindo/jindo_file_system_unit_test.cpp
index 11a4c76e..e1a1c092 100644
--- a/src/paimon/fs/jindo/jindo_file_system_unit_test.cpp
+++ b/src/paimon/fs/jindo/jindo_file_system_unit_test.cpp
@@ -136,4 +136,14 @@ TEST(JindoFileSystemUnitTest, 
CreateReturnsParentDirectoryFailure) {
     ASSERT_EQ(fs.GetCalls()[0], "mkdirs");
 }
 
+TEST(JindoFileSystemUnitTest, 
OpenWithFileStatusRejectsNegativeLengthBeforeOpening) {
+    // The length is validated before the store is touched, so this holds 
without a live OSS:
+    // an uninitialized JdoFileSystem would report an init error, not a size 
error, if the
+    // validation were skipped.
+    jindo::JindoFileSystem fs(std::make_unique<JdoFileSystem>());
+
+    ASSERT_NOK_WITH_MSG(fs.Open(FileStatus("oss://bucket/data.parquet", 
/*length=*/-1)),
+                        "file size");
+}
+
 }  // namespace paimon::test
diff --git a/third_party/jindosdk-nextarch/include/JdoFileSystem.hpp 
b/third_party/jindosdk-nextarch/include/JdoFileSystem.hpp
index f23790c5..8540e479 100644
--- a/third_party/jindosdk-nextarch/include/JdoFileSystem.hpp
+++ b/third_party/jindosdk-nextarch/include/JdoFileSystem.hpp
@@ -134,6 +134,13 @@ public:
 
     JdoStatus openReader(const std::string &path, std::unique_ptr<JdoReader>* 
result);
 
+    // Open a read-only reader with a trusted file length. The length is 
handed to the store
+    // together with a "file status already resolved" hint, so open skips the 
getFileStatus
+    // request it would otherwise issue. The caller is responsible for the 
length being correct;
+    // a stale length may make reads end early or fail at read time instead of 
at open time.
+    JdoStatus openReader(const std::string &path, int64_t file_length,
+                         std::unique_ptr<JdoReader>* result);
+
     JdoStatus openWriter(const std::string &path, std::unique_ptr<JdoWriter>* 
result);
 
     JdoStatus mkdir(const std::string &path, bool recursive);
diff --git a/third_party/jindosdk-nextarch/src/JdoFileSystem.cpp 
b/third_party/jindosdk-nextarch/src/JdoFileSystem.cpp
index 9a4607fd..c2810a88 100644
--- a/third_party/jindosdk-nextarch/src/JdoFileSystem.cpp
+++ b/third_party/jindosdk-nextarch/src/JdoFileSystem.cpp
@@ -33,6 +33,7 @@
 #include "jdo_api.h"
 #include "jdo_defines.h"
 #include "jdo_error.h"
+#include "jdo_option_keys.h"
 #include "jdo_options.h"
 #include "jdo_file_status.h"
 #include "jdo_list_dir_result.h"
@@ -1148,6 +1149,33 @@ JdoStatus JdoFileSystem::openReader(const std::string 
&path,
     return JdoStatus::OK();
 }
 
+JdoStatus JdoFileSystem::openReader(const std::string &path, int64_t 
file_length,
+                                    std::unique_ptr<JdoReader> *result) {
+    auto [store, err] = GetJdoStore(path);
+    if (!err.ok()) {
+        return err;
+    }
+
+    // The hint is what actually suppresses the getFileStatus on open; the 
store only reads
+    // JDO_OPEN_OPTS_FILE_LENGTH when it is set, and otherwise resolves the 
status itself.
+    auto options = jdo_createOptions();
+    jdo_setOption(options, JDO_OPEN_OPTS_HAS_GET_FILE_STATUS, "true");
+    jdo_setOption(options, JDO_OPEN_OPTS_FILE_LENGTH, 
std::to_string(file_length).c_str());
+
+    START_CALL(store)
+    auto stream = jdo_open(ctx, path.c_str(), JDO_OPEN_FLAG_READ_ONLY, 0777, 
options);
+    END_CALL()
+    jdo_freeOptions(options);
+
+    if (errorCode != 0) {
+        return JdoStatus::InternalError(errorCode, "open file reader failed: 
", errorMsg);
+    }
+
+    result->reset(new JindoReader(path, store, stream));
+
+    return JdoStatus::OK();
+}
+
 JdoStatus JdoFileSystem::openWriter(const std::string &path,
                                     std::unique_ptr<JdoWriter> *result) {
     auto [store, err] = GetJdoStore(path);

Reply via email to