Skip to content

Commit 79c96c3

Browse files
mrdrivingduckcodex
andcommitted
perf(fs): avoid redundant object-store metadata requests
Allow callers with trusted file metadata to open streams with a known size and avoid a metadata request for object storage. Use data-file metadata in split reads while retaining Open(path) as the default implementation. Co-authored-by: GPT-5.6 Terra <codex@users.noreply.github.com>
1 parent 3663237 commit 79c96c3

10 files changed

Lines changed: 71 additions & 10 deletions

include/paimon/fs/file_system.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,15 @@ class PAIMON_EXPORT FileSystem {
193193
/// failure (e.g., file not found, permission denied).
194194
virtual Result<std::unique_ptr<InputStream>> Open(const std::string& path) const = 0;
195195

196+
/// Open an existing file for reading with a known file size.
197+
///
198+
/// File systems that can use the size to avoid metadata requests may override this method.
199+
/// The default implementation ignores the size and opens the file normally.
200+
virtual Result<std::unique_ptr<InputStream>> Open(const std::string& path,
201+
int64_t /*file_size*/) const {
202+
return Open(path);
203+
}
204+
196205
/// Create a new file for writing.
197206
/// @param path The file path to create.
198207
/// @param overwrite If true, overwrite existing file; if false, fail if file exists.

src/paimon/common/fs/object_store_file_system.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,17 @@ Result<std::unique_ptr<InputStream>> ObjectStoreFileSystem::Open(const std::stri
388388
ToUri(object_path), metadata.value().size);
389389
}
390390

