HappenLee commented on code in PR #67140:
URL: https://github.com/apache/doris/pull/67140#discussion_r4024803999
##########
be/src/exec/operator/distinct_streaming_aggregation_operator.cpp:
##########
@@ -176,8 +195,13 @@ 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));
+ if (low_memory_mode() || (memory_limit > 0 && _memory_usage() >
memory_limit)) {
+ _stop_emplace_flag = true;
Review Comment:
Please address this distinct recovery path before merging. At the current
head, `7d172fc7fcca0976466ef044672014b92b271d1b`, a temporary budget breach
still permanently sets `_stop_emplace_flag`, so restoring the live budget
cannot restore local deduplication.
The workload-group low-memory switch does not cover every case. With the
DYNAMIC slot-memory policy and hard limits enabled, additional active query
slots can reduce this query's budget while the workload group remains below its
low watermark. When those queries finish, the budget can recover without
low-memory mode ever being set.
For example, with 8 parallel tasks and a retained hash set using 64 MiB per
task:
| Query memory limit | Per-task pre-aggregation budget | Current behavior |
| --- | --- | --- |
| 8 GiB | About 204.8 MiB | Deduplicate normally |
| Temporarily reduced to 1 GiB | 32 MiB | Set the permanent stop flag and
pass through |
| Restored to 8 GiB | About 204.8 MiB | Still pass through, although the
retained state is below budget |
For a long, duplicate-heavy query, this turns a temporary reduction into
additional exchange traffic and downstream work for the entire remaining
execution. Raising the budget or its floor only reduces the chance of
triggering the issue; it does not fix recovery.
Please separate the temporary, per-block budget decision from the existing
permanent stops for low reduction and low-memory mode. Preserve the retained
hash set and resume deduplication when the budget permits, provided no
permanent stop was triggered. Do not simply clear `_stop_emplace_flag` on
recovery, since it also represents those other stop reasons.
Please also carry the effective pass-through state consistently through
output/buffering and pushed-down LIMIT handling. In particular, raw rows
emitted during the temporary pass-through must not later consume the distinct
LIMIT allowance when deduplication resumes: `pull()` currently increments
`_num_rows_returned` for every output row, and `push()` consults that counter
when enforcing the limit.
Add lower-then-restore tests, both with and without a pushed-down LIMIT,
that verify:
- The low-budget block passes through without growing the hash set.
- After restoration, previously retained keys are filtered again and new
keys can be inserted.
- Earlier pass-through duplicates do not cause premature LIMIT/EOS after
recovery.
- Stops caused by low reduction or low-memory mode retain their existing
semantics.
This remains a performance/recovery issue in the current code. The LIMIT
points above are requirements for implementing recovery without reintroducing
the earlier correctness bug.
--
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]