Skip to content

Commit 4ddc508

Browse files
committed
refactor(read)!: remove decoded warmup mode
BREAKING CHANGE: WarmupLevel::DECODED is removed. Use RAW for byte warmup or NONE to disable it.
1 parent dd993eb commit 4ddc508

17 files changed

Lines changed: 161 additions & 261 deletions

‎docs/source/user_guide/prefetch.rst‎

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -83,37 +83,34 @@ the default ``loser-tree`` sort engine additionally warms the first file of
8383
every sorted run before merging. It applies to append-table reads and append
8484
compaction as well, but does not cross split boundaries.
8585

86-
How far the next file is prepared is set with
86+
Whether the next file's raw bytes are fetched early is set with
8787
``ReadContextBuilder::SetWarmupLevel()``. Append compaction builds its own read
8888
context and always uses the default level.
8989

9090
- ``WarmupLevel::NONE``: do not warm up.
9191
- ``WarmupLevel::RAW`` (default): fetch the next file's raw bytes through the
9292
read-ahead cache without decoding them. It has no effect when the read-ahead
9393
cache is disabled with ``ReadContextBuilder::SetReadAheadCacheEnabled()``.
94-
- ``WarmupLevel::DECODED``: also start decoding the next file, which hides the
95-
most latency but uses the most memory.
9694

9795
Warmup and the read-ahead cache work independently of batch prefetch for
98-
Parquet and ORC. With ``EnablePrefetch(false)``, ``RAW`` still warms the cache,
99-
and ``DECODED`` prepares only the first batch on a background thread; later
100-
batches are read synchronously. The same behavior applies when the format or
101-
the adaptive strategy decides against prefetching. With batch prefetch active,
102-
``DECODED`` starts the normal parallel decode loop instead.
96+
Parquet and ORC. With ``EnablePrefetch(false)``, ``RAW`` still warms the cache
97+
and batches are read synchronously. The same behavior applies when the format
98+
or the adaptive strategy decides against prefetching. Warmup only fetches raw
99+
bytes; it never decodes batches. When batch prefetch is active, the first
100+
actual read starts the parallel decode loop.
103101

104102
The defaults are batch prefetch off, read-ahead cache on, and ``RAW`` warmup.
105103
``WarmupLevel::NONE`` disables early warmup while retaining byte caching during
106-
normal reads. Disabling the cache makes ``RAW`` a no-op but still permits
107-
``DECODED``. When the shared cache is active, Parquet disables its own
108-
pre-buffering to avoid buffering the same byte ranges twice.
104+
normal reads. Disabling the cache makes ``RAW`` a no-op. When the shared cache
105+
is active, Parquet disables its own pre-buffering to avoid buffering the same
106+
byte ranges twice.
109107

110108
``WarmupLevel::RAW`` issues its fetches from the reading thread through the
111109
file system's asynchronous read, so it only overlaps I/O when that read is
112110
really asynchronous. The local file system serves it synchronously, so there
113111
``RAW`` warmup reads the next file on the reading thread before the current
114112
batch is returned, adding latency instead of hiding it. On such file systems,
115-
prefer ``WarmupLevel::DECODED``, which fetches on the background decode thread,
116-
or ``WarmupLevel::NONE``.
113+
use ``WarmupLevel::NONE`` to disable early fetching.
117114

118115
Warmup preserves the returned rows and their order, but it costs memory: under
119116
``WarmupLevel::RAW`` the next file may hold up to the read-ahead cache's

