github-actions[bot] commented on code in PR #67087:
URL: https://github.com/apache/doris/pull/67087#discussion_r3852313611


##########
be/src/exec/sink/writer/iceberg/viceberg_sort_writer.cpp:
##########
@@ -82,7 +83,21 @@ size_t VIcebergSortWriter::data_size() const {
 
 size_t VIcebergSortWriter::get_reserve_mem_size(RuntimeState* state, bool eos) 
const {
     std::lock_guard<std::mutex> lock(_sorter_mutex);
-    return _sorter == nullptr ? 0 : _sorter->get_reserve_mem_size(state, eos);
+    size_t reserve_size = _sorter == nullptr ? 0 : 
_sorter->get_reserve_mem_size(state, eos);
+    if (eos && !_sorted_spill_files.empty()) {
+        const size_t buffer_size = 
static_cast<size_t>(state->spill_buffer_size_bytes());
+        const size_t merge_limit = 
static_cast<size_t>(state->spill_sort_merge_mem_limit_bytes());
+        const size_t max_fan_in = std::max<size_t>(2, merge_limit / 
buffer_size);
+        // Reservation is computed before sink(), so a non-empty EOS can add 
one final spill run.
+        const size_t selected_streams = std::min(_sorted_spill_files.size(), 
max_fan_in - 1) + 1;
+        // Every selected cursor eagerly owns one input block. A multiway 
merge also builds a
+        // separate output block while those inputs remain live.
+        const size_t merge_buffer_count = selected_streams + (selected_streams 
> 1 ? 1 : 0);

Review Comment:
   **[P1] Include merger workspace in the EOS reservation**
   
   With the default 8 MiB spill buffer and one-byte rows, the intermediate 
merger receives a batch of 8,388,608 rows. `VSortedRunMerger::get_next()` 
reserves both its index and block-address vectors to that count, so those two 
vectors alone allocate about 128 MiB while this formula requests only 72 MiB 
for the default eight-way merge. Every eagerly prepared cursor also retains its 
reader's serialized buffer and a deserialized input block. Under memory 
pressure the synchronous EOS close can therefore exceed a reservation that was 
successfully granted. Please bound/count the row-selection and retained 
reader/input/output workspace, and cover a real intermediate merge rather than 
only the arithmetic helper.



##########
be/src/exec/sink/writer/iceberg/viceberg_table_writer.cpp:
##########
@@ -221,6 +219,34 @@ Status VIcebergTableWriter::write_prepared_block(Block& 
block) {
     return _write_prepared_block(block);
 }
 
+size_t VIcebergTableWriter::get_reserve_mem_size(RuntimeState* state, bool 
eos) const {
+    size_t reserve_size = state->minimum_operator_memory_required_bytes();
+    if (!eos) {
+        auto current_writer = _current_writer.load();
+        if (!current_writer) {
+            return reserve_size;
+        }
+        auto* sort_writer = 
dynamic_cast<VIcebergSortWriter*>(current_writer.get());
+        DORIS_CHECK(sort_writer != nullptr);
+        return std::max(reserve_size, sort_writer->get_reserve_mem_size(state, 
false));
+    }
+
+    auto partition_writer_snapshot = 
std::make_shared<PartitionWriterSnapshot>();
+    partition_writer_snapshot->reserve(_partitions_to_writers.size());
+    // close() finalizes partition writers sequentially, so reserve the 
largest close peak rather
+    // than the sum. Publish the same owning set for workload 
accounting/revoke if reservation
+    // fails. The admission floor covers a first non-empty EOS before its 
first writer exists.

Review Comment:
   **[P1] Reserve for partitions created by the pending block**
   
   This estimate only visits writers that already exist, but it runs before 
`sink()` consumes the pending block. On the first non-empty EOS, or when the 
block introduces a new partition, the reservation is therefore only the fixed 
admission floor; that same call then materializes/filters the block, creates a 
sorter, appends the rows, and immediately finalizes it. The production 32,000 
KiB floor is not tied to a legal adaptive block (which can target up to 512 
MiB) or its sort workspace. Please account for the pending block and possible 
new destinations, or split ingestion from finalization and recompute after 
those writers exist.



##########
be/src/exec/sink/writer/iceberg/viceberg_sort_writer.cpp:
##########
@@ -58,9 +58,10 @@ Status VIcebergSortWriter::open(RuntimeState* state, 
RuntimeProfile* profile,
 Status VIcebergSortWriter::write(Block& block) {
     std::lock_guard<std::mutex> lock(_sorter_mutex);
 
+    // FullSorter consumes the input block, so derive the spill batch size 
before append_block().
+    _update_spill_block_batch_row_count(block);

Review Comment:
   **[P1] Keep spill batches byte-bounded as row widths change**
   
   This one-shot sample permanently fixes the spill row count from the first 
non-empty block. If later variable-length rows are wider, `_do_spill()` still 
reads that many rows and has no byte stop, so a narrow first block can turn the 
configured 8 MiB spill buffer into a block hundreds of MiB large during memory 
revocation. The final-merger clamp does not affect this spill or 
intermediate-merge path, and the uniform-width helper test cannot expose the 
skew. Please make these reads byte-bounded (or continually lower the row limit 
from observed/max widths) and test a narrow-first, wide-later actual spill.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to