Skip to content

Commit 6bf0636

Browse files
authored
perf(fs): skip getFileStatus on Jindo open when the file length is known (#332)
1 parent 0078e18 commit 6bf0636

7 files changed

Lines changed: 93 additions & 0 deletions

File tree

‎src/paimon/common/fs/file_system_test.cpp‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,13 @@ TEST_P(FileSystemTest, TestOpenWithKnownFileSize) {
292292
ASSERT_OK_AND_ASSIGN(auto input_stream, fs_->Open(file_status));
293293
ASSERT_OK_AND_ASSIGN(int64_t file_size, input_stream->Length());
294294
ASSERT_EQ(file_size, content.size());
295+
// Reading through the trusted-length open must serve the object's real bytes, not just
296+
// echo the length the caller passed in.
297+
std::string read_content(content.size(), '\0');
298+
ASSERT_OK_AND_ASSIGN(int64_t read_len,
299+
input_stream->Read(read_content.data(), read_content.size()));
300+
ASSERT_EQ(read_len, static_cast<int64_t>(content.size()));
301+
ASSERT_EQ(content, read_content);
295302
ASSERT_OK(input_stream->Close());
296303

297304
ASSERT_TRUE(fs_->Open(FileStatus(file_path, /*length=*/-1)).status().IsInvalid());

‎src/paimon/fs/jindo/jindo_file_system.cpp‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,18 @@ Result<std::unique_ptr<InputStream>> JindoFileSystem::Open(const std::string& pa
8888
return std::make_unique<JindoInputStream>(impl_, std::move(reader));
8989
}
9090

91+
Result<std::unique_ptr<InputStream>> JindoFileSystem::Open(const FileStatus& file_status) const {
92+
const int64_t file_length = file_status.GetLen();
93+
PAIMON_RETURN_NOT_OK(ValidateValueNonNegative(file_length, "file size"));
94+
// The trusted length lets the store skip the getFileStatus it otherwise issues on open.
95+
// The status is not re-validated here; a stale or incorrect length surfaces as a read
96+
// error later rather than at open time.
97+
std::unique_ptr<JdoReader> reader;
98+
PAIMON_RETURN_NOT_OK_FROM_JINDO(
99+
impl_->GetFileSystem()->openReader(file_status.GetPath(), file_length, &reader));
100+
return std::make_unique<JindoInputStream>(impl_, std::move(reader));
101+
}
102+
91103
Result<std::unique_ptr<OutputStream>> JindoFileSystem::Create(const std::string& path,
92104
bool overwrite) const {
93105
PAIMON_ASSIGN_OR_RAISE(bool exist, Exists(path));

‎src/paimon/fs/jindo/jindo_file_system.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class JindoFileSystem : public FileSystem {
4141
using FileSystem::Open;
4242

4343
Result<std::unique_ptr<InputStream>> Open(const std::string& path) const override;
44+
Result<std::unique_ptr<InputStream>> Open(const FileStatus& file_status) const override;
4445
Result<std::unique_ptr<OutputStream>> Create(const std::string& path,
4546
bool overwrite) const override;
4647
Status Mkdirs(const std::string& path) const override;

‎src/paimon/fs/jindo/jindo_file_system_test.cpp‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,32 @@ TEST(JindoFileSystemAsyncReadTest, TestConcurrentReadAsyncAndReadFromOss) {
204204
}
205205
}
206206

207+
TEST_F(JindoFileSystemTest, TestOpenWithKnownFileSizeReadsBackContent) {
208+
const std::string content = "abcdefghijk";
209+
const std::string file_path = test_dir_ + "file.data";
210+
ASSERT_OK(fs_->WriteFile(file_path, content, /*overwrite=*/true));
211+
212+
// The read path hands Open a length it already trusts; the stream must still serve the
213+
// object's real bytes, not just echo the length back.
214+
ASSERT_OK_AND_ASSIGN(auto in_stream,
215+
fs_->Open(FileStatus(file_path, static_cast<int64_t>(content.size()))));
216+
ASSERT_OK_AND_ASSIGN(int64_t length, in_stream->Length());
217+
ASSERT_EQ(length, static_cast<int64_t>(content.size()));
218+
std::string read_content(content.size(), '\0');
219+
ASSERT_OK_AND_ASSIGN(int64_t read_len,
220+
in_stream->Read(read_content.data(), read_content.size()));
221+
ASSERT_EQ(read_len, static_cast<int64_t>(content.size()));
222+
ASSERT_EQ(content, read_content);
223+
ASSERT_OK(in_stream->Close());
224+
225+
// A stale length shorter than the object must never yield bytes beyond what it vouches
226+
// for: the wrapper read is all-or-nothing, so over-reading the declared length fails with
227+
// an EOF error instead of silently returning truncated data.
228+
const int64_t short_length = static_cast<int64_t>(content.size()) - 3;
229+
ASSERT_OK_AND_ASSIGN(auto short_stream, fs_->Open(FileStatus(file_path, short_length)));
230+
std::string short_read(content.size(), '\0');
231+
ASSERT_NOK_WITH_MSG(short_stream->Read(short_read.data(), short_read.size()), "EOF reached");
232+
ASSERT_OK(short_stream->Close());
233+
}
234+
207235
} // namespace paimon::jindo::test

‎src/paimon/fs/jindo/jindo_file_system_unit_test.cpp‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,14 @@ TEST(JindoFileSystemUnitTest, CreateReturnsParentDirectoryFailure) {
136136
ASSERT_EQ(fs.GetCalls()[0], "mkdirs");
137137
}
138138

139+
TEST(JindoFileSystemUnitTest, OpenWithFileStatusRejectsNegativeLengthBeforeOpening) {
140+
// The length is validated before the store is touched, so this holds without a live OSS:
141+
// an uninitialized JdoFileSystem would report an init error, not a size error, if the
142+
// validation were skipped.
143+
jindo::JindoFileSystem fs(std::make_unique<JdoFileSystem>());
144+
145+
ASSERT_NOK_WITH_MSG(fs.Open(FileStatus("oss://bucket/data.parquet", /*length=*/-1)),
146+
"file size");
147+
}
148+
139149
} // namespace paimon::test

‎third_party/jindosdk-nextarch/include/JdoFileSystem.hpp‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ class JDO_EXPORT JdoFileSystem {
134134

135135
JdoStatus openReader(const std::string &path, std::unique_ptr<JdoReader>* result);
136136

137+
// Open a read-only reader with a trusted file length. The length is handed to the store
138+
// together with a "file status already resolved" hint, so open skips the getFileStatus
139+
// request it would otherwise issue. The caller is responsible for the length being correct;
140+
// a stale length may make reads end early or fail at read time instead of at open time.
141+
JdoStatus openReader(const std::string &path, int64_t file_length,
142+
std::unique_ptr<JdoReader>* result);
143+
137144
JdoStatus openWriter(const std::string &path, std::unique_ptr<JdoWriter>* result);
138145

139146
JdoStatus mkdir(const std::string &path, bool recursive);

‎third_party/jindosdk-nextarch/src/JdoFileSystem.cpp‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "jdo_api.h"
3434
#include "jdo_defines.h"
3535
#include "jdo_error.h"
36+
#include "jdo_option_keys.h"
3637
#include "jdo_options.h"
3738
#include "jdo_file_status.h"
3839
#include "jdo_list_dir_result.h"
@@ -1148,6 +1149,33 @@ JdoStatus JdoFileSystem::openReader(const std::string &path,
11481149
return JdoStatus::OK();
11491150
}
11501151

1152+
JdoStatus JdoFileSystem::openReader(const std::string &path, int64_t file_length,
1153+
std::unique_ptr<JdoReader> *result) {
1154+
auto [store, err] = GetJdoStore(path);
1155+
if (!err.ok()) {
1156+
return err;
1157+
}
1158+
1159+
// The hint is what actually suppresses the getFileStatus on open; the store only reads
1160+
// JDO_OPEN_OPTS_FILE_LENGTH when it is set, and otherwise resolves the status itself.
1161+
auto options = jdo_createOptions();
1162+
jdo_setOption(options, JDO_OPEN_OPTS_HAS_GET_FILE_STATUS, "true");
1163+
jdo_setOption(options, JDO_OPEN_OPTS_FILE_LENGTH, std::to_string(file_length).c_str());
1164+
1165+
START_CALL(store)
1166+
auto stream = jdo_open(ctx, path.c_str(), JDO_OPEN_FLAG_READ_ONLY, 0777, options);
1167+
END_CALL()
1168+
jdo_freeOptions(options);
1169+
1170+
if (errorCode != 0) {
1171+
return JdoStatus::InternalError(errorCode, "open file reader failed: ", errorMsg);
1172+
}
1173+
1174+
result->reset(new JindoReader(path, store, stream));
1175+
1176+
return JdoStatus::OK();
1177+
}
1178+
11511179
JdoStatus JdoFileSystem::openWriter(const std::string &path,
11521180
std::unique_ptr<JdoWriter> *result) {
11531181
auto [store, err] = GetJdoStore(path);

0 commit comments

Comments
 (0)