391+
Result<std::unique_ptr<InputStream>> ObjectStoreFileSystem::Open(const std::string& path,
392+
int64_t file_size) const {
393+
PAIMON_RETURN_NOT_OK(ValidateValueNonNegative(file_size, "file size"));
394+
PAIMON_ASSIGN_OR_RAISE(ObjectStorePath object_path, ParsePath(path));
395+
if (object_path.key.empty()) {
396+
return Status::Invalid(fmt::format("{} is a directory", path));
397+
}
398+
return std::make_unique<ObjectStoreInputStream>(client_, read_ahead_limiter_, object_path,
399+
ToUri(object_path), file_size);
400+
}
401+
391402
Result<std::unique_ptr<FileStatus>> ObjectStoreFileSystem::GetFileStatus(
392403
const std::string& path) const {
393404
PAIMON_ASSIGN_OR_RAISE(ObjectStorePath object_path, ParsePath(path));

src/paimon/common/fs/object_store_file_system.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ class PAIMON_EXPORT ObjectStoreFileSystem : public FileSystem {
8585
~ObjectStoreFileSystem() override = default;
8686

8787
Result<std::unique_ptr<InputStream>> Open(const std::string& path) const override;
88+
Result<std::unique_ptr<InputStream>> Open(const std::string& path,
89+
int64_t file_size) const override;
8890
Result<std::unique_ptr<FileStatus>> GetFileStatus(const std::string& path) const override;
8991
Status ListDir(const std::string& directory,
9092
std::vector<std::unique_ptr<BasicFileStatus>>* file_status_list) const override;

src/paimon/common/fs/object_store_file_system_test.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,15 @@ TEST(ObjectStoreFileSystemTest, TestOpenBucketRootIsDirectory) {
159159
ASSERT_EQ(client->list_calls_, 0);
160160
}
161161

162+
TEST(ObjectStoreFileSystemTest, TestOpenWithKnownLengthSkipsHead) {
163+
auto client = std::make_shared<MockObjectStoreClient>();
164+
client->objects_["file"] = "data";
165+
ObjectStoreFileSystem fs("s3", client);
166+
ASSERT_OK_AND_ASSIGN(auto stream, fs.Open("s3://bucket/file", 4));
167+
ASSERT_EQ(stream->Length().value(), 4);
168+
ASSERT_EQ(client->head_calls_, 0);
169+
}
170+
162171
TEST(ObjectStoreFileSystemTest, TestPathWithLeadingSlashes) {
163172
auto client = std::make_shared<MockObjectStoreClient>();
164173
client->objects_["file"] = "data";

src/paimon/common/fs/resolving_file_system.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ Result<std::unique_ptr<InputStream>> ResolvingFileSystem::Open(const std::string
7979
return fs->Open(path);
8080
}
8181

82+
Result<std::unique_ptr<InputStream>> ResolvingFileSystem::Open(const std::string& path,
83+
int64_t file_size) const {
84+
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<FileSystem> fs, GetRealFileSystem(path));
85+
return fs->Open(path, file_size);
86+
}
87+
8288
Result<std::unique_ptr<OutputStream>> ResolvingFileSystem::Create(const std::string& path,
8389
bool overwrite) const {
8490
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<FileSystem> fs, GetRealFileSystem(path));

src/paimon/common/fs/resolving_file_system.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class ResolvingFileSystem : public FileSystem {
4141
~ResolvingFileSystem() override = default;
4242

4343
Result<std::unique_ptr<InputStream>> Open(const std::string& path) const override;
44+
Result<std::unique_ptr<InputStream>> Open(const std::string& path,
45+
int64_t file_size) const override;
4446
Result<std::unique_ptr<OutputStream>> Create(const std::string& path,
4547
bool overwrite) const override;
4648

src/paimon/common/reader/prefetch_file_batch_reader_impl.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,19 @@ Result<std::unique_ptr<PrefetchFileBatchReaderImpl>> PrefetchFileBatchReaderImpl
6262
const std::shared_ptr<Executor>& executor, bool initialize_read_ranges,
6363
PrefetchCacheMode prefetch_cache_mode, const CacheConfig& cache_config,
6464
const std::shared_ptr<MemoryPool>& pool) {
65+
return Create(data_file_path, /*data_file_size=*/-1, reader_builder, fs,
66+
prefetch_max_parallel_num, batch_size, prefetch_batch_count,
67+
enable_adaptive_prefetch_strategy, executor, initialize_read_ranges,
68+
prefetch_cache_mode, cache_config, pool);
69+
}
70+
71+
Result<std::unique_ptr<PrefetchFileBatchReaderImpl>> PrefetchFileBatchReaderImpl::Create(
72+
const std::string& data_file_path, int64_t data_file_size, const ReaderBuilder* reader_builder,
73+
const std::shared_ptr<FileSystem>& fs, uint32_t prefetch_max_parallel_num, int32_t batch_size,
74+
uint32_t prefetch_batch_count, bool enable_adaptive_prefetch_strategy,
75+
const std::shared_ptr<Executor>& executor, bool initialize_read_ranges,
76+
PrefetchCacheMode prefetch_cache_mode, const CacheConfig& cache_config,
77+
const std::shared_ptr<MemoryPool>& pool) {
6578
if (prefetch_max_parallel_num == 0) {
6679
return Status::Invalid("prefetch max parallel num should be greater than 0.");
6780
}
@@ -83,16 +96,17 @@ Result<std::unique_ptr<PrefetchFileBatchReaderImpl>> PrefetchFileBatchReaderImpl
8396

8497
std::shared_ptr<ReadAheadCache> cache;
8598
if (prefetch_cache_mode != PrefetchCacheMode::NEVER) {
86-
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<InputStream> input_stream, fs->Open(data_file_path));
99+
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<InputStream> input_stream,
100+
fs->Open(data_file_path, data_file_size));
87101
cache = std::make_shared<ReadAheadCache>(input_stream, cache_config, pool);
88102
}
89103
std::vector<std::future<Result<std::unique_ptr<FileBatchReader>>>> futures;
90104
for (uint32_t i = 0; i < prefetch_max_parallel_num; i++) {
91105
futures.push_back(Via(executor.get(),
92-
[&fs, &data_file_path, &reader_builder,
106+
[&fs, &data_file_path, data_file_size, &reader_builder,
93107
&cache]() -> Result<std::unique_ptr<FileBatchReader>> {
94108
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<InputStream> input_stream,
95-
fs->Open(data_file_path));
109+
fs->Open(data_file_path, data_file_size));
96110
auto cache_input_stream = std::make_shared<CacheInputStream>(
97111
std::move(input_stream), cache);
98112
return reader_builder->Build(cache_input_stream);

src/paimon/common/reader/prefetch_file_batch_reader_impl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ class Metrics;
5555

5656
class PrefetchFileBatchReaderImpl : public PrefetchFileBatchReader {
5757
public:
58+
static Result<std::unique_ptr<PrefetchFileBatchReaderImpl>> Create(
59+
const std::string& data_file_path, int64_t data_file_size,
60+
const ReaderBuilder* reader_builder, const std::shared_ptr<FileSystem>& fs,
61+
uint32_t prefetch_max_parallel_num, int32_t batch_size, uint32_t prefetch_batch_count,
62+
bool enable_adaptive_prefetch_strategy, const std::shared_ptr<Executor>& executor,
63+
bool initialize_read_ranges, PrefetchCacheMode prefetch_cache_mode,
64+
const CacheConfig& cache_config, const std::shared_ptr<MemoryPool>& pool);
65+
5866
static Result<std::unique_ptr<PrefetchFileBatchReaderImpl>> Create(
5967
const std::string& data_file_path, const ReaderBuilder* reader_builder,
6068
const std::shared_ptr<FileSystem>& fs, uint32_t prefetch_max_parallel_num,

src/paimon/core/operation/abstract_split_read.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,13 +144,13 @@ Result<std::unique_ptr<ReaderBuilder>> AbstractSplitRead::PrepareReaderBuilder(
144144

145145
Result<std::unique_ptr<FileBatchReader>> AbstractSplitRead::CreateFileBatchReader(
146146
const std::string& file_format_identifier, const std::string& data_file_path,
147-
const ReaderBuilder* reader_builder) const {
147+
int64_t data_file_size, const ReaderBuilder* reader_builder) const {
148148
if (context_->EnablePrefetch() && file_format_identifier != "blob" &&
149149
file_format_identifier != "avro") {
150150
PAIMON_ASSIGN_OR_RAISE(
151151
std::unique_ptr<PrefetchFileBatchReaderImpl> prefetch_reader,
152152
PrefetchFileBatchReaderImpl::Create(
153-
data_file_path, reader_builder, options_.GetFileSystem(),
153+
data_file_path, data_file_size, reader_builder, options_.GetFileSystem(),
154154
context_->GetPrefetchMaxParallelNum(), options_.GetReadBatchSize(),
155155
context_->GetPrefetchBatchCount(), options_.EnableAdaptivePrefetchStrategy(),
156156
executor_,
@@ -159,7 +159,7 @@ Result<std::unique_ptr<FileBatchReader>> AbstractSplitRead::CreateFileBatchReade
159159
return std::make_unique<DelegatingPrefetchReader>(std::move(prefetch_reader));
160160
} else {
161161
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<InputStream> input_stream,
162-
options_.GetFileSystem()->Open(data_file_path));
162+
options_.GetFileSystem()->Open(data_file_path, data_file_size));
163163
return reader_builder->Build(input_stream);
164164
}
165165
}
@@ -204,9 +204,9 @@ Result<std::unique_ptr<FileBatchReader>> AbstractSplitRead::CreateFieldMappingRe
204204
field_mapping->non_partition_info.non_partition_data_schema);
205205

206206
PAIMON_ASSIGN_OR_RAISE(std::string file_format_identifier, file_meta->FileFormat());
207-
PAIMON_ASSIGN_OR_RAISE(
208-
std::unique_ptr<FileBatchReader> file_reader,
209-
CreateFileBatchReader(file_format_identifier, data_file_path, reader_builder));
207+
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<FileBatchReader> file_reader,
208+
CreateFileBatchReader(file_format_identifier, data_file_path,
209+
file_meta->file_size, reader_builder));
210210
std::set<int32_t> skip_map_selected_keys_filter_field_ids;
211211
if (file_format_identifier != "blob") {
212212
std::pair<std::unique_ptr<FileBatchReader>, std::set<int32_t>> shared_shredding_result;

src/paimon/core/operation/abstract_split_read.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ class AbstractSplitRead : public SplitRead {
107107

108108
Result<std::unique_ptr<FileBatchReader>> CreateFileBatchReader(
109109
const std::string& file_format_identifier, const std::string& data_file_path,
110-
const ReaderBuilder* reader_builder) const;
110+
int64_t data_file_size, const ReaderBuilder* reader_builder) const;
111111

112112
// return nullptr if data file is skipped by index or dv
113113
Result<std::unique_ptr<FileBatchReader>> CreateFieldMappingReader(

0 commit comments

Comments
 (0)