‎include/paimon/read_context.h‎

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -425,18 +425,15 @@ class PAIMON_EXPORT ReadContextBuilder {
425425
/// @return Reference to this builder for method chaining.
426426
ReadContextBuilder& WithCacheConfig(const CacheConfig& config);
427427

428-
/// Set how far the reader prepares the next file before it is read.
428+
/// Set whether the reader fetches the next file's raw bytes before it is read.
429429
///
430-
/// Warmup overlaps remote-storage latency with the read of the current file. Higher levels hide
431-
/// more latency but use more memory, and may warm files that a query never reads (for example
432-
/// when a LIMIT stops the scan early).
430+
/// Warmup can overlap remote-storage latency with reading the current file. It may fetch bytes
431+
/// for files that a query never reads, for example when a LIMIT stops the scan early.
433432
/// @param level The warmup level to use (default: WarmupLevel::RAW).
434433
/// @return Reference to this builder for method chaining.
435434
/// @note WarmupLevel::RAW warms the read-ahead cache, so it has no effect and behaves like
436435
/// WarmupLevel::NONE when the cache is off (see SetReadAheadCacheEnabled()).
437-
/// WarmupLevel::DECODED does not depend on the cache.
438-
/// Both levels work with EnablePrefetch(false). Without prefetch, DECODED prepares only the
439-
/// first batch; subsequent batches are read synchronously.
436+
/// RAW also works with EnablePrefetch(false) and never starts batch decoding.
440437
/// @see WarmupLevel
441438
ReadContextBuilder& SetWarmupLevel(WarmupLevel level);
442439

‎include/paimon/reader/file_batch_reader.h‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,8 @@ class PAIMON_EXPORT FileBatchReader : public BatchReader {
6464
/// Get whether or not support read precisely while bitmap pushed down.
6565
virtual bool SupportPreciseBitmapSelection() const = 0;
6666

67-
/// Starts whatever background work this reader would otherwise start on its first read, so a
68-
/// caller that knows this reader is next can pay that startup while still consuming the
69-
/// previous one.
67+
/// Fetches this file's raw bytes ahead of its first read, so a caller that knows this reader
68+
/// is next can overlap the fetch with consuming the previous file.
7069
///
7170
/// This is an optional hint and never changes what the reader returns: ordering, filtering and
7271
/// metrics are the same with or without it. A reader with nothing to start, or one that is not
@@ -78,8 +77,8 @@ class PAIMON_EXPORT FileBatchReader : public BatchReader {
7877
/// that cannot start its work leaves it to be started by the first read, which reports the
7978
/// failure itself.
8079
///
81-
/// What an implementation starts is up to the implementation - a reader that prefetches on a
82-
/// background thread starts that thread, one that reads synchronously may do nothing at all.
80+
/// A reader may fetch through its read-ahead cache using the filesystem's asynchronous I/O.
81+
/// Warmup does not start batch decoding. A reader without byte-warmup support does nothing.
8382
/// @warning Whatever it starts, the call itself is not thread-safe: make it from the same
8483
/// thread that reads this reader, and not concurrently with any other call on it.
8584
virtual void Warmup() {}

‎include/paimon/utils/prefetch_cache_config.h‎

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -124,27 +124,18 @@ class PAIMON_EXPORT CacheConfig {
124124
uint64_t block_cache_limit_ = 1024 * 1024;
125125
};
126126

127-
/// Controls how far a reader prepares the next file before that file is actually read.
127+
/// Controls whether a reader fetches the next file's raw bytes before that file is read.
128128
///
129-
/// Warmup overlaps remote-storage latency with the read of the current file. Each level takes the
130-
/// next file one step further along the read pipeline: `RAW` covers the remote fetch, `DECODED`
131-
/// adds the decode on top of it. Higher levels hide more latency, but commit more memory and
132-
/// background I/O to files that a query may end up never reading (for example when a LIMIT stops
133-
/// the scan early). Callers can trade latency against memory by picking a level.
129+
/// Warmup can overlap remote-storage latency with reading the current file. It may fetch bytes
130+
/// for a file a query never consumes, for example when a LIMIT stops the scan early.
134131
enum class PAIMON_EXPORT WarmupLevel {
135132
/// Do not warm up. The next file's I/O starts only when it is actually read. This is the
136133
/// behavior from before warmup existed and uses no extra memory or background threads.
137134
NONE,
138135
/// Fetch only the next file's raw, still-compressed bytes into memory, and leave the decoder
139-
/// alone. Overlaps the remote fetch while keeping memory lower than `DECODED`, because no
140-
/// decoded batches are materialized ahead of the read. It fetches through the read-ahead
141-
/// cache, so it falls back to `NONE` when that cache is disabled. This is the default, as it
142-
/// hides the remote fetch without committing memory to decoded batches.
136+
/// alone. No decoded batches are materialized ahead of the read. It uses the read-ahead cache,
137+
/// so it falls back to `NONE` when that cache is disabled. This is the default level.
143138
RAW,
144-
/// Fetch the raw bytes and start the background decode loop as well, so decoded batches are
145-
/// ready before the file is read. Hides the most latency but uses the most memory.
146-
/// Without batch prefetch, prepares only the first batch and then continues synchronously.
147-
DECODED,
148139
};
149140

150141
} // namespace paimon

‎src/paimon/common/file_index/bitmap/apply_bitmap_index_batch_reader_test.cpp‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,7 @@ class ApplyBitmapIndexBatchReaderTest : public ::testing::Test,
128128
/*enable_adaptive_prefetch_strategy=*/false, executor_,
129129
/*initialize_read_ranges=*/true,
130130
/*read_ahead_cache_enabled=*/true, CacheConfig(),
131-
/*enable_io_metrics=*/false, WarmupLevel::DECODED, pool_,
132-
GetArrowPool(pool_)));
131+
/*enable_io_metrics=*/false, WarmupLevel::RAW, pool_, GetArrowPool(pool_)));
133132
} else {
134133
file_batch_reader =
135134
std::make_unique<MockFileBatchReader>(data, target_type_, batch_size);

‎src/paimon/common/reader/data_file_reader_factory.cpp‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ bool FormatSupportsPrefetch(const std::string& format_identifier) {
4848
format_identifier != "vortex";
4949
}
5050

51-
bool FormatSupportsSynchronousWarmup(const std::string& format_identifier) {
51+
bool FormatSupportsReadAhead(const std::string& format_identifier) {
5252
// Other format plugins may expose only FileBatchReader. Keep their plain read path intact.
5353
return format_identifier == "parquet" || format_identifier == "orc";
5454
}
@@ -79,7 +79,7 @@ Result<std::unique_ptr<ReaderBuilder>> DataFileReaderFactory::CreateReaderBuilde
7979
read_hints.read_ahead_cache_enabled =
8080
read_options.read_ahead_cache_enabled &&
8181
((read_options.prefetch_enabled && FormatSupportsPrefetch(format_identifier)) ||
82-
FormatSupportsSynchronousWarmup(format_identifier));
82+
FormatSupportsReadAhead(format_identifier));
8383
reader_builder->WithReadHints(read_hints);
8484
return reader_builder;
8585
}
@@ -102,7 +102,7 @@ Result<std::unique_ptr<FileBatchReader>> DataFileReaderFactory::Open(
102102
read_options.warmup_level, pool, arrow_pool));
103103
return std::make_unique<DelegatingPrefetchReader>(std::move(prefetch_reader));
104104
}
105-
if (FormatSupportsSynchronousWarmup(format_identifier)) {
105+
if (FormatSupportsReadAhead(format_identifier)) {
106106
PAIMON_ASSIGN_OR_RAISE(
107107
FileReadAhead::OpenedReaders opened,
108108
FileReadAhead::Open(file_path, file_size, reader_builder, file_system,

‎src/paimon/common/reader/data_file_reader_factory_test.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ TEST(DataFileReaderFactoryTest, OpenForwardsWarmupLevelToPrefetchReader) {
6868
data_type->fields())
6969
.ValueOrDie();
7070

71-
for (WarmupLevel level : {WarmupLevel::NONE, WarmupLevel::RAW, WarmupLevel::DECODED}) {
71+
for (WarmupLevel level : {WarmupLevel::NONE, WarmupLevel::RAW}) {
7272
MockFormatReaderBuilder reader_builder(data, data_type, /*read_batch_size=*/10);
7373
DataFileReadOptions read_options;
7474
read_options.read_batch_size = 10;

‎src/paimon/common/reader/late_materializing_file_batch_reader_test.cpp‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,7 @@ TEST_F(LateMaterializingFileBatchReaderTest, WorksAsInnerOfPrefetchReader) {
706706
/*prefetch_max_parallel_num=*/1, /*batch_size=*/3, /*prefetch_batch_count=*/2,
707707
/*enable_adaptive_prefetch_strategy=*/false, executor,
708708
/*initialize_read_ranges=*/false, /*read_ahead_cache_enabled=*/false, CacheConfig(),
709-
/*enable_io_metrics=*/false, WarmupLevel::DECODED, pool_, GetArrowPool(pool_)));
709+
/*enable_io_metrics=*/false, WarmupLevel::RAW, pool_, GetArrowPool(pool_)));
710710
auto predicate =
711711
PredicateBuilder::GreaterOrEqual(/*field_index=*/0, "k", FieldType::BIGINT, Literal(4l));
712712
::ArrowSchema c_schema;
@@ -742,7 +742,7 @@ TEST_F(LateMaterializingFileBatchReaderTest, PrefetchInnerReentrantSetReadSchema
742742
/*prefetch_max_parallel_num=*/1, /*batch_size=*/3, /*prefetch_batch_count=*/2,
743743
/*enable_adaptive_prefetch_strategy=*/false, executor,
744744
/*initialize_read_ranges=*/false, /*read_ahead_cache_enabled=*/false, CacheConfig(),
745-
/*enable_io_metrics=*/false, WarmupLevel::DECODED, pool_, GetArrowPool(pool_)));
745+
/*enable_io_metrics=*/false, WarmupLevel::RAW, pool_, GetArrowPool(pool_)));
746746

