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


##########
be/src/exec/operator/distinct_streaming_aggregation_operator.cpp:
##########
@@ -176,8 +195,17 @@ Status 
DistinctStreamingAggLocalState::_distinct_pre_agg_with_serialized_key(
     const uint32_t rows = (uint32_t)in_block->rows();
     _distinct_row.clear();
 
-    if (_parent->cast<DistinctStreamingAggOperatorX>()._is_streaming_preagg && 
low_memory_mode()) {
-        _stop_emplace_flag = true;
+    auto& parent = _parent->cast<DistinctStreamingAggOperatorX>();
+    if (parent._is_streaming_preagg) {
+        const auto memory_limit = parent._memory_limit(state());
+        COUNTER_SET(_memory_use_limit, static_cast<int64_t>(memory_limit));
+        // A LIMIT pushed down to this stage bounds the hash set by itself and 
must keep
+        // deduplicating: pass-through rows would otherwise consume the limit 
with duplicates
+        // (see push()).
+        if (low_memory_mode() ||
+            (parent.limit() == -1 && memory_limit > 0 && _memory_usage() > 
memory_limit)) {

Review Comment:
   [P1] Keep the byte cap active when a LIMIT is pushed down. The LIMIT is 
enforced only after `_emplace_into_hash_table_to_distinct()` has inserted the 
entire block, so it does not bound the retained hash set or arena: with `LIMIT 
1` and one block of many unique large strings, `lazy_emplace_batch_void()` 
copies every key before `push()` truncates the output, and this non-spillable 
state can exhaust the query tracker despite `MemoryUseLimit`. Let the existing 
pass-through/no-local-truncation logic handle a cap breach (or stop emplacement 
at the remaining limit/budget), and cover variable-length keys.



##########
be/src/exec/operator/streaming_aggregation_operator.cpp:
##########
@@ -1106,14 +1130,18 @@ Status StreamingAggOperatorX::pull(RuntimeState* state, 
Block* block, bool* eos)
     auto& local_state = get_local_state(state);
     SCOPED_PEAK_MEM(&local_state._estimate_memory_usage);
     if (!local_state._pre_aggregated_block->empty()) {
+        // Pass-through rows are neither aggregated nor deduplicated, so a 
limit pushed down to
+        // this local stage must not count them: duplicates would consume the 
allowance and stop
+        // the child before enough distinct keys reached the global stage, 
which applies the
+        // limit again on the final result.
         local_state._pre_aggregated_block->swap(*block);
     } else {
         RETURN_IF_ERROR(local_state._get_results_with_serialized_key(state, 
block, eos));
         local_state.make_nullable_output_key(block);
         // dispose the having clause, should not be execute in prestreaming agg
         RETURN_IF_ERROR(local_state.filter_block(local_state._conjuncts, 
block));
+        local_state.reached_limit(block, eos);

Review Comment:
   [P2] Preserve process-row accounting for the pass-through branch. 
`reached_limit()` also increments the query `IOContext::process_rows`; after 
moving it only here, every raw block returned above for a cap or low-reduction 
fallback disappears from `TQueryStatistics.process_rows`. Split that accounting 
from local LIMIT truncation, or explicitly update `process_rows` for 
pass-through rows, and extend the recovery test to assert it while keeping `eos 
== false`.



##########
be/src/exec/operator/distinct_streaming_aggregation_operator.cpp:
##########
@@ -67,9 +72,23 @@ Status DistinctStreamingAggLocalState::open(RuntimeState* 
state) {
         RETURN_IF_ERROR(p._probe_expr_ctxs[i]->clone(state, 
_probe_expr_ctxs[i]));
     }
     RETURN_IF_ERROR(_init_hash_method(_probe_expr_ctxs));
+    COUNTER_SET(_memory_use_limit, 
static_cast<int64_t>(p._memory_limit(state)));
     return Status::OK();
 }
 
+size_t DistinctStreamingAggLocalState::_memory_usage() const {
+    size_t usage = _arena.size();

Review Comment:
   [P1] Count the hash method's retained serialization scratch here. 
Composite-key probing fills `MethodSerialized::Base::arena`, `stored_keys`, and 
`hash_values` for the whole block before deduplication, and `Arena::clear()` 
retains its largest head chunk. A duplicate-heavy 4096-row block with roughly 2 
KiB serialized keys can therefore leave about 8 MiB retained while the 
persistent distinct set contains one key; this helper still reports below a 4 
MiB `MemoryUseLimit`, so later blocks keep aggregating. Include the method's 
retained buffers (or release oversized scratch) and cover duplicate composite 
keys.



-- 
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