747747
auto full_schema = arrow::schema(full_fields_);
748748
auto predicate1 =
@@ -805,7 +805,7 @@ TEST_F(LateMaterializingFileBatchReaderTest, PrefetchInnerParallelReadersWithSee
805805
/*prefetch_max_parallel_num=*/3, /*batch_size=*/3, /*prefetch_batch_count=*/6,
806806
/*enable_adaptive_prefetch_strategy=*/false, executor,
807807
/*initialize_read_ranges=*/false, /*read_ahead_cache_enabled=*/false, CacheConfig(),
808-
/*enable_io_metrics=*/false, WarmupLevel::DECODED, pool_, GetArrowPool(pool_)));
808+
/*enable_io_metrics=*/false, WarmupLevel::RAW, pool_, GetArrowPool(pool_)));
809809
auto predicate =
810810
PredicateBuilder::GreaterOrEqual(/*field_index=*/0, "k", FieldType::BIGINT, Literal(5l));
811811
::ArrowSchema c_schema;
@@ -960,7 +960,7 @@ TEST_F(LateMaterializingFileBatchReaderTest, PrefetchInnerRegistersPayloadPreBuf
960960
/*prefetch_max_parallel_num=*/1, /*batch_size=*/3, /*prefetch_batch_count=*/2,
961961
/*enable_adaptive_prefetch_strategy=*/false, executor,
962962
/*initialize_read_ranges=*/false, /*read_ahead_cache_enabled=*/true, CacheConfig(),
963-
/*enable_io_metrics=*/false, WarmupLevel::DECODED, pool_, GetArrowPool(pool_)));
963+
/*enable_io_metrics=*/false, WarmupLevel::RAW, pool_, GetArrowPool(pool_)));
964964
auto predicate =
965965
PredicateBuilder::GreaterOrEqual(/*field_index=*/0, "k", FieldType::BIGINT, Literal(4l));
966966
::ArrowSchema c_schema;

‎src/paimon/common/reader/prefetch_file_batch_reader_impl.cpp‎

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ PrefetchFileBatchReaderImpl::DispatchReadRanges(
284284
}
285285

286286
Status PrefetchFileBatchReaderImpl::CleanUp() {
287-
synchronous_reader_->ResetWarmup();
287+
synchronous_reader_->ResetReadState();
288288
auto clean_prefetch_queue = [this]() {
289289
for (auto& prefetch_queue : prefetch_queues_) {
290290
while (true) {
@@ -336,9 +336,8 @@ Status PrefetchFileBatchReaderImpl::CleanUp() {
336336
void PrefetchFileBatchReaderImpl::Workloop() {
337337
std::vector<std::future<void>> futures;
338338
futures.resize(readers_.size());
339-
// Warm the read-ahead cache before decoding. At DECODED this is the first warmup; at RAW
340-
// Warmup() already ran it on the caller's thread and the guard makes this a no-op, so the
341-
// cache is never Init'd twice.
339+
// Register the file's byte ranges before decoding. An earlier RAW Warmup() may have already
340+
// fetched them, in which case the shared state makes this a no-op.
342341
WarmCacheOnce();
343342

344343
while (true) {
@@ -587,7 +586,7 @@ void PrefetchFileBatchReaderImpl::ClearPreBufferRangeCallbacks() {
587586
}
588587

589588
void PrefetchFileBatchReaderImpl::Warmup() {
590-
if (warmup_level_ == WarmupLevel::NONE) {
589+
if (warmup_level_ != WarmupLevel::RAW) {
591590
return;
592591
}
593592
// Ranges that are not set mean the reader is either not configured yet or already cleaned up,
@@ -597,18 +596,7 @@ void PrefetchFileBatchReaderImpl::Warmup() {
597596
if (!read_ranges_freshed_) {
598597
return;
599598
}
600-
if (!need_prefetch_) {
601-
synchronous_reader_->Warmup();
602-
return;
603-
}
604-
if (warmup_level_ == WarmupLevel::RAW) {
605-
// Prefetch the raw bytes only, without starting the decoder. The first real read starts the
606-
// background thread, whose Workloop() finds the cache already warmed and skips re-Init.
607-
WarmCacheOnce();
608-
return;
609-
}
610-
// DECODED: start the background decode loop now so decoded batches are ready before the read.
611-
EnsureBackgroundThread();
599+
WarmCacheOnce();
612600
}
613601

614602
Result<BatchReader::ReadBatchWithBitmap> PrefetchFileBatchReaderImpl::NextBatchWithBitmap() {

‎src/paimon/common/reader/prefetch_file_batch_reader_impl.h‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,7 @@ class PrefetchFileBatchReaderImpl : public PrefetchFileBatchReader {
132132

133133
Status CleanUp();
134134
void Workloop();
135-
/// Starts the background prefetch thread if it is not running yet. The first read does this
136-
/// itself; Warmup() is the same call made earlier, so the two must not diverge.
135+
/// Starts the background batch prefetch thread lazily on the first read.
137136
void EnsureBackgroundThread();
138137
/// Shares byte-range registration with the synchronous warmup path.
139138
void WarmCacheOnce();

0 commit comments

Comments
 (0)