You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by "924060929 (via GitHub)" <gi...@apache.org> on 2026/05/18 10:24:45 UTC
[PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
924060929 opened a new pull request, #63366:
URL: https://github.com/apache/doris/pull/63366
Previously, local exchange (LE) nodes were inserted exclusively by the
BE's `_plan_local_exchange` at pipeline build time. The FE had no
visibility into which operators needed a fan-out or shuffle before
execution, making it impossible to validate, optimize, or override LE
decisions at planning time.
This PR introduces a full FE-side local exchange planner that mirrors
BE semantics, brings several correctness fixes, and leaves the legacy
BE path fully intact behind a feature flag. See "Current architecture
notes" at the bottom for what the FE planner does and does not own.
A new `AddLocalExchange` pass runs after normal fragment assignment.
It walks each fragment's plan tree bottom-up, calling the polymorphic
`PlanNode.enforceAndDeriveLocalExchange()` on every node. Nodes
declare what distribution they require of their children; the framework
inserts `LocalExchangeNode` where needed.
`LocalExchangeNode` represents intra-fragment data redistribution and
supports the full set of exchange types: PASSTHROUGH, HASH_SHUFFLE,
BUCKET_HASH_SHUFFLE, GLOBAL_EXECUTION_HASH_SHUFFLE, BROADCAST,
PASS_TO_ONE, ADAPTIVE_PASSTHROUGH, LOCAL_MERGE_SORT, and NOOP.
The pass is guarded by `enable_local_shuffle_planner` (default true).
When disabled, BE continues to run its own `_plan_local_exchange` as
before, keeping the old path fully intact.
`maxPerBeInstances` (max pipeline instances assigned to any single BE)
is used instead of a global `instanceCount`. Planning is a no-op when
`maxPerBeInstances == 1` — inserting LE on a single-threaded pipeline
would cause task-count mismatches and pipeline starvation.
When a serial operator (e.g. OlapScanNode with a single tablet bucket)
feeds a non-serial parent without an intermediate LE, downstream tasks
starve waiting for data that never arrives. The framework detects this
case and inserts a PASSTHROUGH LE to restore N-task parallelism, exactly
matching BE's `required_data_distribution()` serial → PASSTHROUGH rule.
`LocalExchangeTypeRequire` abstracts two strategies:
- `RequireHash` — always resolves to `LOCAL_EXECUTION_HASH_SHUFFLE`
(safe for intra-fragment hash partitioning).
- `RequireSpecific` — preserves BUCKET_HASH_SHUFFLE /
GLOBAL_EXECUTION_HASH_SHUFFLE without degradation.
PR #62438 added `enable_local_exchange_before_agg`, but its BE guard
`!_needs_finalize && !enable_local_exchange_before_agg → base` conflated
two semantically different cases in AggSink and DistinctStreamingAgg:
- **AggSink**: `!finalize && hasKeys` covered both LOCAL preagg
(performance-only) and FIRST_MERGE dedup (correctness-critical).
The flag-gated early-return wrongly skipped HASH for FIRST_MERGE,
producing PASSTHROUGH-over-serial-child → wrong aggregation results.
- **DistinctStreamingAgg**: `!finalize` covered both streaming preagg
(`useStreamingPreagg=true`, performance) and non-streaming dedup
(`useStreamingPreagg=false`, correctness). Same class of bug.
FE fix:
- AggSink: restrict the flag-gated base path to `!isMerge()` LOCAL
phases. FIRST_MERGE always emits HASH regardless of the flag.
- DistinctStreamingAgg: restrict to `useStreamingPreagg=true`.
Non-streaming dedup always emits HASH.
Also add `requiresShuffleForCorrectness()` to mirror BE's
`is_shuffled_operator()`, so SetOperationNode propagates the
"downstream depends on hash" flag correctly instead of using the
coarser `parentRequire.preferType().isHashShuffle()` check that
over-inserted HASH LE on every union branch under a streaming preagg.
These fixes reduce FE/BE consistency mismatches from 8 to 3
(only pre-existing NLJ optimization differences remain).
- `enable_local_shuffle_planner` — use FE planner (default true)
- `enable_local_shuffle` — master switch for local shuffle
- `enable_local_exchange_before_agg` — HASH LE before non-final agg
(default true, mirrors apache/doris#62438)
`validateNoSerialWithoutLocalExchange()` walks the final plan tree and
logs a warning whenever a serial operator feeds a non-serial parent
without an intermediate LocalExchangeNode, catching planning gaps
before execution.
- `test_enable_local_exchange_before_agg.groovy` — 10 agg patterns
with the flag on and off; covers the FIRST_MERGE and
DistinctStreamingAgg correctness fixes.
- `test_local_shuffle_fe_be_consistency.groovy` — runs the same SQL
with `enable_local_shuffle_planner=true` and `=false` across the
full operator matrix (Agg, Sort, Analytic, HashJoin, NLJ, Set, Union,
TableFunction, AssertNumRows, RQG-derived corner cases) and asserts
result rows are identical. Only data correctness is asserted — the
two planners legitimately differ on the exact exchange counts/types
they emit, so plan-shape equality is intentionally not checked.
- `test_local_shuffle_rqg_bugs.groovy` — reproduces 20+ RQG-found
crashes and wrong-result cases.
- `test_old_coordinator_local_shuffle.groovy` — verifies the old
coordinator path is unaffected.
- `test_multilevel_join_agg_local_shuffle.groovy` — multi-level join
and aggregation plan shapes.
- `multi_version.h`: replace `atomic_load/atomic_store` (deprecated in
libstdc++ C++20 / LLVM 20) with `std::shared_mutex`-based RW locking.
- `memory.cpp`: fix `std::max` type mismatch (`long` vs `int64_t`)
on macOS.
- `bucketed_aggregation_sink_operator.h`: fix `ExchangeType::NOOP` →
`TLocalPartitionType::NOOP` after thrift enum rename.
This PR puts the FE planner in the driver's seat for LE insertion but
intentionally does NOT remove the BE-side machinery — readers should be
aware of three pieces the FE planner shares with or defers to BE:
1. **`is_serial_operator` is computed on both sides.** FE computes the
flag and writes it into Thrift, but BE's
`OperatorBase::is_serial_operator()` is still overridden per operator
in C++ and used for BE-side runtime decisions. Any future change to
the BE override needs to be mirrored on the FE side (and vice versa)
to keep the planner's view consistent with execution.
2. **The legacy BE planner stays as a fallback.**
`pipeline_fragment_context.cpp::_plan_local_exchange` is preserved
and gated by `runtime_state.h::plan_local_shuffle()`: when
`enable_local_shuffle_planner=false`, BE plans LE itself, exactly as
before. The two paths are mutually exclusive, never both running on
the same query.
3. **`_propagate_local_exchange_num_tasks` is kept as a runtime safety
net.** The two propagation passes in
`pipeline_fragment_context.cpp` fix up paired pipelines whose
`num_tasks` end up mismatched (e.g. when AGG/SORT/JOIN pipeline
splits leave a serial Exchange feeding an N-task sink). FE's
framework-level serial→non-serial fan-out (`enforceRequire` step 3)
and the `validateNoSerialWithoutLocalExchange` check aim to make
these mismatches impossible by construction, but the BE-side fixup
remains as a defensive guard.
Co-authored-by: Gabriel <li...@selectdb.com>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4479137591
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/15d92ba024b265d9441145d7bd26d93590aa9c63_15d92ba024b265d9441145d7bd26d93590aa9c63_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/15d92ba024b265d9441145d7bd26d93590aa9c63_15d92ba024b265d9441145d7bd26d93590aa9c63_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 78.06% (1854/2375) |
| Line Coverage | 64.52% (33325/51653) |
| Region Coverage | 65.21% (16520/25335) |
| Branch Coverage | 55.70% (8827/15848) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4485186216
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4496936392
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4491773872
# FE Regression Coverage Report
Increment line coverage ` 80.85% (477/590)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_4e05e7bfbb8aace37eb65198a1530e9325047d34_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_4e05e7bfbb8aace37eb65198a1530e9325047d34_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4489014728
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4550839649
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/a68f47c588b0124cf5474ff805c847df5dfc781e_a68f47c588b0124cf5474ff805c847df5dfc781e_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/a68f47c588b0124cf5474ff805c847df5dfc781e_a68f47c588b0124cf5474ff805c847df5dfc781e_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 78.06% (1854/2375) |
| Line Coverage | 64.52% (33333/51665) |
| Region Coverage | 65.18% (16519/25343) |
| Branch Coverage | 55.72% (8834/15854) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4671961348
### Correction on the [P1] throw (AddLocalExchange)
My earlier reply claimed the `throw` "never misfires" — that was wrong, and pushing it proved it. The PR's P0 / cloud_p0 runs surfaced **9 distinct legitimate patterns** where the throw fires but the plan is correct:
| pattern | tests hit |
|---|---|
| `Serial UnionNode → non-serial TableFunctionNode` | unnest_join_list, unnest_where_list, salt_join, skew_join, correlated_scalar_subquery |
| `Serial UnionNode → non-serial AggregationNode` | pull_up_predicate_set_op |
| `Serial RecursiveCteScanNode → non-serial NLJ / Agg` | rec_cte, rec_cte_from_mysql_doc |
| `Serial OlapScanNode → non-serial HashJoinNode` | test_crossjoin_inlineview_slot |
In all of these the parent runs **effectively serial** at runtime and the BE reconciles `num_tasks` across the coupled pipelines, so no LocalExchange is required and the results are correct (they all pass with the `warn`). This validation compares FE-static `isSerialOperatorOnBe`, which can't model that runtime reconciliation — so it is **not a hard invariant**.
I've reverted it to a `warn` (best-effort heuristic) with a comment explaining why a hard throw is unsafe here. Trying the throw was still worthwhile — it's exactly what surfaced these cases.
### Also fixed from the same CI run
- **`force_passthrough` NPE:** `getConnectContext()` is null under the planner unit-test mocks, so the new `enableBroadcastJoinForcePassthrough` read NPE'd in `testHashJoinNodeBranches`. Made it null-safe (treat null context as "no force").
- **clang-format:** `agg_operator_test.cpp` had a >100-col line (v16).
(The remaining P0/cloud_p0 reds are pre-existing/flaky and unrelated to local shuffle — variant `element_at` logical-planner slot errors, a json_extract `IndexOutOfBounds`, a float→bigint cast overflow, a temp-table case, and one `SUCCESS vs RUNNING` timeout. The BE-UT red is a CI git-mirror checkout failure on the agent.)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4675192636
<details>
<summary>TPC-DS: <b>Total hot run time: 169893 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit f056ebbed42446ea468bcc6891c05520bdfb31c3, data reload: false
query5 4328 673 482 482
query6 431 212 183 183
query7 4921 597 315 315
query8 371 221 219 219
query9 8762 4057 4044 4044
query10 466 323 261 261
query11 5922 2385 2229 2229
query12 164 107 103 103
query13 1347 616 492 492
query14 6217 5444 5041 5041
query14_1 4410 4395 4375 4375
query15 207 200 172 172
query16 1014 462 451 451
query17 1105 708 568 568
query18 2579 491 341 341
query19 205 187 139 139
query20 112 118 114 114
query21 214 141 116 116
query22 13649 13636 13522 13522
query23 17275 16513 16075 16075
query23_1 16293 16170 16295 16170
query24 7482 1826 1346 1346
query24_1 1358 1308 1325 1308
query25 602 494 416 416
query26 1314 327 167 167
query27 2695 573 352 352
query28 4460 2082 2060 2060
query29 1126 671 519 519
query30 324 244 201 201
query31 1137 1099 963 963
query32 115 72 60 60
query33 529 345 275 275
query34 1190 1230 671 671
query35 798 816 719 719
query36 1394 1364 1282 1282
query37 162 109 98 98
query38 3172 3154 3046 3046
query39 957 928 900 900
query39_1 882 867 869 867
query40 253 131 108 108
query41 73 79 74 74
query42 102 97 97 97
query43 326 334 283 283
query44
query45 202 193 190 190
query46 1141 1247 772 772
query47 2384 2407 2292 2292
query48 408 400 289 289
query49 643 511 372 372
query50 1069 372 260 260
query51 4472 4395 4419 4395
query52 92 102 79 79
query53 261 279 195 195
query54 284 239 216 216
query55 81 83 72 72
query56 264 253 233 233
query57 1434 1441 1352 1352
query58 261 230 224 224
query59 1600 1672 1434 1434
query60 300 268 280 268
query61 163 164 155 155
query62 717 655 593 593
query63 228 191 190 190
query64 2580 813 648 648
query65
query66 1779 500 357 357
query67 29676 29675 29525 29525
query68
query69 426 311 269 269
query70 963 974 948 948
query71 309 235 213 213
query72 2999 2750 2461 2461
query73 828 778 451 451
query74 5118 4968 4804 4804
query75 2794 2666 2231 2231
query76 2299 1226 813 813
query77 388 393 289 289
query78 12435 12548 11897 11897
query79 1487 1125 775 775
query80 1292 498 393 393
query81 516 290 251 251
query82 637 158 127 127
query83 361 284 249 249
query84
query85 928 540 465 465
query86 434 306 288 288
query87 3467 3330 3137 3137
query88 3765 2789 2777 2777
query89 434 387 338 338
query90 1901 185 181 181
query91 177 162 144 144
query92 60 65 58 58
query93 1596 1488 919 919
query94 738 310 308 308
query95 669 386 340 340
query96 1087 850 353 353
query97 2731 2721 2587 2587
query98 220 212 207 207
query99 1202 1208 1025 1025
Total cold run time: 253549 ms
Total hot run time: 169893 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4765222440
Codex automated review failed and did not complete.
Error: You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at Jun 24th, 2026 10:56 PM.
Workflow run: https://github.com/apache/doris/actions/runs/27931683980
Please inspect the workflow logs and rerun the review after the underlying issue is resolved.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4766024620
# FE UT Coverage Report
Increment line coverage ` 74.69% (419/561)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "BiteTheDDDDt (via GitHub)" <gi...@apache.org>.
BiteTheDDDDt commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3301894165
##########
be/src/exec/exchange/local_exchange_sink_operator.cpp:
##########
@@ -37,24 +37,30 @@ std::vector<Dependency*> LocalExchangeSinkLocalState::dependencies() const {
return deps;
}
-Status LocalExchangeSinkOperatorX::init(RuntimeState* state, ExchangeType type,
- const int num_buckets, const bool use_global_hash_shuffle,
+Status LocalExchangeSinkOperatorX::init(RuntimeState* state, TLocalPartitionType::type type,
+ const int num_buckets,
const std::map<int, int>& shuffle_idx_to_instance_idx) {
+ DCHECK(!_planned_by_fe);
_name = "LOCAL_EXCHANGE_SINK_OPERATOR(" + get_exchange_type_name(type) + ")";
_type = type;
- if (_type == ExchangeType::HASH_SHUFFLE) {
- _shuffle_idx_to_instance_idx.clear();
- _use_global_shuffle = use_global_hash_shuffle;
+ if (_type == TLocalPartitionType::GLOBAL_EXECUTION_HASH_SHUFFLE) {
// For shuffle join, if data distribution has been broken by previous operator, we
// should use a HASH_SHUFFLE local exchanger to shuffle data again. To be mentioned,
// we should use map shuffle idx to instance idx because all instances will be
// distributed to all BEs. Otherwise, we should use shuffle idx directly.
- if (use_global_hash_shuffle) {
- _shuffle_idx_to_instance_idx = shuffle_idx_to_instance_idx;
+ _shuffle_idx_to_instance_idx = shuffle_idx_to_instance_idx;
+ if (state->query_options().__isset.enable_new_shuffle_hash_method &&
Review Comment:
这里和init_partitioner似乎有点冗余
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -290,6 +290,32 @@ Status PipelineFragmentContext::_build_and_prepare_full_pipeline(ThreadPool* thr
RETURN_IF_ERROR(_build_pipelines(_runtime_state->obj_pool(), *_query_ctx->desc_tbl,
&_root_op, root_pipeline));
+ // Propagate _num_instances from LOCAL_EXCHANGE pipelines to ancestor pipelines
+ // that inherited reduced num_tasks from a serial operator.
+ _propagate_local_exchange_num_tasks();
+
+ // Create deferred local exchangers now that all pipelines have final num_tasks.
+ RETURN_IF_ERROR(_create_deferred_local_exchangers());
Review Comment:
如果fe完全接管了local exchange的计算,应该就不需要be自己算required_data_distribution和算子并行度了?be不需要感知required_data_distribution,全按fe规划的来应该就行?
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3413410484
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java:
##########
@@ -634,7 +636,8 @@ protected void splitFragments(PhysicalPlan resultPlan) {
return;
}
- PlanTranslatorContext planTranslatorContext = new PlanTranslatorContext(cascadesContext);
+ this.planTranslatorContext = new PlanTranslatorContext(cascadesContext);
+ PlanTranslatorContext planTranslatorContext = this.planTranslatorContext;
Review Comment:
Done in `fe9a3316be0` — removed; subsequent references resolve to the field `this.planTranslatorContext`.
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java:
##########
@@ -657,7 +660,8 @@ protected void splitFragments(PhysicalPlan resultPlan) {
scanNodeList.addAll(planTranslatorContext.getScanNodes());
physicalRelations.addAll(planTranslatorContext.getPhysicalRelations());
descTable = planTranslatorContext.getDescTable();
- fragments = new ArrayList<>(planTranslatorContext.getPlanFragments());
+ List<PlanFragment> planFragments = planTranslatorContext.getPlanFragments();
Review Comment:
Done in `fe9a3316be0` — inlined into `fragments = new ArrayList<>(planTranslatorContext.getPlanFragments())`.
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java:
##########
@@ -740,6 +744,18 @@ protected void distribute(PhysicalPlan physicalPlan, ExplainLevel explainLevel)
splitFragments(physicalPlan);
doDistribute(canUseNereidsDistributePlanner, explainLevel);
+
+ SessionVariable sessionVariable = cascadesContext.getConnectContext().getSessionVariable();
+ if (sessionVariable.isEnableLocalShufflePlanner() && sessionVariable.isEnableLocalShuffle()) {
+ addLocalExchangeAfterDistribute();
+ }
Review Comment:
Done in `fe9a3316be0` — moved the `enable_local_shuffle_planner && enable_local_shuffle` gate into `addLocalExchangeAfterDistribute()`; the caller now just invokes it unconditionally.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/BucketedAggregationNode.java:
##########
@@ -97,7 +98,7 @@ public String getNodeExplainString(String detailPrefix, TExplainLevel detailLeve
}
@Override
- public boolean isSerialOperator() {
+ public boolean isSerialOperatorOnBe(ConnectContext context) {
Review Comment:
Changed in `fe9a3316be0` to override `isSerialNode()` (returning `false`) instead of `isSerialOperatorOnBe()`, matching every other node — behavior-equivalent.
One correction on the premise: `BucketedAggregationNode extends PlanNode`, not `AggregationNode`, so it never inherited `AggregationNode.isSerialNode()`; `isSerialNode()` was already the base `false`, and the old `isSerialOperatorOnBe()=false` override was effectively redundant. So there was no latent inconsistency, but overriding `isSerialNode()` is the cleaner/consistent form regardless.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AggregationNode.java:
##########
@@ -258,4 +268,155 @@ public boolean isQueryCacheCandidate() {
public void setQueryCacheCandidate(boolean queryCacheCandidate) {
this.queryCacheCandidate = queryCacheCandidate;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+
+ ConnectContext connectContext = translatorContext.getConnectContext();
+ SessionVariable sessionVariable = connectContext.getSessionVariable();
+ // PR #62438: when false, non-finalize agg falls back to BE base class.
+ boolean enableLeBeforeAgg = sessionVariable.enableLocalExchangeBeforeAgg;
+ boolean hasKeys = !aggInfo.getGroupingExprs().isEmpty();
+
+ // Each branch mirrors the corresponding BE operator's required_data_distribution()
+ // check order 1:1. The helper baseClassRequire() expands BE's base class behavior.
+ LocalExchangeTypeRequire requireChild;
+ if (canUseDistinctStreamingAgg(sessionVariable)) {
+ // DistinctStreamingAggOperatorX. Two flavors share this operator class:
+ // - streaming preagg (useStreamingPreagg=true): performance-only,
+ // flag controls
+ // - non-streaming dedup (useStreamingPreagg=false): correctness-required,
+ // always HASH regardless of flag
+ // Diverges from BE: BE's `!_needs_finalize && !enable_local_exchange_before_agg`
+ // early return catches non-streaming dedup too, causing the same family of
+ // wrong-result bug as AggSink (DORIS-25413).
+ if (needsFinalize && !hasKeys) {
+ requireChild = LocalExchangeTypeRequire.noRequire();
+ } else if (!needsFinalize && useStreamingPreagg && !enableLeBeforeAgg) {
+ requireChild = baseClassRequire(connectContext);
+ } else if (needsFinalize || (hasKeys && !useStreamingPreagg)) {
+ requireChild = AddLocalExchange.isColocated(this)
+ ? LocalExchangeTypeRequire.requireHash()
+ : parentRequire.autoRequireHash();
+ } else if (sessionVariable.enableDistinctStreamingAggForcePassthrough) {
+ requireChild = LocalExchangeTypeRequire.requirePassthrough();
+ } else {
+ requireChild = baseClassRequire(connectContext);
+ }
+ } else if (useStreamingPreagg) {
+ // StreamingAggOperatorX
+ if (children.get(0) instanceof HashJoinNode
+ && sessionVariable.enableStreamingAggHashJoinForcePassthrough) {
+ requireChild = LocalExchangeTypeRequire.requirePassthrough();
+ } else if (!needsFinalize && !enableLeBeforeAgg) {
+ requireChild = baseClassRequire(connectContext);
+ } else if (!hasKeys) {
+ requireChild = needsFinalize
+ ? LocalExchangeTypeRequire.noRequire()
+ : baseClassRequire(connectContext);
+ } else {
+ requireChild = LocalExchangeTypeRequire.requireHash();
+ }
+ } else {
+ // AggSinkOperatorX — covers finalize phase AND non-finalize phases (LOCAL
+ // preagg / FIRST_MERGE dedup). Streaming preagg goes through the StreamingAgg
+ // branch above, not here.
+ //
+ // Phase semantics for !needsFinalize:
+ // - FIRST / SECOND (LOCAL phase, !isMerge): performance-only, flag controls
+ // - FIRST_MERGE (correctness-required): always HASH regardless of flag
+ //
+ // Diverges from BE here: BE's `!_needs_finalize && !enable_local_exchange_before_agg`
+ // early return also catches FIRST_MERGE, dropping the HASH requirement and
+ // causing wrong-result (e.g. PASSTHROUGH over serial child breaks the
+ // group-by-key invariant — DORIS-25413).
+ if (!hasKeys) {
+ requireChild = needsFinalize
+ ? LocalExchangeTypeRequire.noRequire()
+ : baseClassRequire(connectContext);
+ } else if (!needsFinalize && !aggInfo.isMerge() && !enableLeBeforeAgg) {
+ // LOCAL phase (FIRST preagg / SECOND distinct local) + user opted out
+ // of pre-agg LE → base class decides: serial child → PASSTHROUGH
+ // (parallelism), non-serial child → NOOP (no LE).
+ requireChild = baseClassRequire(connectContext);
+ } else if (!needsFinalize || AddLocalExchange.isColocated(this)) {
+ // FIRST_MERGE (correctness) or finalize+colocate → HASH.
+ requireChild = parentRequire.autoRequireHash();
+ } else if (hasPartitionExprs(parentRequire)) {
+ // FE-only heuristic: finalize non-colocate with parent hash requirement
+ // → inherit parent's specific hash type.
+ requireChild = parentRequire.autoRequireHash();
+ } else {
+ // FE-only heuristic: finalize non-colocate without parent hash → skip
+ // LE (child Exchange already provides hash distribution).
+ requireChild = LocalExchangeTypeRequire.noRequire();
+ }
+ }
+
+ Pair<PlanNode, LocalExchangeType> enforceResult
+ = enforceRequire(translatorContext, children.get(0), 0, requireChild);
+ children = Lists.newArrayList(enforceResult.first);
+ return Pair.of(this, enforceResult.second);
+ }
+
+ /** BE base class required_data_distribution: serial child → PASSTHROUGH, else → NOOP. */
+ private LocalExchangeTypeRequire baseClassRequire(ConnectContext connectContext) {
+ return children.get(0).isSerialOperatorOnBe(connectContext)
+ ? LocalExchangeTypeRequire.requirePassthrough()
+ : LocalExchangeTypeRequire.noRequire();
+ }
+
+ @Override
+ protected List<Expr> getSemanticPartitionExprs() {
+ return aggInfo.getGroupingExprs();
+ }
+
+ @Override
+ protected List<Expr> getLocalExchangeDistributeExprs(int childIndex, boolean followedByShuffled) {
+ // Mirror BE AggSinkOperatorX::update_operator / StreamingAggOperatorX::update_operator:
+ // _partition_exprs = (distribute_expr_lists set && (followed_by_shuffled || has_distinct))
+ // ? distribute_expr_lists[0] : grouping_exprs
+ // The HASH LocalExchange must partition by _partition_exprs so a streaming partial preagg
+ // locally collapses same-key rows. Using child distribution (default) for a non-shuffled
+ // chain scatters same-group rows across N instances, leaving partial_preagg essentially a
+ // no-op and breaking row-arrival order at downstream merge-finalize (e.g. group_concat).
+ List<Expr> childDist = getChildDistributeExprList(childIndex);
+ boolean hasDistinct = aggInfo.getAggregateExprs().stream()
+ .map(FunctionCallExpr::getFnName)
+ .filter(name -> name != null)
+ .map(name -> name.getFunction())
+ .filter(name -> name != null)
+ .anyMatch(name -> name.startsWith("multi_distinct_"));
Review Comment:
There is no structural signal available at this layer, so the name match is intentional. Nereids rewrites `count/sum(distinct ...)` into dedicated `MultiDistinct*` functions constructed with `distinct=false` and a `multi_distinct_` name; by the time it reaches this legacy `FunctionCallExpr` / `AggregateInfo` layer, `isDistinct()` is already `false` and the function name is the only remaining signal. An `AggInfo.isMultiDistinct()` helper would still have to match the name internally — it couldn't be more "structural". (The BE `__isset.aggregate_functions` check isn't a distinct test either.) Added a comment in `fe9a3316be0` explaining this so the prefix match isn't mistaken for laziness.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3413446628
##########
fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java:
##########
@@ -1130,8 +1130,10 @@ public boolean isProfileSafeStmt() {
// 1. CreateTableCommand(mainly for create as select).
// 2. LoadCommand.
// 3. InsertOverwriteTableCommand.
+ // 4. MergeIntoCommand (merge into ... using ...).
if ((plan instanceof Command) && !(plan instanceof LoadCommand)
- && !(plan instanceof CreateTableCommand) && !(plan instanceof InsertOverwriteTableCommand)) {
+ && !(plan instanceof CreateTableCommand) && !(plan instanceof InsertOverwriteTableCommand)
+ && !(plan instanceof org.apache.doris.nereids.trees.plans.commands.merge.MergeIntoCommand)) {
Review Comment:
Done in `19de375e12e` — imported `MergeIntoCommand` and use the simple name.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/MaterializationNode.java:
##########
@@ -227,7 +231,17 @@ public void setTopMaterializeNode(boolean topMaterializeNode) {
}
@Override
- public boolean isSerialOperator() {
+ public boolean isSerialNode() {
return true;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ Pair<PlanNode, LocalExchangeType> enforceResult = enforceRequire(
+ translatorContext, children.get(0), 0, LocalExchangeTypeRequire.requirePassthrough());
+ children = new ArrayList<>();
Review Comment:
Done in `19de375e12e` — switched to `Lists.newArrayList()`.
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java:
##########
@@ -3314,7 +3349,7 @@ private boolean findOlapScanNodesByPassExchangeNode(PlanNode root) {
return false;
}
- private List<List<Expr>> getDistributeExprs(Plan ... children) {
+ private List<List<Expr>> getChildrenDistributeExprs(Plan ... children) {
Review Comment:
Renamed to `getDistributeExprs(Plan... plans)` in `19de375e12e`. I kept it taking the plans directly rather than a parent node, because the callers pass specific subsets (e.g. `hashJoin.left()`/`right()`, `child(0)`, or `children().toArray(...)`), so a single "parent" input wouldn't fit them all — the rename just drops the misleading "Children" from the name.
##########
regression-test/suites/nereids_p0/local_shuffle/test_local_shuffle_rqg_bugs.groovy:
##########
@@ -0,0 +1,1560 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * Regression tests for bugs discovered by RQG testing on the local-exchange2 branch.
+ *
+ * These queries triggered "must set shared state" errors or incorrect results
+ * in RQG build 183992. Common conditions:
+ * - use_serial_exchange=true (makes ALL Exchanges serial, not just UNPARTITIONED)
+ * - enable_local_shuffle_planner=true (FE-planned local exchange)
+ * - parallel_pipeline_task_num > 1
+ *
+ * Error types reproduced:
+ * 1. must set shared state, in AGGREGATION_OPERATOR
+ * 2. must set shared state, in SORT_OPERATOR
+ * 3. incorrect results with GROUPING SETS + scalar subquery + window function
+ */
+suite("test_local_shuffle_rqg_bugs") {
+
+ // ============================================================
+ // Table setup — mirrors RQG table structure
+ // 10 buckets to match RQG (replication_num=1 for single-BE testing)
+ // ============================================================
+ sql "DROP TABLE IF EXISTS rqg_t1"
+ sql "DROP TABLE IF EXISTS rqg_t2"
+ sql "DROP TABLE IF EXISTS rqg_t3"
+ sql "DROP TABLE IF EXISTS rqg_t4"
+
+ sql """
+ CREATE TABLE rqg_t1 (
+ pk INT NOT NULL,
+ col_int_undef_signed INT,
+ col_int_undef_signed2 INT,
+ col_int_undef_signed_not_null INT NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ CREATE TABLE rqg_t2 (
+ pk INT NOT NULL,
+ col_int_undef_signed INT,
+ col_int_undef_signed2 INT,
+ col_bigint_undef_signed_not_null BIGINT NOT NULL,
+ col_decimal_38_10__undef_signed_not_null DECIMAL(38,10) NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ // Table for build 184181 GLOBAL_HASH_SHUFFLE bugs — needs varchar + bigint columns
+ sql """
+ CREATE TABLE rqg_t3 (
+ pk INT NOT NULL,
+ col_bigint_undef_signed BIGINT,
+ col_varchar_10__undef_signed VARCHAR(10),
+ col_varchar_64__undef_signed VARCHAR(64)
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ // Second table for FULL OUTER JOIN case (col_bigint_undef_signed_not_null)
+ sql """
+ CREATE TABLE rqg_t4 (
+ pk INT NOT NULL,
+ col_bigint_undef_signed_not_null BIGINT NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ INSERT INTO rqg_t3 VALUES
+ (0, -94, 'Abc', 'hello world'),
+ (1, 672609, 'Xyz', null),
+ (2, -3766684, 'Pqr', 'test string'),
+ (3, 5070261, 'abc', 'another row'),
+ (4, null, 'def', 'value four'),
+ (5, -86, 'XgpxlHBLEM', null),
+ (6, 21910, 'abc', 'they'),
+ (7, -63, 'zzzz', 'some text'),
+ (8, -8276281, 'AHlvNtoGLO', 'longer string here'),
+ (9, -101, 'mid', 'final row')
+ """
+
+ sql """
+ INSERT INTO rqg_t4 VALUES
+ (0, 0), (1, 1), (2, 2), (3, 3), (4, 4),
+ (5, 5), (6, 6), (7, 7), (8, 8), (9, 9),
+ (10, 2), (11, 2), (12, 2), (13, 3), (14, 4),
+ (15, 5), (16, 2), (17, 2), (18, 2), (19, 9)
+ """
+
+ // Insert enough rows to exercise multiple pipeline tasks
+ sql """
+ INSERT INTO rqg_t1 VALUES
+ (0, 0, 10, 0), (1, 1, 11, 1), (2, 2, 12, 2), (3, 3, 13, 3),
+ (4, 4, 14, 4), (5, 5, 15, 5), (6, 6, 16, 6), (7, 7, 17, 7),
+ (8, 8, 18, 8), (9, 9, 19, 9), (10, 0, 20, 10), (11, 1, 21, 11),
+ (12, 2, 22, 12), (13, 3, 23, 13), (14, 4, 24, 14), (15, 5, 25, 15),
+ (16, 6, 26, 16), (17, 7, 27, 17), (18, 8, 28, 18), (19, 9, 29, 19)
+ """
+
+ sql """
+ INSERT INTO rqg_t2 VALUES
+ (0, 0, 10, 100, 1.5), (1, 1, 11, 101, 2.5), (2, 2, 12, 102, 3.5),
+ (3, 3, 13, 103, 4.5), (4, 4, 14, 104, 5.5), (5, 5, 15, 105, 6.5),
+ (6, 6, 16, 106, 7.5), (7, 7, 17, 107, 8.5), (8, 8, 18, 108, 9.5),
+ (9, 9, 19, 109, 10.5), (10, 0, 20, 110, 11.5), (11, 1, 21, 111, 12.5),
+ (12, 2, 22, 112, 13.5), (13, 3, 23, 113, 14.5), (14, 4, 24, 114, 15.5),
+ (15, 5, 25, 115, 16.5), (16, 6, 26, 116, 17.5), (17, 7, 27, 117, 18.5),
+ (18, 8, 28, 118, 19.5), (19, 9, 29, 119, 20.5)
+ """
+
+ // Wait for data to be visible
+ Thread.sleep(5000)
Review Comment:
Done in `19de375e12e` — replaced `Thread.sleep(5000)` with a poll on the actual row counts (`rqg_t1`/`rqg_t2` == 20) with a timeout, so it waits on the real condition instead of a fixed delay.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3413700850
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -824,14 +828,97 @@ public void foreachDownInCurrentFragment(Consumer<PlanNode> visitor) {
});
}
- // Operators need to be executed serially. (e.g. finalized agg without key)
- public boolean isSerialOperator() {
+ /**
+ * Node-level "is this operator inherently serial" property — answers without looking
+ * at the fragment. Default false; subclasses override (e.g. finalized agg without key,
+ * UNPARTITIONED ExchangeNode with merge sort).
+ *
+ * Use ONLY in framework-internal places where we are already iterating within a
+ * fragment whose serial-source mode is fixed: {@link #shouldResetSerialFlagForChild}
+ * inputs, {@link #createLocalExchange} heavy-op gate, and child.isSerialNode() checks
+ * embedded inside an enforceRequire path. Do NOT use it when computing a
+ * {@link LocalExchangeNode.LocalExchangeTypeRequire} on a child — call
+ * {@link #isSerialOperatorOnBe} instead.
+ */
+ public boolean isSerialNode() {
Review Comment:
> @morrySnow just curious, why don't you impl it before translation and after post process in Nereids?
(Answering the review-summary question here, anchored to where it actually runs.)
It needs two inputs that only exist **after translation + distribute**, not on the Nereids physical plan:
1. **PlanNode-level info** — `AddLocalExchange` walks the translated `PlanNode` / `PlanFragment` tree via `PlanNode.enforceAndDeriveLocalExchange` (`isSerialNode()` / `isSerialOperatorOnBe()`, the distribute-expr lists serialized to BE thrift, the fragment sink type, …). Those are the legacy `PlanNode`s the translator produces — they don't exist on the Nereids physical operators.
2. **Instance counts** — whether to insert an LE (and the fan-out width) depends on the per-BE instance count: `addLocalExchange` skips fragments where `maxPerBeInstances <= 1` (mirroring BE's `_num_instances <= 1` no-op), and the PASSTHROUGH fan-out targets `_num_instances`. Instance assignment is done by `DistributePlanner`, which runs **after** translation.
So it runs right here — after `splitFragments()` + `doDistribute()` — the first point where both the BE-facing PlanNode tree and the instance assignment exist. Doing it pre-translation in Nereids would mean re-deriving fragment structure + instance counts that haven't been computed yet.
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java:
##########
@@ -740,6 +742,19 @@ protected void distribute(PhysicalPlan physicalPlan, ExplainLevel explainLevel)
splitFragments(physicalPlan);
doDistribute(canUseNereidsDistributePlanner, explainLevel);
+
+ addLocalExchangeAfterDistribute();
Review Comment:
> @morrySnow just curious, why don't you impl it before translation and after post process in Nereids?
(Answering the review-summary question here, anchored to where it actually runs.)
It needs two inputs that only exist **after translation + distribute**, not on the Nereids physical plan:
1. **PlanNode-level info** — `AddLocalExchange` walks the translated `PlanNode` / `PlanFragment` tree via `PlanNode.enforceAndDeriveLocalExchange` (`isSerialNode()` / `isSerialOperatorOnBe()`, the distribute-expr lists serialized to BE thrift, the fragment sink type, …). Those are the legacy `PlanNode`s the translator produces — they don't exist on the Nereids physical operators.
2. **Instance counts** — whether to insert an LE (and the fan-out width) depends on the per-BE instance count: `addLocalExchange` skips fragments where `maxPerBeInstances <= 1` (mirroring BE's `_num_instances <= 1` no-op), and the PASSTHROUGH fan-out targets `_num_instances`. Instance assignment is done by `DistributePlanner`, which runs **after** translation.
So it runs right here — after `splitFragments()` + `doDistribute()` — the first point where both the BE-facing PlanNode tree and the instance assignment exist. Doing it pre-translation in Nereids would mean re-deriving fragment structure + instance counts that haven't been computed yet.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4708802951
<details>
<summary>TPC-H: <b>Total hot run time: 28977 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 832ba5f0bf8ab5846bb316e94f1099ae58fe466f, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17651 4086 4052 4052
q2 q3 10758 1387 819 819
q4 4689 481 340 340
q5 7533 884 597 597
q6 188 180 139 139
q7 781 848 630 630
q8 9596 1638 1716 1638
q9 6531 4544 4542 4542
q10 6858 1830 1517 1517
q11 439 276 245 245
q12 657 420 282 282
q13 18194 3347 2786 2786
q14 268 261 245 245
q15 q16 819 774 712 712
q17 931 945 985 945
q18 6939 5658 5693 5658
q19 1473 1228 1010 1010
q20 541 408 264 264
q21 5760 2697 2254 2254
q22 443 350 302 302
Total cold run time: 101049 ms
Total hot run time: 28977 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4435 4392 4325 4325
q2 q3 4501 4982 4343 4343
q4 2076 2210 1389 1389
q5 4497 4382 4381 4381
q6 227 174 128 128
q7 1751 1952 1755 1755
q8 2561 2188 2140 2140
q9 8293 8180 8170 8170
q10 4808 4731 4301 4301
q11 587 434 382 382
q12 747 838 616 616
q13 3354 3625 3051 3051
q14 310 310 277 277
q15 q16 743 752 632 632
q17 1364 1321 1348 1321
q18 8195 7196 7104 7104
q19 1214 1108 1142 1108
q20 2246 2273 1959 1959
q21 5275 4602 4453 4453
q22 527 441 455 441
Total cold run time: 57711 ms
Total hot run time: 52276 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3412773289
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -824,14 +828,97 @@ public void foreachDownInCurrentFragment(Consumer<PlanNode> visitor) {
});
}
- // Operators need to be executed serially. (e.g. finalized agg without key)
- public boolean isSerialOperator() {
+ /**
+ * Node-level "is this operator inherently serial" property — answers without looking
+ * at the fragment. Default false; subclasses override (e.g. finalized agg without key,
+ * UNPARTITIONED ExchangeNode with merge sort).
+ *
+ * Use ONLY in framework-internal places where we are already iterating within a
+ * fragment whose serial-source mode is fixed: {@link #shouldResetSerialFlagForChild}
+ * inputs, {@link #createLocalExchange} heavy-op gate, and child.isSerialNode() checks
+ * embedded inside an enforceRequire path. Do NOT use it when computing a
+ * {@link LocalExchangeNode.LocalExchangeTypeRequire} on a child — call
+ * {@link #isSerialOperatorOnBe} instead.
+ */
+ public boolean isSerialNode() {
Review Comment:
it should be protected?
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3413654832
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java:
##########
@@ -740,6 +742,19 @@ protected void distribute(PhysicalPlan physicalPlan, ExplainLevel explainLevel)
splitFragments(physicalPlan);
doDistribute(canUseNereidsDistributePlanner, explainLevel);
+
+ addLocalExchangeAfterDistribute();
Review Comment:
> @morrySnow just curious, why don't you impl it before translation and after post process in Nereids?
(Answering the review-summary question here, anchored to where it actually runs.)
It needs two inputs that only exist **after translation + distribute**, not on the Nereids physical plan:
1. **PlanNode-level info** — `AddLocalExchange` walks the translated `PlanNode` / `PlanFragment` tree via `PlanNode.enforceAndDeriveLocalExchange` (`isSerialNode()` / `isSerialOperatorOnBe()`, the distribute-expr lists serialized to BE thrift, the fragment sink type, …). Those are the legacy `PlanNode`s the translator produces — they don't exist on the Nereids physical operators.
2. **Instance counts** — whether to insert an LE (and the fan-out width) depends on the per-BE instance count: `addLocalExchange` skips fragments where `maxPerBeInstances <= 1` (mirroring BE's `_num_instances <= 1` no-op), and the PASSTHROUGH fan-out targets `_num_instances`. Instance assignment is done by `DistributePlanner`, which runs **after** translation.
So it runs right here — after `splitFragments()` + `doDistribute()` — the first point where both the BE-facing PlanNode tree and the instance assignment exist. Doing it pre-translation in Nereids would mean re-deriving fragment structure + instance counts that haven't been computed yet.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3433158030
##########
be/src/exec/pipeline/dependency.h:
##########
@@ -832,50 +832,44 @@ struct SetSharedState : public BasicSharedState {
Status hash_table_init();
};
-enum class ExchangeType : uint8_t {
- NOOP = 0,
- // Shuffle data by Crc32CHashPartitioner
- HASH_SHUFFLE = 1,
- // Round-robin passthrough data blocks.
- PASSTHROUGH = 2,
- // Shuffle data by Crc32HashPartitioner<ShuffleChannelIds> (e.g. same as storage engine).
- BUCKET_HASH_SHUFFLE = 3,
- // Passthrough data blocks to all channels.
- BROADCAST = 4,
- // Passthrough data to channels evenly in an adaptive way.
- ADAPTIVE_PASSTHROUGH = 5,
- // Send all data to the first channel.
- PASS_TO_ONE = 6,
-};
+inline bool is_shuffled_exchange(TLocalPartitionType::type idx) {
Review Comment:
Good catch — fixed in `0436cc4cb57`. Added `#include <gen_cpp/Partitions_types.h>` to `dependency.h` (the header that defines `TLocalPartitionType`, matching e.g. `vdata_stream_sender.h`), so it no longer depends on include order in TUs like `record_batch_queue.cpp` / `group_commit_mgr.cpp`.
##########
fe/fe-core/src/main/java/org/apache/doris/common/profile/SummaryProfile.java:
##########
@@ -563,6 +567,10 @@ public void update(Map<String, String> summaryInfo) {
// This method is used to display the final data status when the overall query ends.
// This can avoid recalculating some strings and so on every time during the update process.
public void queryFinished() {
+ // Mark profile collection as complete. This is written last, after all BE fragment
+ // profiles have been merged (called post-waitForFragmentsDone). Pollers can wait for
+ // this field to avoid reading a partial profile.
Review Comment:
Fixed in `0436cc4cb57` — moved the `IS_PROFILE_COLLECTION_COMPLETED` marker to the **end** of `queryFinished()`, after the optional `SPLITS_ASSIGNMENT_WEIGHT` block, so a poller can no longer observe `completed=true` on a partial profile. (The comment claimed "written last" but it was actually written first — now it genuinely is last.)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4681012159
<details>
<summary>TPC-H: <b>Total hot run time: 29402 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 9f33ce47e1bdfae26100cf19b96a7eab06238d13, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17704 4038 4060 4038
q2 q3 10988 1496 806 806
q4 4823 477 350 350
q5 8626 880 598 598
q6 356 170 134 134
q7 928 830 636 636
q8 10949 1740 1633 1633
q9 7280 4582 4503 4503
q10 6823 1829 1504 1504
q11 457 275 248 248
q12 646 423 282 282
q13 18194 3405 2803 2803
q14 260 263 242 242
q15 q16 823 779 708 708
q17 981 968 932 932
q18 6744 5747 5651 5651
q19 1147 1238 1172 1172
q20 527 411 272 272
q21 5959 2836 2570 2570
q22 434 380 320 320
Total cold run time: 104649 ms
Total hot run time: 29402 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4918 4738 5031 4738
q2 q3 4987 5292 4679 4679
q4 2117 2190 1416 1416
q5 4981 4742 4725 4725
q6 239 195 132 132
q7 1926 1735 1531 1531
q8 2267 2031 1971 1971
q9 7623 7582 7604 7582
q10 4744 4694 4216 4216
q11 535 390 352 352
q12 734 737 525 525
q13 3024 3415 2764 2764
q14 280 280 255 255
q15 q16 680 695 618 618
q17 1295 1264 1282 1264
q18 7439 6953 6693 6693
q19 1119 1087 1106 1087
q20 2237 2256 1985 1985
q21 5275 4637 4467 4467
q22 520 446 426 426
Total cold run time: 56940 ms
Total hot run time: 51426 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4681249141
<details>
<summary>TPC-H: <b>Total hot run time: 29291 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 784476ead971eeebeb0b637f6da4a434e380ce36, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17871 4099 4070 4070
q2 q3 10706 1399 816 816
q4 4696 472 342 342
q5 7542 868 587 587
q6 187 170 133 133
q7 771 841 630 630
q8 9335 1554 1691 1554
q9 5604 4539 4522 4522
q10 6791 1812 1559 1559
q11 456 266 252 252
q12 626 426 292 292
q13 18184 3425 2825 2825
q14 273 260 238 238
q15 q16 824 777 713 713
q17 1008 888 941 888
q18 6887 5756 5552 5552
q19 2282 1315 1070 1070
q20 495 409 258 258
q21 6269 2888 2670 2670
q22 466 373 320 320
Total cold run time: 101273 ms
Total hot run time: 29291 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 5027 4733 4818 4733
q2 q3 4876 5381 4647 4647
q4 2095 2189 1407 1407
q5 4975 4733 4606 4606
q6 233 189 144 144
q7 1908 1781 1531 1531
q8 2404 2101 2140 2101
q9 8021 7689 7622 7622
q10 4731 4679 4216 4216
q11 520 385 348 348
q12 723 740 526 526
q13 3003 3354 2835 2835
q14 275 277 257 257
q15 q16 674 691 608 608
q17 1289 1252 1246 1246
q18 7278 6706 6786 6706
q19 1087 1075 1110 1075
q20 2248 2202 1935 1935
q21 5284 4551 4459 4459
q22 508 439 412 412
Total cold run time: 57159 ms
Total hot run time: 51414 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3413420330
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -690,6 +716,188 @@ Status PipelineFragmentContext::_build_pipelines(ObjectPool* pool, const Descrip
return Status::OK();
}
+Status PipelineFragmentContext::_create_deferred_local_exchangers() {
+ for (auto& info : _deferred_exchangers) {
+ // DANGER ZONE — do not "fix" this line without reading the history.
+ //
+ // sender_count seeds Exchanger::_running_sink_operators, which the source side
+ // waits to reach 0 via sub_running_sink_operators on each sink LocalState close.
+ // The correct value is THIS pipeline-instance's sink task count, which is exactly
+ // info.upstream_pipe->num_tasks() — one PipelineTask per task, one close per task.
+ //
+ // Tempting wrong fix #1: `std::max(num_tasks, _num_instances)` to mirror the
+ // BE-planned path in _add_local_exchange_impl (~line 1023). THIS BREAKS the
+ // common FE-planned shape of `serial scan → LE(PT) → ...`: upstream_pipe
+ // genuinely has num_tasks=1, only 1 close arrives, but seed becomes
+ // _num_instances so _running_sink_operators never reaches 0 — downstream
+ // sources hang on SHUFFLE_DATA_DEPENDENCY (e.g. MTMV refresh from
+ // mtmv_up_down_job_p0/load.groovy stays at Status=RUNNING; build 949402
+ // regressed exactly this way). BE-planned mode uses max() because its
+ // `cur_pipe` is the source-side pipeline (always raised to _num_instances by
+ // add_pipeline) — not analogous to our `upstream_pipe` here, which is the
+ // sink-side pipeline that may legitimately stay at 1 for serial sources.
+ //
+ // Tempting wrong fix #2: multiply by _num_instances on the theory shared_state
+ // is shared across all instances. Same hang — each fragment-instance
+ // PipelineFragmentContext has its OWN _op_id_to_shared_state map, so the
+ // exchanger is per-instance, not per-BE. num_tasks() is already the right
+ // close-count for one instance.
+ //
+ // If a hang shows up with `_running_sink_operators < 0`, the bug is upstream:
+ // _propagate_local_exchange_num_tasks left num_tasks too low (or too high) for
+ // this fragment shape. Fix THAT pass, not this seed value.
+ const int sender_count = info.upstream_pipe->num_tasks();
+ switch (info.partition_type) {
+ case TLocalPartitionType::LOCAL_EXECUTION_HASH_SHUFFLE:
+ case TLocalPartitionType::GLOBAL_EXECUTION_HASH_SHUFFLE:
+ info.shared_state->exchanger = ShuffleExchanger::create_unique(
+ sender_count, _num_instances, info.num_partitions, info.free_blocks_limit,
+ info.partition_type);
+ break;
+ case TLocalPartitionType::BUCKET_HASH_SHUFFLE:
+ info.shared_state->exchanger = BucketShuffleExchanger::create_unique(
+ sender_count, _num_instances, info.num_partitions, info.free_blocks_limit);
+ break;
+ case TLocalPartitionType::PASSTHROUGH:
+ info.shared_state->exchanger = PassthroughExchanger::create_unique(
+ sender_count, _num_instances, info.free_blocks_limit);
+ break;
+ case TLocalPartitionType::BROADCAST:
+ info.shared_state->exchanger = BroadcastExchanger::create_unique(
+ sender_count, _num_instances, info.free_blocks_limit);
+ break;
+ case TLocalPartitionType::PASS_TO_ONE:
+ if (_runtime_state->enable_share_hash_table_for_broadcast_join()) {
+ info.shared_state->exchanger = PassToOneExchanger::create_unique(
+ sender_count, _num_instances, info.free_blocks_limit);
+ } else {
+ info.shared_state->exchanger = BroadcastExchanger::create_unique(
+ sender_count, _num_instances, info.free_blocks_limit);
+ }
+ break;
+ case TLocalPartitionType::ADAPTIVE_PASSTHROUGH:
+ info.shared_state->exchanger = AdaptivePassthroughExchanger::create_unique(
+ sender_count, _num_instances, info.free_blocks_limit);
+ break;
+ case TLocalPartitionType::NOOP:
+ case TLocalPartitionType::LOCAL_MERGE_SORT:
Review Comment:
Not a regression. `LOCAL_MERGE_SORT` as a `TLocalPartitionType` is only emitted by the legacy BE-side local-exchange planner. The local-merge-sort capability itself is driven by `SortNode.use_local_merge` → `LocalMergeSortSourceOperatorX` (`pipeline_fragment_context.cpp:1858`), which FE sets and works the same in both planner modes. The FE-planned local-exchange path simply never needs to emit this partition type, so the factory treats it (and NOOP) as a protocol violation. If you have a sort plan where FE-planned mode would actually need a `LOCAL_MERGE_SORT` *local exchange*, point me at it and I'll dig in.
##########
fe/fe-core/src/main/java/org/apache/doris/qe/runtime/ThriftPlansBuilder.java:
##########
@@ -277,7 +277,8 @@ private static Multiset<DistributedPlanWorker> computeInstanceNumPerWorker(
return workerCounter;
}
- private static Map<Integer, Integer> computeExchangeSenderNum(PipelineDistributedPlan distributedPlan) {
+ private static Map<Integer, Integer> computeExchangeSenderNum(
+ PipelineDistributedPlan distributedPlan) {
Review Comment:
This is just an incidental line-length reformat of the method signature (no behavior change). Happy to revert it to keep the diff minimal if you'd prefer — it crept in from formatting.
##########
be/src/exec/operator/partitioned_hash_join_probe_operator.h:
##########
@@ -228,16 +228,8 @@ class PartitionedHashJoinProbeOperatorX final
Status pull(doris::RuntimeState* state, Block* output_block, bool* eos) const override;
bool need_more_input_data(RuntimeState* state) const override;
- DataDistribution required_data_distribution(RuntimeState* /*state*/) const override {
- if (_join_op == TJoinOp::NULL_AWARE_LEFT_ANTI_JOIN) {
- return {ExchangeType::NOOP};
- }
- return (_join_distribution == TJoinDistributionType::BUCKET_SHUFFLE ||
- _join_distribution == TJoinDistributionType::COLOCATE
- ? DataDistribution(ExchangeType::BUCKET_HASH_SHUFFLE,
- _distribution_partition_exprs)
- : DataDistribution(ExchangeType::HASH_SHUFFLE,
- _distribution_partition_exprs));
+ DataDistribution required_data_distribution(RuntimeState* state) const override {
+ return _inner_probe_operator->required_data_distribution(state);
Review Comment:
Not a perf optimization — it's a consistency refactor. `PartitionedHashJoinProbeOperatorX` now delegates `required_data_distribution()` to its inner `_inner_probe_operator` instead of duplicating the join-distribution logic, so the spill path reports the same required distribution as the non-spill `HashJoinProbeOperatorX` (single source of truth). It also drops the now-unused `_join_distribution` member.
##########
be/src/udf/python/boost_process_compat.h:
##########
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#pragma once
Review Comment:
已移除(commit `1d48e9ae984`):`boost_process_compat.h` 以及 `build.sh` / `python_server.cpp` / `python_udf_runtime.{h,cpp}` 里跟 boost/process 相关的改动都还原到 master 了,这些 mac 编译相关的改动不进这个 PR。
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -290,6 +290,32 @@ Status PipelineFragmentContext::_build_and_prepare_full_pipeline(ThreadPool* thr
RETURN_IF_ERROR(_build_pipelines(_runtime_state->obj_pool(), *_query_ctx->desc_tbl,
&_root_op, root_pipeline));
+ // Propagate _num_instances from LOCAL_EXCHANGE pipelines to ancestor pipelines
+ // that inherited reduced num_tasks from a serial operator.
+ _propagate_local_exchange_num_tasks();
+
+ // Create deferred local exchangers now that all pipelines have final num_tasks.
+ RETURN_IF_ERROR(_create_deferred_local_exchangers());
Review Comment:
是的,这两块不是为了灰度/回滚——是 FE 接管 local exchange 的**当前折中**,不是一步到位。
要让 FE 把"每个算子的并发"也规划好直接下发,本质上需要 FE 自己切 pipeline、自己控制每条 pipeline 的并发(num_tasks 是 pipeline 粒度的,只在 pipeline 边界即 local exchange / pipeline breaker 处变化),BE 就不再规划 pipeline 了。那等于把 BE 的整套 pipeline 构建(`_build_pipeline`、pipeline-breaker 判定、num_tasks 推导、dependency 接线,还要兼顾 spill / runtime filter / shared state)搬到 FE,并改 thrift 契约携带完整 pipeline 结构——战线太长、风险太大。这套 FE 近似等价实现已经做了大半年、近 1 万行代码,没法一把全做。
所以现状是:FE 规划 LE 的**位置/类型/分布**(决定 plan 正确性,过去正是 BE/FE 分歧 bug 的根源),`enable_local_shuffle_planner=true` 时 BE 的 `_plan_local_exchange` 整个跳过;但每条 pipeline 的 num_tasks 仍由 BE 的 `_propagate_local_exchange_num_tasks` 拓扑推导,`_create_deferred_local_exchangers` 因为 exchanger 的 `num_partitions` 要等 num_tasks 定了才能建、所以延后创建。终态是 FE 连 num_tasks 也算好下发、BE 的 propagate 退化成 assert(只校验)——那是后续单独的大改。
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4708259972
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/14ff5b48070e26ba3f1518c301fce97c36998331_14ff5b48070e26ba3f1518c301fce97c36998331_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/14ff5b48070e26ba3f1518c301fce97c36998331_14ff5b48070e26ba3f1518c301fce97c36998331_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 77.32% (1889/2443) |
| Line Coverage | 64.38% (33943/52725) |
| Region Coverage | 64.77% (17455/26948) |
| Branch Coverage | 53.92% (9337/17316) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3414782162
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -939,4 +1031,263 @@ private String mergeIcebergAccessPathsWithId(
}
return StringUtils.join(mergeDisplayAccessPaths, ", ");
}
+
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ ArrayList<PlanNode> newChildren = Lists.newArrayList();
+ for (int i = 0; i < children.size(); i++) {
+ Pair<PlanNode, LocalExchangeType> childOutput
+ = enforceRequire(translatorContext, children.get(i), i, LocalExchangeTypeRequire.noRequire());
+ newChildren.add(childOutput.first);
+ }
+ this.children = newChildren;
+ return Pair.of(this, LocalExchangeType.NOOP);
+ }
+
+ /**
+ * Unified framework method: propagate serial flag → recurse child → satisfy check → Layer 1 skip → insert LE.
+ * Replaces the old enforceChild/enforceChildExchange/forceEnforceChildExchange trio.
+ *
+ * <h3>Data flow</h3>
+ * <ul>
+ * <li><b>serial-ancestor flag</b> ({@link PlanTranslatorContext#hasSerialAncestorInPipeline})
+ * — flows root → leaf during traversal. Mirrors BE's
+ * {@code any_of(operators[idx..end], is_serial_operator)} check used by
+ * {@code _add_local_exchange} to skip LE insertion when an ancestor in the same
+ * pipeline is already serial. Reset at pipeline boundaries via
+ * {@link #shouldResetSerialFlagForChild}.</li>
+ * <li><b>shuffle-for-correctness flag</b>
+ * ({@link PlanTranslatorContext#hasShuffleForCorrectnessAncestor}) — also flows
+ * root → leaf. Mirrors BE's {@code _followed_by_shuffled_operator}: tells a
+ * child whether some downstream operator depends on hash distribution for
+ * correctness, so {@code SetOperationNode} can pre-shuffle union branches.</li>
+ * <li><b>return value</b> {@code Pair<PlanNode, LocalExchangeType>} — first is the
+ * (possibly LE-wrapped) child; second is the actual output distribution as
+ * observed by the parent. Caller's {@code require.satisfy(output)} decides
+ * whether more LE is needed.</li>
+ * <li><b>parent.require</b> describes the constraint on the child output —
+ * computed inside the parent's {@code enforceAndDeriveLocalExchange} per child.</li>
+ * </ul>
+ *
+ * <h3>Invariants</h3>
+ * <ul>
+ * <li>Where a serial → non-serial transition needs redistribution, framework step 3 inserts
+ * the LE (e.g. a serial source fanned out via PASSTHROUGH). This is not a hard invariant:
+ * a serial child feeding a parent that requires PASSTHROUGH / noRequire (TableFunction,
+ * NLJ, Agg) is already correct and needs no LE, so it is intentionally not validated by a
+ * post-pass.</li>
+ * <li>{@code LocalExchangeNode} itself is always non-serial — setting it serial
+ * would defeat its purpose of fanning a 1-task pipeline back to N tasks.</li>
+ * <li>For pipeline-breaking parents ({@code shouldResetSerialFlagForChild=true}),
+ * the child starts a fresh pipeline so {@code hasSerialAncestor} is reset; the
+ * node's own {@code isSerialNode()} still composes in for the child's view.</li>
+ * <li>{@code RequireHash} accepts any hash flavour; {@code RequireSpecific} demands
+ * an exact match (with the one PASSTHROUGH/ADAPTIVE_PASSTHROUGH compatibility).
+ * Pick the looser one whenever correctness allows — see
+ * {@link LocalExchangeNode.LocalExchangeTypeRequire}.</li>
+ * </ul>
+ *
+ * <h3>Layers</h3>
+ * Layer 1 (shouldSkipLE): mirrors BE's need_to_local_exchange — skip when this node or
+ * an ancestor in the same pipeline is serial (operators[idx..end] has serial → skip).
+ * Layer 2 (require/output): each Node declares require and output in enforceAndDeriveLocalExchange.
+ */
+ protected Pair<PlanNode, LocalExchangeType> enforceRequire(
+ PlanTranslatorContext translatorContext, PlanNode child, int childIndex,
+ LocalExchangeTypeRequire require) {
+ // 1. Propagate serial-ancestor flag to child.
+ // For pipeline-splitting operators (shouldReset=true, e.g. non-streaming AGG):
+ // Drop inherited serial flag from parent (parent is in a different pipeline),
+ // but keep this node's own serial status (child is in the same pipeline as this
+ // node's sink, e.g. Exchange is in AGG_Sink pipeline).
+ // For non-splitting operators (shouldReset=false, e.g. streaming AGG):
+ // Inherit parent's serial flag + this node's own.
+ boolean inheritedSerial = shouldResetSerialFlagForChild(childIndex)
+ ? false : translatorContext.hasSerialAncestorInPipeline(this);
+ // Use isSerialOperatorOnBe (= isSerialNode && fragment.useSerialSource) instead of the
+ // raw isSerialNode(). BE's OperatorBase reads the Thrift `is_serial_operator` flag —
+ // which is what FE writes via isSerialOperatorOnBe — so when the fragment is not in
+ // serial-source mode, BE treats this operator as non-serial regardless of isSerialNode.
+ // Using isSerialNode here would set the child's serial-ancestor flag wider than BE's
+ // view and over-skip required LocalExchanges downstream.
+ boolean childHasSerialAncestor = inheritedSerial
+ || isSerialOperatorOnBe(translatorContext.getConnectContext());
+ translatorContext.setHasSerialAncestorInPipeline(child, childHasSerialAncestor);
+
+ // 1b. Propagate shuffle-for-correctness-ancestor flag to child.
+ // Mirrors BE's _followed_by_shuffled_operator: a downstream operator needs hash
+ // distribution for correctness, and the chain to here goes through HASH or NOOP
+ // requirements (so the dependency is preserved).
+ // propagate = ((inheritedShuffled || self.requiresShuffleForCorrectness)
+ // && require is hash)
+ // || (inheritedShuffled && require is noop/passthrough)
+ boolean inheritedShuffled = translatorContext.hasShuffleForCorrectnessAncestor(this);
+ boolean selfOrInheritedShuffled = inheritedShuffled || requiresShuffleForCorrectness();
+ boolean requireIsHash = require.preferType().isHashShuffle();
+ boolean requireIsNoop = require.preferType() == LocalExchangeNode.LocalExchangeType.NOOP;
+ boolean childShuffledAncestor = (selfOrInheritedShuffled && requireIsHash)
+ || (inheritedShuffled && requireIsNoop);
+ translatorContext.setHasShuffleForCorrectnessAncestor(child, childShuffledAncestor);
+
+ // 2. Recurse child (Layer 2: child declares its own require/output)
+ Pair<PlanNode, LocalExchangeType> childOutput =
+ child.enforceAndDeriveLocalExchange(translatorContext, this, require);
+
+ // Steps 2.5 and 3 both react to a serial child but address different concerns:
+ // - Step 2.5 rewrites the OUTPUT-side view (what we tell satisfy/parent about
+ // the child's actual distribution). A serial pipeline runs with 1 task so
+ // its distribution claim is meaningless — flatten to NOOP so the satisfy
+ // check below doesn't get fooled by a stale "I output BUCKET_HASH" claim.
+ // - Step 3 rewrites the REQUIRE-side decision (what we want from the child).
+ // If we previously asked for nothing (noRequire) but the child turns out
+ // to be serial and we're not, upgrade to requirePassthrough so an LE is
+ // inserted to restore parallelism.
+
+ // 2.5. Serial child override (output side): if child is serial on BE, force its
+ // reported output to NOOP. Distribution is irrelevant when the child runs
+ // with 1 task; downstream parallelism is restored either by step 3 (LE
+ // insertion) or skipped entirely by step 4b (we're also serial).
+ if (childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ childOutput = Pair.of(childOutput.first, LocalExchangeType.NOOP);
+ }
+
+ // 3. Framework-level serial child check (require side, mirrors BE base class
+ // required_data_distribution): if child will be serial on BE but this node is
+ // not serial, the pipeline has a 1-task serial child feeding an N-task non-serial
+ // parent. Without LE, pipeline splits (AGG/JOIN) create paired pipelines with
+ // mismatched num_tasks → crash. Upgrade noRequire to requirePassthrough so an
+ // LE is inserted below to restore parallelism.
+ if (require instanceof LocalExchangeNode.NoRequire
+ && childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())
+ && !isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ require = LocalExchangeTypeRequire.requirePassthrough();
+ }
+
+ // 4. Satisfy check: child output meets requirement → done
+ if (require.satisfy(childOutput.second)) {
+ return childOutput;
+ }
+
+ // 4. Layer 1: skip LE when serial operator or ancestor in same pipeline
+ // Equivalent to BE's need_to_local_exchange: any_of(operators[idx..end], is_serial) → skip.
+ // Use isSerialOperatorOnBe (not isSerialNode) because BE's Pipeline::need_to_local_exchange
+ // checks op->is_serial_operator() which reads the Thrift flag set from isSerialOperatorOnBe;
+ // when fragment.useSerialSource is false, BE treats this node as non-serial.
+ if (translatorContext.hasSerialAncestorInPipeline(this)
+ || isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ return childOutput;
+ }
+
+ // 5. Resolve exchange type and create LE node
+ LocalExchangeType preferType = AddLocalExchange.resolveExchangeType(require);
+ List<Expr> distributeExprs = getLocalExchangeDistributeExprs(childIndex, selfOrInheritedShuffled);
+ PlanNode leNode = createLocalExchange(translatorContext, childOutput.first, preferType, distributeExprs);
+ return Pair.of(leNode, preferType);
+ }
+
+ /**
+ * Create a LocalExchangeNode wrapping child with the given exchange type.
+ * No child-type skip — matches BE's _add_local_exchange which inserts LE for any child
+ * type without checking instanceof.
+ *
+ * Handles heavy-ops bottleneck avoidance (mirrors BE pipeline_fragment_context.cpp):
+ * when upstream has 1 task (serial source) and exchange is heavy (hash/bucket/adaptive),
+ * insert a PASSTHROUGH fan-out first to avoid single-task bottleneck on the heavy
+ * exchange sink. Only applies to local-shuffle (pooling scan) fragments.
+ */
+ protected PlanNode createLocalExchange(PlanTranslatorContext translatorContext,
+ PlanNode child, LocalExchangeType exchangeType, List<Expr> distributeExprs) {
+ if (fragment != null && fragment.useSerialSource(translatorContext.getConnectContext())
+ && exchangeType.isHeavyOperation() && child.isSerialNode()) {
+ PlanNode ptNode = new LocalExchangeNode(translatorContext.nextPlanNodeId(),
+ child, LocalExchangeType.PASSTHROUGH, null);
+ return new LocalExchangeNode(translatorContext.nextPlanNodeId(), ptNode,
+ exchangeType, distributeExprs);
+ }
+ return new LocalExchangeNode(translatorContext.nextPlanNodeId(), child,
+ exchangeType, distributeExprs);
+ }
+
+ /**
+ * Whether the child at {@code childIndex} starts a new pipeline context, causing
+ * its serial-ancestor flag to be reset to {@code false} rather than inherited from this node.
+ * Override to return {@code true} for pipeline-splitting nodes (LocalExchangeNode) and nodes
+ * whose children run in an independent pipeline segment (SortNode above analytic, etc.).
+ */
+ protected boolean shouldResetSerialFlagForChild(int childIndex) {
Review Comment:
so this must follow the impl in BE?
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3412722149
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -939,4 +1026,261 @@ private String mergeIcebergAccessPathsWithId(
}
return StringUtils.join(mergeDisplayAccessPaths, ", ");
}
+
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ ArrayList<PlanNode> newChildren = Lists.newArrayList();
+ for (int i = 0; i < children.size(); i++) {
+ Pair<PlanNode, LocalExchangeType> childOutput
+ = enforceRequire(translatorContext, children.get(i), i, LocalExchangeTypeRequire.noRequire());
+ newChildren.add(childOutput.first);
+ }
+ this.children = newChildren;
+ return Pair.of(this, LocalExchangeType.NOOP);
+ }
+
+ /**
+ * Unified framework method: propagate serial flag → recurse child → satisfy check → Layer 1 skip → insert LE.
+ * Replaces the old enforceChild/enforceChildExchange/forceEnforceChildExchange trio.
+ *
+ * <h3>Data flow</h3>
+ * <ul>
+ * <li><b>serial-ancestor flag</b> ({@link PlanTranslatorContext#hasSerialAncestorInPipeline})
+ * — flows root → leaf during traversal. Mirrors BE's
+ * {@code any_of(operators[idx..end], is_serial_operator)} check used by
+ * {@code _add_local_exchange} to skip LE insertion when an ancestor in the same
+ * pipeline is already serial. Reset at pipeline boundaries via
+ * {@link #shouldResetSerialFlagForChild}.</li>
+ * <li><b>shuffle-for-correctness flag</b>
+ * ({@link PlanTranslatorContext#hasShuffleForCorrectnessAncestor}) — also flows
+ * root → leaf. Mirrors BE's {@code _followed_by_shuffled_operator}: tells a
+ * child whether some downstream operator depends on hash distribution for
+ * correctness, so {@code SetOperationNode} can pre-shuffle union branches.</li>
+ * <li><b>return value</b> {@code Pair<PlanNode, LocalExchangeType>} — first is the
+ * (possibly LE-wrapped) child; second is the actual output distribution as
+ * observed by the parent. Caller's {@code require.satisfy(output)} decides
+ * whether more LE is needed.</li>
+ * <li><b>parent.require</b> describes the constraint on the child output —
+ * computed inside the parent's {@code enforceAndDeriveLocalExchange} per child.</li>
+ * </ul>
+ *
+ * <h3>Invariants</h3>
+ * <ul>
+ * <li>Every serial → non-serial transition has an LE somewhere between them
+ * (enforced by framework step 3 below, validated by
+ * {@link AddLocalExchange#validateNoSerialWithoutLocalExchange}).</li>
Review Comment:
[P3] Please update this invariant after removing `validateNoSerialWithoutLocalExchange`. Commit `3630071a` deleted that validation because some serial-to-non-serial transitions legitimately have no LocalExchange, but this bullet still says every transition has one and links to the now-nonexistent method. Please rewrite it to describe the narrower condition enforced by step 3, or remove the stale validation claim.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4706106002
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4706461907
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/1d7cf6dbd6095ee1fb5860efe258b0ce99242ae6_1d7cf6dbd6095ee1fb5860efe258b0ce99242ae6_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/1d7cf6dbd6095ee1fb5860efe258b0ce99242ae6_1d7cf6dbd6095ee1fb5860efe258b0ce99242ae6_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 77.32% (1889/2443) |
| Line Coverage | 64.40% (33954/52725) |
| Region Coverage | 64.76% (17452/26948) |
| Branch Coverage | 53.96% (9344/17316) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4731455027
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4732411052
Codex automated review failed and did not complete.
Error: You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at Jun 18th, 2026 1:09 AM.
Workflow run: https://github.com/apache/doris/actions/runs/27696292624
Please inspect the workflow logs and rerun the review after the underlying issue is resolved.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4733306322
<details>
<summary>ClickBench: <b>Total hot run time: 25.81 s</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit c2c51332d7f90c80ccabf77b27c23317d636a128, data reload: false
query1 0.00 0.00 0.00
query2 0.15 0.08 0.08
query3 0.36 0.24 0.24
query4 1.61 0.24 0.24
query5 0.33 0.33 0.33
query6 1.17 0.67 0.66
query7 0.03 0.01 0.01
query8 0.09 0.08 0.07
query9 0.51 0.39 0.38
query10 0.58 0.60 0.60
query11 0.33 0.18 0.18
query12 0.32 0.19 0.19
query13 0.52 0.53 0.51
query14 0.93 0.92 0.93
query15 0.67 0.59 0.59
query16 0.38 0.39 0.39
query17 1.00 1.05 1.00
query18 0.30 0.29 0.30
query19 1.92 1.82 1.76
query20 0.02 0.02 0.02
query21 15.40 0.37 0.30
query22 4.82 0.13 0.13
query23 15.85 0.48 0.30
query24 2.60 0.56 0.42
query25 0.15 0.11 0.09
query26 0.73 0.27 0.22
query27 0.11 0.09 0.10
query28 3.40 0.96 0.53
query29 12.45 4.48 3.53
query30 0.39 0.26 0.26
query31 2.77 0.64 0.34
query32 3.23 0.60 0.48
query33 2.94 2.98 2.97
query34 15.85 4.08 3.34
query35 3.30 3.25 3.28
query36 0.64 0.52 0.50
query37 0.11 0.09 0.09
query38 0.08 0.07 0.07
query39 0.08 0.06 0.06
query40 0.19 0.18 0.17
query41 0.12 0.08 0.08
query42 0.09 0.06 0.06
query43 0.07 0.07 0.07
Total cold run time: 96.59 s
Total hot run time: 25.81 s
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4738167972
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4739352006
# BE Regression && UT Coverage Report
Increment line coverage `76.60% (311/406)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_0436cc4cb573b104a59408a6169bd145bcfeff6c_merge/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_0436cc4cb573b104a59408a6169bd145bcfeff6c_merge/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 64.09% (24560/38322) |
| Line Coverage | 47.88% (255578/533815) |
| Region Coverage | 44.59% (210879/472979) |
| Branch Coverage | 45.63% (91504/200514) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4714247339
/review
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4765391810
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4551029809
# FE UT Coverage Report
Increment line coverage ` 73.85% (449/608)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_a68f47c588b0124cf5474ff805c847df5dfc781e/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_a68f47c588b0124cf5474ff805c847df5dfc781e/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3372699227
##########
fe/fe-core/src/main/java/org/apache/doris/planner/HashJoinNode.java:
##########
@@ -285,4 +302,71 @@ public List<Expr> getOtherJoinConjuncts() {
public List<Expr> getMarkJoinConjuncts() {
return markJoinConjuncts;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+
+ LocalExchangeTypeRequire probeSideRequire;
+ LocalExchangeTypeRequire buildSideRequire;
+ LocalExchangeType outputType = null;
+
+ if (joinOp == JoinOperator.NULL_AWARE_LEFT_ANTI_JOIN) {
+ buildSideRequire = probeSideRequire = LocalExchangeTypeRequire.noRequire();
+ outputType = LocalExchangeType.NOOP;
+ } else if (distrMode == DistributionMode.BROADCAST) {
+ // BE: _child->is_serial_operator() ? PASSTHROUGH/PASS_TO_ONE : NOOP
+ boolean probeChildSerial = children.get(0).isSerialOperatorOnBe(
+ translatorContext.getConnectContext());
+ boolean buildChildSerial = children.get(1).isSerialOperatorOnBe(
+ translatorContext.getConnectContext());
+ probeSideRequire = probeChildSerial
Review Comment:
[P2] Preserve `enable_broadcast_join_force_passthrough`
The BE planner returns `PASSTHROUGH` unconditionally for a broadcast-join probe when `state->enable_broadcast_join_force_passthrough()` is true (`HashJoinProbeOperatorX::required_data_distribution`). This FE branch only checks whether the probe child is serial. Once `enable_local_shuffle_planner` is enabled, BE local-exchange planning is skipped, so a non-serial broadcast join silently ignores this session variable and no balancing exchange is inserted. Please include the session flag in `probeSideRequire` and add a plan-shape assertion for the true/false cases.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AddLocalExchange.java:
##########
@@ -0,0 +1,182 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.planner;
+
+import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.glue.translator.PlanTranslatorContext;
+import org.apache.doris.nereids.trees.plans.distribute.DistributedPlan;
+import org.apache.doris.nereids.trees.plans.distribute.FragmentIdMapping;
+import org.apache.doris.nereids.trees.plans.distribute.PipelineDistributedPlan;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeType;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeTypeRequire;
+import org.apache.doris.planner.LocalExchangeNode.RequireHash;
+
+/**
+ * FE-side local exchange planner — inserts {@link LocalExchangeNode} into each fragment's
+ * plan tree so that within-fragment data redistribution is decided at planning time
+ * instead of at BE pipeline-build time.
+ *
+ * <h3>When this runs</h3>
+ * Invoked from {@code NereidsPlanner.addLocalExchangeAfterDistribute()} right after
+ * {@code DistributePlanner} has assigned instances to fragments and before the plan is
+ * serialized to BE. Gated by session variable {@code enable_local_shuffle_planner}
+ * (default true) and {@code enable_local_shuffle}; when either is off this pass is
+ * skipped entirely and BE falls back to its own {@code _plan_local_exchange}. The two
+ * paths are mutually exclusive: BE consults {@code runtime_state.h::plan_local_shuffle()}
+ * to know whether it should plan LE itself.
+ *
+ * <h3>What it changes</h3>
+ * <ul>
+ * <li>For each fragment with {@code maxPerBeInstances > 1}, walks the plan tree
+ * bottom-up via {@link PlanNode#enforceAndDeriveLocalExchange} and inserts
+ * LocalExchangeNodes where children's output distribution doesn't satisfy the
+ * parent's requirement.</li>
+ * <li>May wrap the fragment root with an extra PASSTHROUGH LE so the data sink
+ * (DataStreamSink / OlapTableSink) runs with the full instance count even when
+ * the root operator is serial — see {@link #addLocalExchangeForFragment}.</li>
+ * <li>Does NOT modify the fragment sink itself, fragment boundaries, or instance
+ * assignment.</li>
+ * </ul>
+ *
+ * <h3>Per-BE instance semantics</h3>
+ * Skips fragments where every BE has at most 1 instance. Using a global instance count
+ * would insert LE for "2 BEs × 1 instance" cases, which BE's own
+ * {@code _plan_local_exchange} would not — leading to pipeline task-count mismatch and
+ * deadlock. See {@link #addLocalExchange}.
+ *
+ * <h3>Reading order</h3>
+ * Start with {@link PlanNode#enforceRequire} (the recursion engine), then individual
+ * {@code enforceAndDeriveLocalExchange} overrides on PlanNode subclasses.
+ */
+public class AddLocalExchange {
+ /** addLocalExchange with distributed plans, skipping single-instance fragments.
+ * BE's _plan_local_exchange checks _num_instances which is the per-BE instance count.
+ * With _num_instances<=1 all pipelines on that BE have 1 task so local exchange is a no-op.
+ * We must use the same per-BE semantics: skip when every BE has at most 1 instance.
+ * Using global instanceCount would insert LE for fragments where 2 BEs each have 1 instance
+ * (global=2, per-BE=1), causing pipeline task mismatch and deadlock. */
+ public void addLocalExchange(FragmentIdMapping<DistributedPlan> distributedPlans,
+ PlanTranslatorContext context) {
+ for (DistributedPlan plan : distributedPlans.values()) {
+ PipelineDistributedPlan pipePlan = (PipelineDistributedPlan) plan;
+ long maxPerBeInstances = pipePlan.getInstanceJobs().stream()
+ .collect(java.util.stream.Collectors.groupingBy(
+ j -> j.getAssignedWorker().id(), java.util.stream.Collectors.counting()))
+ .values().stream().mapToLong(Long::longValue).max().orElse(0);
+ if (maxPerBeInstances <= 1) {
+ continue;
+ }
+ PlanFragment fragment = pipePlan.getFragmentJob().getFragment();
+ addLocalExchangeForFragment(fragment, context);
+ }
+ }
+
+ private void addLocalExchangeForFragment(PlanFragment fragment, PlanTranslatorContext context) {
+ DataSink sink = fragment.getSink();
+ LocalExchangeTypeRequire require = sink == null
+ ? LocalExchangeTypeRequire.noRequire() : sink.getLocalExchangeTypeRequire();
+ PlanNode root = fragment.getPlanRoot();
+ context.setHasSerialAncestorInPipeline(root, false);
+ Pair<PlanNode, LocalExchangeType> output = root
+ .enforceAndDeriveLocalExchange(context, null, require);
+ PlanNode newRoot = output.first;
+ // The fragment data sink (DataStreamSink, OlapTableSink) runs in the same pipeline
+ // as the root. If the root will be serial on BE, the sink pipeline has 1 task —
+ // only instance 0 sends data, others hang or miss writes.
+ // Insert PASSTHROUGH fan-out so sink runs with _num_instances tasks.
+ // This matches BE-native's default required_data_distribution():
+ // _child->is_serial_operator() ? PASSTHROUGH : NOOP
+ if (newRoot.isSerialOperatorOnBe(context.getConnectContext())) {
+ newRoot = new LocalExchangeNode(context.nextPlanNodeId(), newRoot,
+ LocalExchangeType.PASSTHROUGH, null);
+ }
+ if (newRoot != root) {
+ fragment.setPlanRoot(newRoot);
+ }
+ validateNoSerialWithoutLocalExchange(fragment.getPlanRoot(), context.getConnectContext());
+ }
+
+ /**
+ * In a local-shuffle fragment, the root check above guarantees the root pipeline
+ * has N tasks. Any serial operator reduces its pipeline to 1 task. If this serial
+ * operator feeds into a non-serial parent without LocalExchangeNode in between,
+ * some pipelines have 1 task while others have N → shared_state mismatch, data loss.
+ *
+ * Serial→serial chains are fine (all at 1 task, consistent). Only the transition
+ * from serial to non-serial needs LE to restore parallelism.
+ */
+ private void validateNoSerialWithoutLocalExchange(PlanNode node,
+ org.apache.doris.qe.ConnectContext context) {
+ for (PlanNode child : node.getChildren()) {
+ validateNoSerialWithoutLocalExchange(child, context);
+ if (child.isSerialOperatorOnBe(context)
+ && !(child instanceof LocalExchangeNode)
+ && !(node instanceof LocalExchangeNode)
+ && !(node instanceof ExchangeNode)
+ && !node.isSerialOperatorOnBe(context)) {
+ org.apache.logging.log4j.LogManager.getLogger(AddLocalExchange.class).warn(
Review Comment:
[P1] Reject invalid serial-to-parallel plans instead of logging
This predicate identifies a definite task-count mismatch: the child pipeline runs one task while the parent runs `_num_instances`. The comments and regression cases in this PR document that executing such a shape can hang, lose rows, or initialize shared state inconsistently. Logging a warning and continuing still sends the known-invalid plan to BE, potentially returning incorrect results. Please throw a planning error/assert here (or repair the plan) rather than continue.
##########
regression-test/suites/nereids_p0/local_shuffle/test_local_shuffle_rqg_bugs.groovy:
##########
@@ -0,0 +1,1557 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * Regression tests for bugs discovered by RQG testing on the local-exchange2 branch.
+ *
+ * These queries triggered "must set shared state" errors or incorrect results
+ * in RQG build 183992. Common conditions:
+ * - use_serial_exchange=true (makes ALL Exchanges serial, not just UNPARTITIONED)
+ * - enable_local_shuffle_planner=true (FE-planned local exchange)
+ * - parallel_pipeline_task_num > 1
+ *
+ * Error types reproduced:
+ * 1. must set shared state, in AGGREGATION_OPERATOR
+ * 2. must set shared state, in SORT_OPERATOR
+ * 3. incorrect results with GROUPING SETS + scalar subquery + window function
+ */
+suite("test_local_shuffle_rqg_bugs") {
+
+ // ============================================================
+ // Table setup — mirrors RQG table structure
+ // 10 buckets to match RQG (replication_num=1 for single-BE testing)
+ // ============================================================
+ sql "DROP TABLE IF EXISTS rqg_t1"
+ sql "DROP TABLE IF EXISTS rqg_t2"
+ sql "DROP TABLE IF EXISTS rqg_t3"
+ sql "DROP TABLE IF EXISTS rqg_t4"
+
+ sql """
+ CREATE TABLE rqg_t1 (
+ pk INT NOT NULL,
+ col_int_undef_signed INT,
+ col_int_undef_signed2 INT,
+ col_int_undef_signed_not_null INT NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ CREATE TABLE rqg_t2 (
+ pk INT NOT NULL,
+ col_int_undef_signed INT,
+ col_int_undef_signed2 INT,
+ col_bigint_undef_signed_not_null BIGINT NOT NULL,
+ col_decimal_38_10__undef_signed_not_null DECIMAL(38,10) NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ // Table for build 184181 GLOBAL_HASH_SHUFFLE bugs — needs varchar + bigint columns
+ sql """
+ CREATE TABLE rqg_t3 (
+ pk INT NOT NULL,
+ col_bigint_undef_signed BIGINT,
+ col_varchar_10__undef_signed VARCHAR(10),
+ col_varchar_64__undef_signed VARCHAR(64)
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ // Second table for FULL OUTER JOIN case (col_bigint_undef_signed_not_null)
+ sql """
+ CREATE TABLE rqg_t4 (
+ pk INT NOT NULL,
+ col_bigint_undef_signed_not_null BIGINT NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ INSERT INTO rqg_t3 VALUES
+ (0, -94, 'Abc', 'hello world'),
+ (1, 672609, 'Xyz', null),
+ (2, -3766684, 'Pqr', 'test string'),
+ (3, 5070261, 'abc', 'another row'),
+ (4, null, 'def', 'value four'),
+ (5, -86, 'XgpxlHBLEM', null),
+ (6, 21910, 'abc', 'they'),
+ (7, -63, 'zzzz', 'some text'),
+ (8, -8276281, 'AHlvNtoGLO', 'longer string here'),
+ (9, -101, 'mid', 'final row')
+ """
+
+ sql """
+ INSERT INTO rqg_t4 VALUES
+ (0, 0), (1, 1), (2, 2), (3, 3), (4, 4),
+ (5, 5), (6, 6), (7, 7), (8, 8), (9, 9),
+ (10, 2), (11, 2), (12, 2), (13, 3), (14, 4),
+ (15, 5), (16, 2), (17, 2), (18, 2), (19, 9)
+ """
+
+ // Insert enough rows to exercise multiple pipeline tasks
+ sql """
+ INSERT INTO rqg_t1 VALUES
+ (0, 0, 10, 0), (1, 1, 11, 1), (2, 2, 12, 2), (3, 3, 13, 3),
+ (4, 4, 14, 4), (5, 5, 15, 5), (6, 6, 16, 6), (7, 7, 17, 7),
+ (8, 8, 18, 8), (9, 9, 19, 9), (10, 0, 20, 10), (11, 1, 21, 11),
+ (12, 2, 22, 12), (13, 3, 23, 13), (14, 4, 24, 14), (15, 5, 25, 15),
+ (16, 6, 26, 16), (17, 7, 27, 17), (18, 8, 28, 18), (19, 9, 29, 19)
+ """
+
+ sql """
+ INSERT INTO rqg_t2 VALUES
+ (0, 0, 10, 100, 1.5), (1, 1, 11, 101, 2.5), (2, 2, 12, 102, 3.5),
+ (3, 3, 13, 103, 4.5), (4, 4, 14, 104, 5.5), (5, 5, 15, 105, 6.5),
+ (6, 6, 16, 106, 7.5), (7, 7, 17, 107, 8.5), (8, 8, 18, 108, 9.5),
+ (9, 9, 19, 109, 10.5), (10, 0, 20, 110, 11.5), (11, 1, 21, 111, 12.5),
+ (12, 2, 22, 112, 13.5), (13, 3, 23, 113, 14.5), (14, 4, 24, 114, 15.5),
+ (15, 5, 25, 115, 16.5), (16, 6, 26, 116, 17.5), (17, 7, 27, 117, 18.5),
+ (18, 8, 28, 118, 19.5), (19, 9, 29, 119, 20.5)
+ """
+
+ // Wait for data to be visible
+ Thread.sleep(5000)
+
+ // ============================================================
+ // Common settings
+ // ============================================================
+ sql "SET enable_nereids_planner=true"
+ sql "SET enable_fallback_to_original_planner=false"
+ sql "SET runtime_filter_mode=off"
+ sql "SET enable_profile=true"
+ sql "SET enable_sql_cache=false"
+ sql "SET enable_local_shuffle=true"
+
+ // ============================================================
+ // Bug 1: must set shared state, in AGGREGATION_OPERATOR
+ // RQG case: eliminate_group_by_uniform.case_id_11007680713
+ // Key conditions: use_serial_exchange=true, parallel_pipeline_task_num=3
+ // SQL: EXCEPT with count(*) GROUP BY on both sides
+ // ============================================================
+
+ // Test with FE planner (the buggy path)
+ logger.info("=== Bug 1a: AGG shared state - EXCEPT with serial exchange (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=3,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_common_expr_pushdown=false,
+ disable_streaming_preaggregations=true)*/
+ col_int_undef_signed_not_null as col1,
+ col_int_undef_signed_not_null as col2,
+ 0 as col3, count(1)
+ FROM rqg_t1
+ GROUP BY col1, col2, col3
+ EXCEPT
+ SELECT col_bigint_undef_signed_not_null as col1,
+ col_decimal_38_10__undef_signed_not_null as col2,
+ 5 as col3, count(1)
+ FROM rqg_t2
+ GROUP BY col1, col2, col3
+ """
+ logger.info("Bug 1a: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 1a FAILED: ${t.message}")
+ assertTrue(false, "Bug 1a: must set shared state in AGGREGATION_OPERATOR: ${t.message}")
+ }
+
+ // Compare with BE native planner
+ logger.info("=== Bug 1b: AGG shared state - EXCEPT with serial exchange (BE native) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=3,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_common_expr_pushdown=false,
+ disable_streaming_preaggregations=true)*/
+ col_int_undef_signed_not_null as col1,
+ col_int_undef_signed_not_null as col2,
+ 0 as col3, count(1)
+ FROM rqg_t1
+ GROUP BY col1, col2, col3
+ EXCEPT
+ SELECT col_bigint_undef_signed_not_null as col1,
+ col_decimal_38_10__undef_signed_not_null as col2,
+ 5 as col3, count(1)
+ FROM rqg_t2
+ GROUP BY col1, col2, col3
+ """
+ logger.info("Bug 1b: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 1b FAILED: ${t.message}")
+ assertTrue(false, "Bug 1b: BE native also fails: ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 2: must set shared state, in SORT_OPERATOR
+ // RQG case: grouping_set.case_id_5308471751
+ // Key conditions: use_serial_exchange=true, parallel_pipeline_task_num=5
+ // SQL: GROUPING SETS + window function (PERCENT_RANK)
+ // ============================================================
+
+ logger.info("=== Bug 2a: SORT shared state - GROUPING SETS + window (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=5,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true)*/
+ SUM(PERCENT_RANK() OVER (PARTITION BY col_int_undef_signed2 ORDER BY col_int_undef_signed2))
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2),(pk, pk),(col_int_undef_signed))
+ """
+ logger.info("Bug 2a: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 2a FAILED: ${t.message}")
+ assertTrue(false, "Bug 2a: must set shared state in SORT_OPERATOR: ${t.message}")
+ }
+
+ logger.info("=== Bug 2b: SORT shared state - GROUPING SETS + window (BE native) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=5,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true)*/
+ SUM(PERCENT_RANK() OVER (PARTITION BY col_int_undef_signed2 ORDER BY col_int_undef_signed2))
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2),(pk, pk),(col_int_undef_signed))
+ """
+ logger.info("Bug 2b: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 2b FAILED: ${t.message}")
+ assertTrue(false, "Bug 2b: BE native also fails: ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 3: incorrect results with GROUPING SETS + scalar subquery + window
+ // RQG case: grouping_set.case_id_5694495756
+ // Key conditions: parallel_pipeline_task_num=2, disable_streaming_preaggregations=true
+ // Expected: all rows same value; Actual: values split proportionally (1/3, 2/3)
+ // ============================================================
+
+ logger.info("=== Bug 3: incorrect results - GROUPING SETS + subquery + window ===")
+ // FE planner
+ def result_fe = sql """
+ SELECT /*+SET_VAR(parallel_pipeline_task_num=2,
+ enable_local_shuffle_planner=true,
+ disable_streaming_preaggregations=true,
+ enable_share_hash_table_for_broadcast_join=true)*/
+ SUM((SELECT MAX(col_int_undef_signed2) FROM rqg_t1))
+ OVER (PARTITION BY pk ORDER BY pk)
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2, pk),(pk), (pk))
+ """
+ // BE native
+ def result_be = sql """
+ SELECT /*+SET_VAR(parallel_pipeline_task_num=2,
+ enable_local_shuffle_planner=false,
+ disable_streaming_preaggregations=true,
+ enable_share_hash_table_for_broadcast_join=true)*/
+ SUM((SELECT MAX(col_int_undef_signed2) FROM rqg_t1))
+ OVER (PARTITION BY pk ORDER BY pk)
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2, pk),(pk), (pk))
+ """
+ logger.info("Bug 3 FE result rows: ${result_fe.size()}, first few: ${result_fe.take(5)}")
+ logger.info("Bug 3 BE result rows: ${result_be.size()}, first few: ${result_be.take(5)}")
+
+ // All values in both should be the same
+ if (result_fe.size() != result_be.size()) {
+ logger.warn("Bug 3: row count mismatch FE=${result_fe.size()} vs BE=${result_be.size()}")
Review Comment:
[P2] Make this regression fail the suite
When FE and BE produce different row counts this only logs a warning, so the regression still passes. The same issue appears in the Bug 4/5 catches below, which log query failures without rethrowing or asserting. These cases therefore provide no protection against the crashes and result mismatches they describe. Please use a result assertion/`qt_*`, and let unexpected SQL exceptions propagate, so a recurrence fails the suite.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4670455531
<details>
<summary>TPC-H: <b>Total hot run time: 29176 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 7c898c900506116cfb44b47189025b6fbfa7e44c, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17658 3995 3989 3989
q2 q3 10999 1504 823 823
q4 4801 479 341 341
q5 8599 887 620 620
q6 346 172 136 136
q7 906 850 645 645
q8 10946 1568 1584 1568
q9 7098 4488 4496 4488
q10 6873 1898 1516 1516
q11 436 275 259 259
q12 653 439 292 292
q13 18137 3404 2843 2843
q14 275 262 243 243
q15 q16 816 771 704 704
q17 946 972 918 918
q18 6758 5726 5552 5552
q19 1146 1308 1135 1135
q20 526 401 259 259
q21 5629 2616 2541 2541
q22 430 364 304 304
Total cold run time: 103978 ms
Total hot run time: 29176 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4313 4239 4227 4227
q2 q3 4564 4977 4327 4327
q4 2087 2213 1402 1402
q5 4444 4275 4279 4275
q6 224 177 130 130
q7 2308 1806 1749 1749
q8 2541 2149 2132 2132
q9 8020 8056 8101 8056
q10 4784 4756 4385 4385
q11 575 411 381 381
q12 757 754 529 529
q13 3262 3674 3028 3028
q14 317 320 276 276
q15 q16 713 753 629 629
q17 1357 1332 1435 1332
q18 8185 7332 6948 6948
q19 1109 1099 1093 1093
q20 2228 2237 1957 1957
q21 5314 4613 4545 4545
q22 514 457 409 409
Total cold run time: 57616 ms
Total hot run time: 51810 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4498451933
<details>
<summary>TPC-H: <b>Total hot run time: 31122 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 3ac8525eaac942c0bbeb93cf46d4c95ecf353156, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17639 3843 3858 3843
q2 q3 11031 1433 812 812
q4 4813 481 357 357
q5 10756 2313 2096 2096
q6 389 176 138 138
q7 974 762 662 662
q8 9632 1749 1671 1671
q9 7039 5045 4978 4978
q10 6487 2095 1796 1796
q11 433 276 241 241
q12 648 424 293 293
q13 18156 3453 2819 2819
q14 271 261 237 237
q15 q16 821 773 710 710
q17 998 937 925 925
q18 7221 5856 5636 5636
q19 1165 1248 1007 1007
q20 512 405 269 269
q21 5587 2628 2325 2325
q22 450 366 307 307
Total cold run time: 105022 ms
Total hot run time: 31122 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4205 4164 4120 4120
q2 q3 4591 4969 4315 4315
q4 2120 2274 1388 1388
q5 4383 4257 5342 4257
q6 250 189 138 138
q7 1931 1774 1594 1594
q8 2415 2132 2066 2066
q9 8043 7979 7939 7939
q10 4647 4510 4073 4073
q11 594 416 369 369
q12 764 758 512 512
q13 3408 3569 2960 2960
q14 307 319 270 270
q15 q16 716 736 633 633
q17 1361 1336 1301 1301
q18 7808 7299 6807 6807
q19 1125 1114 1124 1114
q20 2213 2210 1923 1923
q21 5342 4630 4454 4454
q22 516 467 403 403
Total cold run time: 56739 ms
Total hot run time: 50636 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4484211987
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4487508895
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/9016e5a105f84231f1a535be233aaf3e595c711d_9016e5a105f84231f1a535be233aaf3e595c711d_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/9016e5a105f84231f1a535be233aaf3e595c711d_9016e5a105f84231f1a535be233aaf3e595c711d_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 78.06% (1854/2375) |
| Line Coverage | 64.52% (33334/51662) |
| Region Coverage | 65.20% (16523/25341) |
| Branch Coverage | 55.75% (8838/15852) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4488610712
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4738675411
# FE UT Coverage Report
Increment line coverage ` 74.73% (420/562)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_0436cc4cb573b104a59408a6169bd145bcfeff6c/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_0436cc4cb573b104a59408a6169bd145bcfeff6c/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4765629089
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4739664036
# FE Regression Coverage Report
Increment line coverage ` 76.44% (451/590)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_0436cc4cb573b104a59408a6169bd145bcfeff6c_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_0436cc4cb573b104a59408a6169bd145bcfeff6c_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4508996295
# BE Regression && UT Coverage Report
Increment line coverage `85.51% (354/414)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_846d365a88901fa32fdf44b42b6cd04b900da259_merge/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_846d365a88901fa32fdf44b42b6cd04b900da259_merge/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 73.71% (27903/37857) |
| Line Coverage | 57.61% (303202/526266) |
| Region Coverage | 54.83% (254070/463365) |
| Branch Coverage | 56.35% (109800/194854) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4551441930
<details>
<summary>TPC-H: <b>Total hot run time: 32181 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit a68f47c588b0124cf5474ff805c847df5dfc781e, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17763 4185 4116 4116
q2 q3 10936 1460 814 814
q4 4814 494 342 342
q5 10582 2307 2115 2115
q6 397 176 141 141
q7 934 783 653 653
q8 9712 1754 1599 1599
q9 7000 5066 5120 5066
q10 6561 2219 1917 1917
q11 439 274 244 244
q12 657 427 303 303
q13 18158 3553 2834 2834
q14 271 260 236 236
q15 q16 829 784 715 715
q17 959 946 948 946
q18 6895 5750 6243 5750
q19 1191 1281 1077 1077
q20 536 410 277 277
q21 6005 2754 2726 2726
q22 465 360 310 310
Total cold run time: 105104 ms
Total hot run time: 32181 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 5120 4798 4917 4798
q2 q3 4901 5293 4731 4731
q4 2157 2260 1408 1408
q5 4931 4717 4783 4717
q6 223 165 120 120
q7 1784 1642 1465 1465
q8 2274 1997 1975 1975
q9 7659 7641 7656 7641
q10 4742 4693 4215 4215
q11 545 394 363 363
q12 732 749 527 527
q13 3130 3444 2827 2827
q14 282 279 251 251
q15 q16 692 706 627 627
q17 1295 1267 1260 1260
q18 7394 7102 6883 6883
q19 1157 1104 1113 1104
q20 2250 2235 1939 1939
q21 5368 4688 4565 4565
q22 524 452 411 411
Total cold run time: 57160 ms
Total hot run time: 51827 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4681111504
<details>
<summary>TPC-DS: <b>Total hot run time: 168597 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 9f33ce47e1bdfae26100cf19b96a7eab06238d13, data reload: false
query5 4309 613 481 481
query6 434 190 171 171
query7 4823 574 304 304
query8 369 211 183 183
query9 8784 4002 3993 3993
query10 438 307 251 251
query11 5922 2356 2106 2106
query12 158 98 96 96
query13 1255 608 449 449
query14 6377 5406 5054 5054
query14_1 4397 4360 4396 4360
query15 212 194 174 174
query16 1040 452 454 452
query17 1149 679 565 565
query18 2708 471 337 337
query19 207 175 137 137
query20 123 104 102 102
query21 220 140 110 110
query22 13637 13593 13509 13509
query23 17362 16545 16139 16139
query23_1 16288 16231 16210 16210
query24 7554 1776 1307 1307
query24_1 1343 1302 1288 1288
query25 549 429 360 360
query26 1295 306 168 168
query27 2679 564 326 326
query28 4440 2027 2025 2025
query29 1060 599 455 455
query30 302 235 195 195
query31 1103 1061 945 945
query32 107 61 58 58
query33 507 348 242 242
query34 1170 1207 659 659
query35 822 791 678 678
query36 1368 1370 1220 1220
query37 153 100 89 89
query38 3155 3128 2979 2979
query39 924 944 891 891
query39_1 871 893 864 864
query40 210 122 99 99
query41 64 61 71 61
query42 98 93 96 93
query43 319 318 273 273
query44
query45 196 188 179 179
query46 1069 1156 749 749
query47 2402 2345 2200 2200
query48 385 378 299 299
query49 636 471 351 351
query50 956 362 258 258
query51 4630 4465 4323 4323
query52 87 88 77 77
query53 249 269 191 191
query54 271 211 200 200
query55 77 76 69 69
query56 248 228 206 206
query57 1429 1408 1330 1330
query58 243 225 213 213
query59 1587 1682 1412 1412
query60 282 242 228 228
query61 154 152 151 151
query62 693 638 592 592
query63 239 190 182 182
query64 2521 816 615 615
query65
query66 1739 448 343 343
query67 29653 29614 29461 29461
query68
query69 408 307 265 265
query70 991 944 977 944
query71 316 216 206 206
query72 2928 2620 2342 2342
query73 858 771 456 456
query74 5102 4916 4772 4772
query75 2664 2590 2268 2268
query76 2371 1136 795 795
query77 370 379 279 279
query78 12398 12460 11833 11833
query79 1298 1043 750 750
query80 567 495 409 409
query81 449 285 235 235
query82 241 159 131 131
query83 277 281 261 261
query84
query85 918 585 503 503
query86 337 308 274 274
query87 3401 3343 3240 3240
query88 3580 2766 2714 2714
query89 416 383 340 340
query90 2207 191 187 187
query91 192 176 161 161
query92 67 62 71 62
query93 1422 1441 877 877
query94 570 406 310 310
query95 683 456 344 344
query96 1112 876 338 338
query97 2682 2695 2556 2556
query98 214 206 210 206
query99 1157 1179 1019 1019
Total cold run time: 250555 ms
Total hot run time: 168597 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4679263646
# FE Regression Coverage Report
Increment line coverage ` 0.00% (0/692)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_f056ebbed42446ea468bcc6891c05520bdfb31c3_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_f056ebbed42446ea468bcc6891c05520bdfb31c3_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4682262951
# FE Regression Coverage Report
Increment line coverage ` 79.51% (458/576)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_784476ead971eeebeb0b637f6da4a434e380ce36_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_784476ead971eeebeb0b637f6da4a434e380ce36_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4701737240
<details>
<summary>TPC-DS: <b>Total hot run time: 168861 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 8db568b5100ed397fc6347033402642e90e5509e, data reload: false
query5 4321 623 478 478
query6 428 189 175 175
query7 4957 549 310 310
query8 365 204 196 196
query9 8786 4024 4043 4024
query10 444 312 270 270
query11 5949 2384 2177 2177
query12 156 106 98 98
query13 1290 585 448 448
query14 6412 5432 5072 5072
query14_1 4397 4404 4431 4404
query15 208 201 178 178
query16 1015 446 433 433
query17 1126 722 607 607
query18 2705 490 349 349
query19 209 183 146 146
query20 113 106 107 106
query21 222 143 114 114
query22 13706 13690 13357 13357
query23 17705 16659 16182 16182
query23_1 16389 16379 16334 16334
query24 7576 1768 1307 1307
query24_1 1311 1327 1310 1310
query25 537 423 370 370
query26 1306 326 169 169
query27 2634 573 337 337
query28 4456 2036 2021 2021
query29 1060 618 483 483
query30 309 242 200 200
query31 1119 1066 952 952
query32 112 63 56 56
query33 529 314 242 242
query34 1178 1161 668 668
query35 747 782 686 686
query36 1422 1415 1235 1235
query37 165 102 87 87
query38 3235 3154 3037 3037
query39 938 922 906 906
query39_1 895 872 863 863
query40 220 123 100 100
query41 64 65 61 61
query42 94 93 93 93
query43 333 324 284 284
query44
query45 197 189 182 182
query46 1072 1241 742 742
query47 2433 2387 2236 2236
query48 401 419 294 294
query49 620 494 348 348
query50 996 371 260 260
query51 4460 4438 4316 4316
query52 87 87 76 76
query53 242 267 188 188
query54 258 220 195 195
query55 77 75 68 68
query56 243 220 225 220
query57 1451 1414 1318 1318
query58 244 214 210 210
query59 1568 1674 1533 1533
query60 281 249 215 215
query61 152 152 150 150
query62 706 641 589 589
query63 227 186 186 186
query64 2511 788 626 626
query65
query66 1781 478 343 343
query67 29806 29748 29579 29579
query68
query69 421 302 265 265
query70 911 908 975 908
query71 306 220 209 209
query72 2914 2629 2333 2333
query73 872 804 453 453
query74 5137 4970 4815 4815
query75 2649 2597 2233 2233
query76 2344 1139 761 761
query77 345 380 289 289
query78 12271 12446 11835 11835
query79 1269 1066 770 770
query80 518 463 384 384
query81 446 282 241 241
query82 231 156 119 119
query83 270 284 244 244
query84
query85 840 515 401 401
query86 331 304 265 265
query87 3380 3423 3149 3149
query88 3636 2747 2717 2717
query89 406 394 322 322
query90 2157 192 179 179
query91 168 149 129 129
query92 66 60 59 59
query93 1465 1470 848 848
query94 534 343 318 318
query95 717 487 344 344
query96 1105 885 367 367
query97 2712 2694 2561 2561
query98 209 206 198 198
query99 1165 1157 1026 1026
Total cold run time: 250849 ms
Total hot run time: 168861 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3399953617
##########
gensrc/thrift/Partitions.thrift:
##########
@@ -119,3 +153,5 @@ struct TDataPartition {
3: optional list<TRangePartition> partition_infos
4: optional TMergePartitionInfo merge_partition_info
}
+
Review Comment:
`git diff --check` reports `gensrc/thrift/Partitions.thrift:156: new blank line at EOF` after this closing brace. Please remove the extra trailing blank line(s) so the file ends here.
##########
regression-test/data/query_p0/join/test_multilevel_join_agg_local_shuffle.out:
##########
@@ -0,0 +1,815 @@
+-- This file is automatically generated. You should know what you did if you want to edit this
+-- !bucket_shuffle_broadcast_agg_stage_1_result_on --
+1 52 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_shuffle_broadcast_agg_stage_1_result_off --
+1 52 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_shuffle_broadcast_agg_stage_2_result_on --
+1 52 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_shuffle_broadcast_agg_stage_2_result_off --
+1 52 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_shuffle_broadcast_agg_stage_3_result_on --
+1 52 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_shuffle_broadcast_agg_stage_3_result_off --
+1 52 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !shuffle_broadcast_broadcast_agg_stage_1_result_on --
+1 10 62 10
+2 5 54 20
+3 5 38 30
+4 6 49 40
+
+-- !shuffle_broadcast_broadcast_agg_stage_1_result_off --
+1 10 62 10
+2 5 54 20
+3 5 38 30
+4 6 49 40
+
+-- !shuffle_broadcast_broadcast_agg_stage_2_result_on --
+1 10 62 10
+2 5 54 20
+3 5 38 30
+4 6 49 40
+
+-- !shuffle_broadcast_broadcast_agg_stage_2_result_off --
+1 10 62 10
+2 5 54 20
+3 5 38 30
+4 6 49 40
+
+-- !shuffle_broadcast_broadcast_agg_stage_3_result_on --
+1 10 62 10
+2 5 54 20
+3 5 38 30
+4 6 49 40
+
+-- !shuffle_broadcast_broadcast_agg_stage_3_result_off --
+1 10 62 10
+2 5 54 20
+3 5 38 30
+4 6 49 40
+
+-- !bucket_broadcast_shuffle_agg_stage_1_result_on --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_broadcast_shuffle_agg_stage_1_result_off --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_broadcast_shuffle_agg_stage_2_result_on --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_broadcast_shuffle_agg_stage_2_result_off --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_broadcast_shuffle_agg_stage_3_result_on --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_broadcast_shuffle_agg_stage_3_result_off --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !alternating_bucket_bucket_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_bucket_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_bucket_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_bucket_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_bucket_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_bucket_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_shuffle_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_shuffle_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_shuffle_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_shuffle_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_shuffle_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_shuffle_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_broadcast_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_broadcast_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_broadcast_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_broadcast_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_broadcast_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_broadcast_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_bucket_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_bucket_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_bucket_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_bucket_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_bucket_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_bucket_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_shuffle_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_shuffle_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_shuffle_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_shuffle_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_shuffle_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_shuffle_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_broadcast_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_broadcast_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_broadcast_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_broadcast_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_broadcast_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_broadcast_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_bucket_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_bucket_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_bucket_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_bucket_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_bucket_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_bucket_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_shuffle_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_shuffle_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_shuffle_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_shuffle_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_shuffle_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_shuffle_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_broadcast_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_broadcast_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_broadcast_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_broadcast_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_broadcast_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_broadcast_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !window_union_all_bucket_row_number_result_on --
+1 52 4 40
+2 21 3 60
+3 16 2 60
+4 20 2 80
+
+-- !window_union_all_bucket_row_number_result_off --
+1 52 4 40
+2 21 3 60
+3 16 2 60
+4 20 2 80
+
+-- !window_union_all_bucket_window_sum_result_on --
+1 10 13 40
+2 6 7 60
+3 3 8 60
+4 3 10 80
+
+-- !window_union_all_bucket_window_sum_result_off --
+1 10 13 40
+2 6 7 60
+3 3 8 60
+4 3 10 80
+
+-- !window_union_all_shuffle_row_number_result_on --
+1 208 8 44
+2 21 3 21
+3 16 2 16
+4 20 2 18
+
+-- !window_union_all_shuffle_row_number_result_off --
+1 208 8 44
+2 21 3 21
+3 16 2 16
+4 20 2 18
+
+-- !window_union_all_shuffle_window_sum_result_on --
+1 36 26 44
+2 6 7 21
+3 3 8 16
+4 3 10 18
+
+-- !window_union_all_shuffle_window_sum_result_off --
+1 36 26 44
+2 6 7 21
+3 3 8 16
+4 3 10 18
+
+-- !window_union_all_broadcast_row_number_result_on --
+1 52 4 40
+2 21 3 60
+3 16 2 60
+4 20 2 80
+
+-- !window_union_all_broadcast_row_number_result_off --
+1 52 4 40
+2 21 3 60
+3 16 2 60
+4 20 2 80
+
+-- !window_union_all_broadcast_window_sum_result_on --
+1 10 13 40
+2 6 7 60
+3 3 8 60
+4 3 10 80
+
+-- !window_union_all_broadcast_window_sum_result_off --
+1 10 13 40
+2 6 7 60
+3 3 8 60
+4 3 10 80
+
+-- !window_except_bucket_row_number_result_on --
+1 10 2 20
+2 10 2 40
+3 5 1 30
+4 6 1 40
+
+-- !window_except_bucket_row_number_result_off --
+1 10 2 20
+2 10 2 40
+3 5 1 30
+4 6 1 40
+
+-- !window_except_bucket_window_sum_result_on --
+1 3 5 20
+2 3 5 40
+3 1 5 30
+4 1 6 40
+
+-- !window_except_bucket_window_sum_result_off --
+1 3 5 20
+2 3 5 40
+3 1 5 30
+4 1 6 40
+
+-- !window_except_shuffle_row_number_result_on --
+1 40 4 22
+2 10 2 14
+3 5 1 8
+4 6 1 9
+
+-- !window_except_shuffle_row_number_result_off --
+1 40 4 22
+2 10 2 14
+3 5 1 8
+4 6 1 9
+
+-- !window_except_shuffle_window_sum_result_on --
+1 10 10 22
+2 3 5 14
+3 1 5 8
+4 1 6 9
+
+-- !window_except_shuffle_window_sum_result_off --
+1 10 10 22
+2 3 5 14
+3 1 5 8
+4 1 6 9
+
+-- !window_except_broadcast_row_number_result_on --
+1 10 2 20
+2 10 2 40
+3 5 1 30
+4 6 1 40
+
+-- !window_except_broadcast_row_number_result_off --
+1 10 2 20
+2 10 2 40
+3 5 1 30
+4 6 1 40
+
+-- !window_except_broadcast_window_sum_result_on --
+1 3 5 20
+2 3 5 40
+3 1 5 30
+4 1 6 40
+
+-- !window_except_broadcast_window_sum_result_off --
+1 3 5 20
+2 3 5 40
+3 1 5 30
+4 1 6 40
+
+-- !window_intersect_bucket_row_number_result_on --
+
+-- !window_intersect_bucket_row_number_result_off --
+
+-- !window_intersect_bucket_window_sum_result_on --
+
+-- !window_intersect_bucket_window_sum_result_off --
+
+-- !window_intersect_shuffle_row_number_result_on --
+
+-- !window_intersect_shuffle_row_number_result_off --
+
+-- !window_intersect_shuffle_window_sum_result_on --
+
+-- !window_intersect_shuffle_window_sum_result_off --
+
+-- !window_intersect_broadcast_row_number_result_on --
+
+-- !window_intersect_broadcast_row_number_result_off --
+
+-- !window_intersect_broadcast_window_sum_result_on --
+
+-- !window_intersect_broadcast_window_sum_result_off --
+
+-- !bucket_broadcast_agg_result_on --
+1 26 10
+2 9 20
+3 8 30
+4 10 40
+
+-- !bucket_broadcast_agg_result_off --
+1 26 10
+2 9 20
+3 8 30
+4 10 40
+
+-- !partitioned_broadcast_agg_result_on --
+1 32 10
+2 19 20
+3 13 30
+4 15 40
+
+-- !partitioned_broadcast_agg_result_off --
+1 32 10
+2 19 20
+3 13 30
+4 15 40
+
+-- !bucket_partitioned_agg_result_on --
+1 52 44
+2 9 14
+3 8 8
+4 10 9
+
+-- !bucket_partitioned_agg_result_off --
+1 52 44
+2 9 14
+3 8 8
+4 10 9
+
+-- !all_three_multilevel_agg_result_on --
+1 96 10
+2 23 20
+3 16 30
+4 19 40
+
+-- !all_three_multilevel_agg_result_off --
+1 96 10
+2 23 20
+3 16 30
+4 19 40
+
+-- !agg_join_agg_mix_result_on --
+1 10 11 10
+2 5 7 20
+3 5 8 30
+4 6 9 40
+
+-- !agg_join_agg_mix_result_off --
+1 10 11 10
+2 5 7 20
+3 5 8 30
+4 6 9 40
+
+-- !double_broadcast_after_bucket_result_on --
+1 26 20
+2 9 40
+3 8 60
+4 10 80
+
+-- !double_broadcast_after_bucket_result_off --
+1 26 20
+2 9 40
+3 8 60
+4 10 80
+
+-- !partitioned_join_between_two_aggs_then_broadcast_result_on --
+1 16 10
+2 12 20
+3 13 30
+4 15 40
+
+-- !partitioned_join_between_two_aggs_then_broadcast_result_off --
+1 16 10
+2 12 20
+3 13 30
+4 15 40
+
+-- !bucket_shuffle_broadcast_two_stage_agg_result_on --
+1 52 44 10
+2 9 14 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_shuffle_broadcast_two_stage_agg_result_off --
+1 52 44 10
+2 9 14 20
+3 8 8 30
+4 10 9 40
+
+-- !left_join_null_preserving_with_multilevel_agg_result_on --
+1 5 44
+2 5 14
+3 5 8
+4 6 9
+
+-- !left_join_null_preserving_with_multilevel_agg_result_off --
+1 5 44
+2 5 14
+3 5 8
+4 6 9
+
+-- !seven_layer_bucket_shuffle_broadcast_result_on --
+1 37 10
+2 16 20
+3 16 30
+4 19 40
+
+-- !seven_layer_bucket_shuffle_broadcast_result_off --
+1 37 10
+2 16 20
+3 16 30
+4 19 40
+
+-- !eight_layer_mixed_join_agg_chain_result_on --
+1 52 44 20
+2 9 14 40
+3 8 8 60
+4 10 9 80
+
+-- !eight_layer_mixed_join_agg_chain_result_off --
+1 52 44 20
+2 9 14 40
+3 8 8 60
+4 10 9 80
+
+-- !seven_layer_left_join_mix_result_on --
+1 10 11 10
+2 5 7 20
+3 5 8 30
+4 6 9 40
+
+-- !seven_layer_left_join_mix_result_off --
+1 10 11 10
+2 5 7 20
+3 5 8 30
+4 6 9 40
+
+-- !broadcast_shuffle_broadcast_nested_agg_result_on --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !broadcast_shuffle_broadcast_nested_agg_result_off --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !window_union_join_agg_result_on --
+1 13 14
+2 7 23
+3 8 32
+4 10 42
+
+-- !window_union_join_agg_result_off --
+1 13 14
+2 7 23
+3 8 32
+4 10 42
+
+-- !window_except_join_agg_result_on --
+1 1 10
+2 1 20
+3 1 30
+
+-- !window_except_join_agg_result_off --
+1 1 10
+2 1 20
+3 1 30
+
+-- !window_intersect_shuffle_agg_result_on --
+1 22 2
+2 7 1
+3 8 1
+4 9 1
+
+-- !window_intersect_shuffle_agg_result_off --
+1 22 2
+2 7 1
+3 8 1
+4 9 1
+
+-- !window_union_except_broadcast_agg_result_on --
+1 13 4 10
+2 7 3 20
+3 8 2 30
+4 10 2 40
+
+-- !window_union_except_broadcast_agg_result_off --
+1 13 4 10
+2 7 3 20
+3 8 2 30
+4 10 2 40
+
+-- !window_setop_join_agg_chain_result_on --
+1 20 2 22
+2 10 2 14
+3 5 1 8
+4 6 1 9
+
+-- !window_setop_join_agg_chain_result_off --
+1 20 2 22
+2 10 2 14
+3 5 1 8
+4 6 1 9
+
Review Comment:
`git diff --check` reports `regression-test/data/query_p0/join/test_multilevel_join_agg_local_shuffle.out:815: new blank line at EOF` after this result row. Please regenerate or trim the expected output so it ends here.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4686337234
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/9ccb7f30b5fb41183c99a77cc3de3311715aa9b6_9ccb7f30b5fb41183c99a77cc3de3311715aa9b6_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/9ccb7f30b5fb41183c99a77cc3de3311715aa9b6_9ccb7f30b5fb41183c99a77cc3de3311715aa9b6_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 77.32% (1889/2443) |
| Line Coverage | 64.41% (33959/52725) |
| Region Coverage | 64.81% (17464/26948) |
| Branch Coverage | 53.97% (9346/17316) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4686601063
<details>
<summary>TPC-H: <b>Total hot run time: 29135 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 9ccb7f30b5fb41183c99a77cc3de3311715aa9b6, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17616 4034 4006 4006
q2 q3 10952 1397 818 818
q4 4736 478 339 339
q5 8484 901 580 580
q6 353 180 138 138
q7 946 834 622 622
q8 10931 1633 1632 1632
q9 7224 4549 4555 4549
q10 6836 1839 1512 1512
q11 437 267 249 249
q12 645 424 286 286
q13 18172 3392 2761 2761
q14 262 260 231 231
q15 q16 818 779 716 716
q17 937 939 909 909
q18 6862 5703 5637 5637
q19 1156 1212 992 992
q20 695 437 264 264
q21 6037 2749 2589 2589
q22 451 392 305 305
Total cold run time: 104550 ms
Total hot run time: 29135 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4774 4719 4949 4719
q2 q3 4849 5270 4689 4689
q4 2138 2180 1420 1420
q5 4948 4825 4620 4620
q6 239 177 131 131
q7 1855 1768 1578 1578
q8 2378 1962 1941 1941
q9 7563 7600 7617 7600
q10 4804 4696 4209 4209
q11 538 392 363 363
q12 818 747 525 525
q13 3019 3317 2817 2817
q14 281 277 249 249
q15 q16 680 700 603 603
q17 1289 1264 1256 1256
q18 7469 6762 6991 6762
q19 1098 1120 1092 1092
q20 2268 2233 1973 1973
q21 5276 4577 4419 4419
q22 516 457 402 402
Total cold run time: 56800 ms
Total hot run time: 51368 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4714370758
@BiteTheDDDDt (放到顶层,代码行上的线程回复你那边一直没刷出来——我之前误删了发到 thread 根上的那条,留下的是回复到子评论上的、GitHub UI 渲染不出来,抱歉)
> 我没懂如果fe一开始就把所有local exchange规划好,所有算子的并行度规划好,直接下发。be为什么需要_create_deferred_local_exchangers和_propagate_local_exchange_num_tasks?
>
> 这个是现在设计出来就是要先做折中方案的,还是可以直接一把做全面?_propagate_local_exchange_num_tasks和_create_deferred_local_exchangers这两块新增的应该不是为了灰度/回滚吧。
是的,这两块不是为了灰度/回滚——是 FE 接管 local exchange 的**当前折中**,不是一步到位。
要让 FE 把"每个算子的并发"也规划好直接下发,本质上需要 FE 自己切 pipeline、自己控制每条 pipeline 的并发(`num_tasks` 是 pipeline 粒度的,只在 pipeline 边界即 local exchange / pipeline breaker 处变化),BE 就不再规划 pipeline 了。那等于把 BE 的整套 pipeline 构建(`_build_pipeline`、pipeline-breaker 判定、`num_tasks` 推导、dependency 接线,还要兼顾 spill / runtime filter / shared state)搬到 FE,并改 thrift 契约携带完整 pipeline 结构——战线太长、风险太大。这套 FE 近似等价实现已经做了大半年、近 1 万行代码,没法一把全做。
所以现状是:FE 规划 LE 的**位置/类型/分布**(决定 plan 正确性,过去正是 BE/FE 分歧 bug 的根源),`enable_local_shuffle_planner=true` 时 BE 的 `_plan_local_exchange` 整个跳过;但每条 pipeline 的 `num_tasks` 仍由 BE 的 `_propagate_local_exchange_num_tasks` 拓扑推导,`_create_deferred_local_exchangers` 因为 exchanger 的 `num_partitions` 要等 `num_tasks` 定了才能建、所以延后创建。终态是 FE 连 `num_tasks` 也算好下发、BE 的 propagate 退化成 assert(只校验)——那是后续单独的大改。
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3413421247
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -290,6 +290,32 @@ Status PipelineFragmentContext::_build_and_prepare_full_pipeline(ThreadPool* thr
RETURN_IF_ERROR(_build_pipelines(_runtime_state->obj_pool(), *_query_ctx->desc_tbl,
&_root_op, root_pipeline));
+ // Propagate _num_instances from LOCAL_EXCHANGE pipelines to ancestor pipelines
+ // that inherited reduced num_tasks from a serial operator.
+ _propagate_local_exchange_num_tasks();
+
+ // Create deferred local exchangers now that all pipelines have final num_tasks.
+ RETURN_IF_ERROR(_create_deferred_local_exchangers());
Review Comment:
是的,这两块不是为了灰度/回滚——是 FE 接管 local exchange 的**当前折中**,不是一步到位。
要让 FE 把"每个算子的并发"也规划好直接下发,本质上需要 FE 自己切 pipeline、自己控制每条 pipeline 的并发(num_tasks 是 pipeline 粒度的,只在 pipeline 边界即 local exchange / pipeline breaker 处变化),BE 就不再规划 pipeline 了。那等于把 BE 的整套 pipeline 构建(`_build_pipeline`、pipeline-breaker 判定、num_tasks 推导、dependency 接线,还要兼顾 spill / runtime filter / shared state)搬到 FE,并改 thrift 契约携带完整 pipeline 结构——战线太长、风险太大。这套 FE 近似等价实现已经做了大半年、近 1 万行代码,没法一把全做。
所以现状是:FE 规划 LE 的**位置/类型/分布**(决定 plan 正确性,过去正是 BE/FE 分歧 bug 的根源),`enable_local_shuffle_planner=true` 时 BE 的 `_plan_local_exchange` 整个跳过;但每条 pipeline 的 num_tasks 仍由 BE 的 `_propagate_local_exchange_num_tasks` 拓扑推导,`_create_deferred_local_exchangers` 因为 exchanger 的 `num_partitions` 要等 num_tasks 定了才能建、所以延后创建。终态是 FE 连 num_tasks 也算好下发、BE 的 propagate 退化成 assert(只校验)——那是后续单独的大改。
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4765843210
# BE UT Coverage Report
Increment line coverage `15.27% (62/406)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 54.43% (21365/39252) |
| Line Coverage | 38.06% (204394/537052) |
| Region Coverage | 34.05% (160359/470985) |
| Branch Coverage | 35.05% (70209/200333) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4765205196
/review
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4766186970
# BE UT Coverage Report
Increment line coverage `15.27% (62/406)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 54.42% (21361/39252) |
| Line Coverage | 38.02% (204203/537052) |
| Region Coverage | 34.03% (160296/470985) |
| Branch Coverage | 35.03% (70175/200333) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4739418360
# FE Regression Coverage Report
Increment line coverage ` 79.68% (451/566)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_0436cc4cb573b104a59408a6169bd145bcfeff6c_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_0436cc4cb573b104a59408a6169bd145bcfeff6c_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4733128023
<details>
<summary>TPC-H: <b>Total hot run time: 29850 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit c2c51332d7f90c80ccabf77b27c23317d636a128, data reload: false
------ Round 1 ----------------------------------
============================================
q1 17765 4236 4324 4236
q2 2005 317 190 190
q3 10891 1509 849 849
q4 4807 474 356 356
q5 8433 882 565 565
q6 334 182 142 142
q7 835 838 627 627
q8 10582 1759 1687 1687
q9 5768 4543 4561 4543
q10 6826 1853 1501 1501
q11 452 280 239 239
q12 650 426 300 300
q13 18145 3495 2787 2787
q14 274 272 238 238
q15 q16 784 787 710 710
q17 978 885 1030 885
q18 6979 5718 5620 5620
q19 1164 1351 1209 1209
q20 504 413 266 266
q21 5475 2714 2601 2601
q22 442 361 299 299
Total cold run time: 104093 ms
Total hot run time: 29850 ms
----- Round 2, with runtime_filter_mode=off -----
============================================
q1 4553 4485 4433 4433
q2 330 360 231 231
q3 4677 5030 4559 4559
q4 2196 2292 1398 1398
q5 4599 4401 4882 4401
q6 264 208 147 147
q7 1981 1828 1620 1620
q8 2597 2286 2294 2286
q9 8170 8178 8110 8110
q10 5050 4800 4340 4340
q11 608 417 393 393
q12 747 752 552 552
q13 3320 3703 3003 3003
q14 314 317 292 292
q15 q16 749 773 716 716
q17 1430 1389 1369 1369
q18 8250 7234 6900 6900
q19 1112 1106 1151 1106
q20 2230 2265 1954 1954
q21 5450 4784 4600 4600
q22 541 460 409 409
Total cold run time: 59168 ms
Total hot run time: 52819 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4734095192
# BE Regression && UT Coverage Report
Increment line coverage `89.41% (363/406)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_c2c51332d7f90c80ccabf77b27c23317d636a128_merge/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_c2c51332d7f90c80ccabf77b27c23317d636a128_merge/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 72.32% (27676/38267) |
| Line Coverage | 55.73% (296935/532849) |
| Region Coverage | 52.55% (248183/472248) |
| Branch Coverage | 53.67% (107352/200029) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4738178778
# BE Regression && UT Coverage Report
Increment line coverage `89.41% (363/406)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_c2c51332d7f90c80ccabf77b27c23317d636a128_merge/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_c2c51332d7f90c80ccabf77b27c23317d636a128_merge/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 72.32% (27676/38267) |
| Line Coverage | 55.73% (296941/532849) |
| Region Coverage | 52.57% (248267/472248) |
| Branch Coverage | 53.67% (107358/200029) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4738192904
/review
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4494902599
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4505410948
<details>
<summary>TPC-H: <b>Total hot run time: 31438 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit f2d43a29db5822a72153fd88cbcd8bead33cbc6d, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17614 3875 3866 3866
q2 q3 10964 1519 811 811
q4 4816 474 354 354
q5 10645 2302 2071 2071
q6 386 179 135 135
q7 943 776 657 657
q8 9591 1759 1602 1602
q9 7226 5020 5003 5003
q10 6584 2094 1819 1819
q11 441 270 242 242
q12 646 434 295 295
q13 18133 3456 2831 2831
q14 266 264 235 235
q15 q16 817 792 713 713
q17 962 995 945 945
q18 6830 5827 5655 5655
q19 1200 1355 1104 1104
q20 524 407 259 259
q21 5586 2699 2529 2529
q22 447 363 312 312
Total cold run time: 104621 ms
Total hot run time: 31438 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4183 4080 4092 4080
q2 q3 4582 4943 4277 4277
q4 2125 2228 1378 1378
q5 4435 4259 5173 4259
q6 254 194 144 144
q7 1973 1755 1584 1584
q8 2385 2131 2082 2082
q9 7865 7824 7898 7824
q10 4606 4472 4098 4098
q11 592 408 384 384
q12 733 741 537 537
q13 3328 3628 2962 2962
q14 294 294 297 294
q15 q16 724 739 643 643
q17 1362 1309 1309 1309
q18 7928 7090 6700 6700
q19 1171 1096 1113 1096
q20 2264 2224 1955 1955
q21 5334 4625 4528 4528
q22 518 465 424 424
Total cold run time: 56656 ms
Total hot run time: 50558 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4506848943
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4485864440
# BE UT Coverage Report
Increment line coverage `11.11% (46/414)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/4affd222d6550dd103b7829a6fced0b7d45d317a_4affd222d6550dd103b7829a6fced0b7d45d317a/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/4affd222d6550dd103b7829a6fced0b7d45d317a_4affd222d6550dd103b7829a6fced0b7d45d317a/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 53.56% (20733/38712) |
| Line Coverage | 37.18% (196066/527339) |
| Region Coverage | 33.52% (153723/458547) |
| Branch Coverage | 34.55% (66975/193832) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4550835013
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4674684335
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4675427140
# FE UT Coverage Report
Increment line coverage ` 74.48% (429/576)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_f056ebbed42446ea468bcc6891c05520bdfb31c3/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_f056ebbed42446ea468bcc6891c05520bdfb31c3/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4675118864
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/f056ebbed42446ea468bcc6891c05520bdfb31c3_f056ebbed42446ea468bcc6891c05520bdfb31c3_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/f056ebbed42446ea468bcc6891c05520bdfb31c3_f056ebbed42446ea468bcc6891c05520bdfb31c3_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 78.39% (1915/2443) |
| Line Coverage | 64.90% (34216/52725) |
| Region Coverage | 65.33% (17604/26948) |
| Branch Coverage | 54.01% (9352/17316) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4669961175
# FE UT Coverage Report
Increment line coverage ` 72.79% (428/588)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_7c898c900506116cfb44b47189025b6fbfa7e44c/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_7c898c900506116cfb44b47189025b6fbfa7e44c/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3411912827
##########
be/src/exec/operator/partition_sort_sink_operator.h:
##########
@@ -95,9 +95,10 @@ class PartitionSortSinkOperatorX final : public DataSinkOperatorX<PartitionSortS
Status sink_impl(RuntimeState* state, Block* in_block, bool eos) override;
DataDistribution required_data_distribution(RuntimeState* /*state*/) const override {
if (_topn_phase == TPartTopNPhase::TWO_PHASE_GLOBAL) {
- return DataDistribution(ExchangeType::HASH_SHUFFLE, _distribute_exprs);
+ return DataDistribution(TLocalPartitionType::GLOBAL_EXECUTION_HASH_SHUFFLE,
Review Comment:
why all HASH_SHUFFLE update to `GLOBAL_EXECUTION_HASH_SHUFFLE`? When will `LOCAL_EXECUTION_HASH_SHUFFLE` be generated?
##########
be/src/exec/pipeline/pipeline.h:
##########
@@ -68,16 +68,15 @@ class Pipeline : public std::enable_shared_from_this<Pipeline> {
[[nodiscard]] PipelineId id() const { return _pipeline_id; }
- static bool is_hash_exchange(ExchangeType idx) {
- return idx == ExchangeType::HASH_SHUFFLE || idx == ExchangeType::BUCKET_HASH_SHUFFLE;
+ static bool is_hash_exchange(TLocalPartitionType::type idx) {
+ return is_shuffled_exchange(idx);
}
// For HASH_SHUFFLE, BUCKET_HASH_SHUFFLE, and ADAPTIVE_PASSTHROUGH,
Review Comment:
need update this comment
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -810,52 +1018,59 @@ Status PipelineFragmentContext::_add_local_exchange_impl(
sink_id, local_exchange_id, use_global_hash_shuffle ? _total_instances : _num_instances,
data_distribution.partition_exprs, bucket_seq_to_instance_idx);
if (bucket_seq_to_instance_idx.empty() &&
- data_distribution.distribution_type == ExchangeType::BUCKET_HASH_SHUFFLE) {
- data_distribution.distribution_type = ExchangeType::HASH_SHUFFLE;
+ data_distribution.distribution_type == TLocalPartitionType::BUCKET_HASH_SHUFFLE) {
+ data_distribution.distribution_type =
+ use_global_hash_shuffle ? TLocalPartitionType::GLOBAL_EXECUTION_HASH_SHUFFLE
+ : TLocalPartitionType::LOCAL_EXECUTION_HASH_SHUFFLE;
+ }
+ if (!use_global_hash_shuffle &&
+ data_distribution.distribution_type == TLocalPartitionType::GLOBAL_EXECUTION_HASH_SHUFFLE) {
+ data_distribution.distribution_type = TLocalPartitionType::LOCAL_EXECUTION_HASH_SHUFFLE;
Review Comment:
so the operator's GLOBAL_EXECUTION_HASH_SHUFFLE will be updated according to `use_global_hash_shuffle` here?
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -690,6 +716,188 @@ Status PipelineFragmentContext::_build_pipelines(ObjectPool* pool, const Descrip
return Status::OK();
}
+Status PipelineFragmentContext::_create_deferred_local_exchangers() {
+ for (auto& info : _deferred_exchangers) {
+ // DANGER ZONE — do not "fix" this line without reading the history.
+ //
+ // sender_count seeds Exchanger::_running_sink_operators, which the source side
+ // waits to reach 0 via sub_running_sink_operators on each sink LocalState close.
+ // The correct value is THIS pipeline-instance's sink task count, which is exactly
+ // info.upstream_pipe->num_tasks() — one PipelineTask per task, one close per task.
+ //
+ // Tempting wrong fix #1: `std::max(num_tasks, _num_instances)` to mirror the
+ // BE-planned path in _add_local_exchange_impl (~line 1023). THIS BREAKS the
+ // common FE-planned shape of `serial scan → LE(PT) → ...`: upstream_pipe
+ // genuinely has num_tasks=1, only 1 close arrives, but seed becomes
+ // _num_instances so _running_sink_operators never reaches 0 — downstream
+ // sources hang on SHUFFLE_DATA_DEPENDENCY (e.g. MTMV refresh from
+ // mtmv_up_down_job_p0/load.groovy stays at Status=RUNNING; build 949402
+ // regressed exactly this way). BE-planned mode uses max() because its
+ // `cur_pipe` is the source-side pipeline (always raised to _num_instances by
+ // add_pipeline) — not analogous to our `upstream_pipe` here, which is the
+ // sink-side pipeline that may legitimately stay at 1 for serial sources.
+ //
+ // Tempting wrong fix #2: multiply by _num_instances on the theory shared_state
+ // is shared across all instances. Same hang — each fragment-instance
+ // PipelineFragmentContext has its OWN _op_id_to_shared_state map, so the
+ // exchanger is per-instance, not per-BE. num_tasks() is already the right
+ // close-count for one instance.
+ //
+ // If a hang shows up with `_running_sink_operators < 0`, the bug is upstream:
+ // _propagate_local_exchange_num_tasks left num_tasks too low (or too high) for
+ // this fragment shape. Fix THAT pass, not this seed value.
+ const int sender_count = info.upstream_pipe->num_tasks();
+ switch (info.partition_type) {
+ case TLocalPartitionType::LOCAL_EXECUTION_HASH_SHUFFLE:
+ case TLocalPartitionType::GLOBAL_EXECUTION_HASH_SHUFFLE:
+ info.shared_state->exchanger = ShuffleExchanger::create_unique(
+ sender_count, _num_instances, info.num_partitions, info.free_blocks_limit,
+ info.partition_type);
+ break;
+ case TLocalPartitionType::BUCKET_HASH_SHUFFLE:
+ info.shared_state->exchanger = BucketShuffleExchanger::create_unique(
+ sender_count, _num_instances, info.num_partitions, info.free_blocks_limit);
+ break;
+ case TLocalPartitionType::PASSTHROUGH:
+ info.shared_state->exchanger = PassthroughExchanger::create_unique(
+ sender_count, _num_instances, info.free_blocks_limit);
+ break;
+ case TLocalPartitionType::BROADCAST:
+ info.shared_state->exchanger = BroadcastExchanger::create_unique(
+ sender_count, _num_instances, info.free_blocks_limit);
+ break;
+ case TLocalPartitionType::PASS_TO_ONE:
+ if (_runtime_state->enable_share_hash_table_for_broadcast_join()) {
+ info.shared_state->exchanger = PassToOneExchanger::create_unique(
+ sender_count, _num_instances, info.free_blocks_limit);
+ } else {
+ info.shared_state->exchanger = BroadcastExchanger::create_unique(
+ sender_count, _num_instances, info.free_blocks_limit);
+ }
+ break;
+ case TLocalPartitionType::ADAPTIVE_PASSTHROUGH:
+ info.shared_state->exchanger = AdaptivePassthroughExchanger::create_unique(
+ sender_count, _num_instances, info.free_blocks_limit);
+ break;
+ case TLocalPartitionType::NOOP:
+ case TLocalPartitionType::LOCAL_MERGE_SORT:
Review Comment:
so, this is a regression for plan_by_fe? what is the reason?
##########
be/src/exec/pipeline/dependency.h:
##########
@@ -832,50 +832,44 @@ struct SetSharedState : public BasicSharedState {
Status hash_table_init();
};
-enum class ExchangeType : uint8_t {
- NOOP = 0,
- // Shuffle data by Crc32CHashPartitioner
- HASH_SHUFFLE = 1,
- // Round-robin passthrough data blocks.
- PASSTHROUGH = 2,
- // Shuffle data by Crc32HashPartitioner<ShuffleChannelIds> (e.g. same as storage engine).
- BUCKET_HASH_SHUFFLE = 3,
- // Passthrough data blocks to all channels.
- BROADCAST = 4,
- // Passthrough data to channels evenly in an adaptive way.
- ADAPTIVE_PASSTHROUGH = 5,
- // Send all data to the first channel.
- PASS_TO_ONE = 6,
-};
+inline bool is_shuffled_exchange(TLocalPartitionType::type idx) {
+ return idx == TLocalPartitionType::GLOBAL_EXECUTION_HASH_SHUFFLE ||
+ idx == TLocalPartitionType::LOCAL_EXECUTION_HASH_SHUFFLE ||
+ idx == TLocalPartitionType::BUCKET_HASH_SHUFFLE;
+}
Review Comment:
same function in `operator.cpp`?
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -723,7 +931,7 @@ Status PipelineFragmentContext::_create_tree_helper(
*root = op;
}
/**
- * `ExchangeType::HASH_SHUFFLE` should be used if an operator is followed by a shuffled operator (shuffled hash join, union operator followed by co-located operators).
+ * `TLocalPartitionType::HASH_SHUFFLE` should be used if an operator is followed by a shuffled operator (shuffled hash join, union operator followed by co-located operators).
Review Comment:
HASH_SHUFFLE no longer existed, update the comment
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -690,6 +716,188 @@ Status PipelineFragmentContext::_build_pipelines(ObjectPool* pool, const Descrip
return Status::OK();
}
+Status PipelineFragmentContext::_create_deferred_local_exchangers() {
+ for (auto& info : _deferred_exchangers) {
+ // DANGER ZONE — do not "fix" this line without reading the history.
+ //
+ // sender_count seeds Exchanger::_running_sink_operators, which the source side
+ // waits to reach 0 via sub_running_sink_operators on each sink LocalState close.
+ // The correct value is THIS pipeline-instance's sink task count, which is exactly
+ // info.upstream_pipe->num_tasks() — one PipelineTask per task, one close per task.
+ //
+ // Tempting wrong fix #1: `std::max(num_tasks, _num_instances)` to mirror the
+ // BE-planned path in _add_local_exchange_impl (~line 1023). THIS BREAKS the
+ // common FE-planned shape of `serial scan → LE(PT) → ...`: upstream_pipe
+ // genuinely has num_tasks=1, only 1 close arrives, but seed becomes
+ // _num_instances so _running_sink_operators never reaches 0 — downstream
+ // sources hang on SHUFFLE_DATA_DEPENDENCY (e.g. MTMV refresh from
+ // mtmv_up_down_job_p0/load.groovy stays at Status=RUNNING; build 949402
Review Comment:
the comment contains meaningless build id 949402
##########
be/src/exec/operator/partitioned_hash_join_probe_operator.h:
##########
@@ -228,16 +228,8 @@ class PartitionedHashJoinProbeOperatorX final
Status pull(doris::RuntimeState* state, Block* output_block, bool* eos) const override;
bool need_more_input_data(RuntimeState* state) const override;
- DataDistribution required_data_distribution(RuntimeState* /*state*/) const override {
- if (_join_op == TJoinOp::NULL_AWARE_LEFT_ANTI_JOIN) {
- return {ExchangeType::NOOP};
- }
- return (_join_distribution == TJoinDistributionType::BUCKET_SHUFFLE ||
- _join_distribution == TJoinDistributionType::COLOCATE
- ? DataDistribution(ExchangeType::BUCKET_HASH_SHUFFLE,
- _distribution_partition_exprs)
- : DataDistribution(ExchangeType::HASH_SHUFFLE,
- _distribution_partition_exprs));
+ DataDistribution required_data_distribution(RuntimeState* state) const override {
+ return _inner_probe_operator->required_data_distribution(state);
Review Comment:
this is a optimization?
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -1796,6 +2011,89 @@ Status PipelineFragmentContext::_create_operator(ObjectPool* pool, const TPlanNo
RETURN_IF_ERROR(cur_pipe->add_operator(op, _parallel_instances));
break;
}
+ case TPlanNodeType::LOCAL_EXCHANGE_NODE: {
+ op = std::make_shared<LocalExchangeSourceOperatorX>(pool, tnode, next_operator_id(), descs);
+ // The downstream pipeline (containing LocalExchangeSource) must have
+ // _num_instances tasks — matching BE-native _inherit_pipeline_properties
+ // which sets pipe_with_source.set_num_tasks(_num_instances).
+ // Without this, when the parent pipeline was reduced by a serial operator
+ // (e.g., serial Exchange with use_serial_exchange=true, or UNPARTITIONED
+ // Exchange), the downstream inherits the reduced num_tasks via
+ // add_pipeline(parent). The deferred exchanger creates _num_instances
+ // channels but only fewer source tasks initialize mem_counters — the
+ // sink round-robins to all channels and crashes on uninitialized ones.
+ auto downstream_num_tasks = _num_instances;
Review Comment:
`downstream_num_tasks` is an unnecessary local variable.
##########
regression-test/suites/nereids_p0/local_shuffle/test_local_shuffle_rqg_bugs.groovy:
##########
@@ -0,0 +1,1560 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * Regression tests for bugs discovered by RQG testing on the local-exchange2 branch.
+ *
+ * These queries triggered "must set shared state" errors or incorrect results
+ * in RQG build 183992. Common conditions:
+ * - use_serial_exchange=true (makes ALL Exchanges serial, not just UNPARTITIONED)
+ * - enable_local_shuffle_planner=true (FE-planned local exchange)
+ * - parallel_pipeline_task_num > 1
+ *
+ * Error types reproduced:
+ * 1. must set shared state, in AGGREGATION_OPERATOR
+ * 2. must set shared state, in SORT_OPERATOR
+ * 3. incorrect results with GROUPING SETS + scalar subquery + window function
+ */
+suite("test_local_shuffle_rqg_bugs") {
+
+ // ============================================================
+ // Table setup — mirrors RQG table structure
+ // 10 buckets to match RQG (replication_num=1 for single-BE testing)
+ // ============================================================
+ sql "DROP TABLE IF EXISTS rqg_t1"
+ sql "DROP TABLE IF EXISTS rqg_t2"
+ sql "DROP TABLE IF EXISTS rqg_t3"
+ sql "DROP TABLE IF EXISTS rqg_t4"
+
+ sql """
+ CREATE TABLE rqg_t1 (
+ pk INT NOT NULL,
+ col_int_undef_signed INT,
+ col_int_undef_signed2 INT,
+ col_int_undef_signed_not_null INT NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ CREATE TABLE rqg_t2 (
+ pk INT NOT NULL,
+ col_int_undef_signed INT,
+ col_int_undef_signed2 INT,
+ col_bigint_undef_signed_not_null BIGINT NOT NULL,
+ col_decimal_38_10__undef_signed_not_null DECIMAL(38,10) NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ // Table for build 184181 GLOBAL_HASH_SHUFFLE bugs — needs varchar + bigint columns
+ sql """
+ CREATE TABLE rqg_t3 (
+ pk INT NOT NULL,
+ col_bigint_undef_signed BIGINT,
+ col_varchar_10__undef_signed VARCHAR(10),
+ col_varchar_64__undef_signed VARCHAR(64)
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ // Second table for FULL OUTER JOIN case (col_bigint_undef_signed_not_null)
+ sql """
+ CREATE TABLE rqg_t4 (
+ pk INT NOT NULL,
+ col_bigint_undef_signed_not_null BIGINT NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ INSERT INTO rqg_t3 VALUES
+ (0, -94, 'Abc', 'hello world'),
+ (1, 672609, 'Xyz', null),
+ (2, -3766684, 'Pqr', 'test string'),
+ (3, 5070261, 'abc', 'another row'),
+ (4, null, 'def', 'value four'),
+ (5, -86, 'XgpxlHBLEM', null),
+ (6, 21910, 'abc', 'they'),
+ (7, -63, 'zzzz', 'some text'),
+ (8, -8276281, 'AHlvNtoGLO', 'longer string here'),
+ (9, -101, 'mid', 'final row')
+ """
+
+ sql """
+ INSERT INTO rqg_t4 VALUES
+ (0, 0), (1, 1), (2, 2), (3, 3), (4, 4),
+ (5, 5), (6, 6), (7, 7), (8, 8), (9, 9),
+ (10, 2), (11, 2), (12, 2), (13, 3), (14, 4),
+ (15, 5), (16, 2), (17, 2), (18, 2), (19, 9)
+ """
+
+ // Insert enough rows to exercise multiple pipeline tasks
+ sql """
+ INSERT INTO rqg_t1 VALUES
+ (0, 0, 10, 0), (1, 1, 11, 1), (2, 2, 12, 2), (3, 3, 13, 3),
+ (4, 4, 14, 4), (5, 5, 15, 5), (6, 6, 16, 6), (7, 7, 17, 7),
+ (8, 8, 18, 8), (9, 9, 19, 9), (10, 0, 20, 10), (11, 1, 21, 11),
+ (12, 2, 22, 12), (13, 3, 23, 13), (14, 4, 24, 14), (15, 5, 25, 15),
+ (16, 6, 26, 16), (17, 7, 27, 17), (18, 8, 28, 18), (19, 9, 29, 19)
+ """
+
+ sql """
+ INSERT INTO rqg_t2 VALUES
+ (0, 0, 10, 100, 1.5), (1, 1, 11, 101, 2.5), (2, 2, 12, 102, 3.5),
+ (3, 3, 13, 103, 4.5), (4, 4, 14, 104, 5.5), (5, 5, 15, 105, 6.5),
+ (6, 6, 16, 106, 7.5), (7, 7, 17, 107, 8.5), (8, 8, 18, 108, 9.5),
+ (9, 9, 19, 109, 10.5), (10, 0, 20, 110, 11.5), (11, 1, 21, 111, 12.5),
+ (12, 2, 22, 112, 13.5), (13, 3, 23, 113, 14.5), (14, 4, 24, 114, 15.5),
+ (15, 5, 25, 115, 16.5), (16, 6, 26, 116, 17.5), (17, 7, 27, 117, 18.5),
+ (18, 8, 28, 118, 19.5), (19, 9, 29, 119, 20.5)
+ """
+
+ // Wait for data to be visible
+ Thread.sleep(5000)
Review Comment:
Use a check instead of the unstable sleep
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3412980337
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AssertNumRowsNode.java:
##########
@@ -78,7 +84,17 @@ public int getNumInstances() {
}
@Override
- public boolean isSerialOperator() {
+ public boolean isSerialNode() {
return true;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(PlanTranslatorContext translatorContext,
+ PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+
+ Pair<PlanNode, LocalExchangeType> enforceResult = enforceRequire(
+ translatorContext, children.get(0), 0, LocalExchangeTypeRequire.requirePassthrough());
Review Comment:
i think it should require NOOP? because it only produce one row
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4707471189
<details>
<summary>TPC-DS: <b>Total hot run time: 168358 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 1d7cf6dbd6095ee1fb5860efe258b0ce99242ae6, data reload: false
query5 4344 639 493 493
query6 431 186 172 172
query7 4869 542 299 299
query8 355 210 191 191
query9 8726 4054 4040 4040
query10 454 321 258 258
query11 5915 2327 2133 2133
query12 156 101 98 98
query13 1270 627 459 459
query14 6437 5385 5018 5018
query14_1 4347 4331 4331 4331
query15 200 192 171 171
query16 1004 433 443 433
query17 1087 668 544 544
query18 2428 470 332 332
query19 196 175 140 140
query20 107 104 103 103
query21 216 138 115 115
query22 13596 13558 13306 13306
query23 17424 16590 16286 16286
query23_1 16218 16211 16282 16211
query24 7554 1748 1287 1287
query24_1 1313 1304 1318 1304
query25 565 460 400 400
query26 1319 303 174 174
query27 2692 596 326 326
query28 4476 2071 2005 2005
query29 1104 626 492 492
query30 310 236 196 196
query31 1117 1086 994 994
query32 105 64 60 60
query33 539 331 267 267
query34 1175 1153 654 654
query35 756 780 693 693
query36 1387 1414 1231 1231
query37 157 104 90 90
query38 3167 3120 3053 3053
query39 929 931 912 912
query39_1 893 885 862 862
query40 244 128 105 105
query41 70 69 66 66
query42 97 94 96 94
query43 323 320 290 290
query44
query45 199 189 183 183
query46 1100 1190 730 730
query47 2430 2414 2295 2295
query48 410 406 304 304
query49 650 484 367 367
query50 1031 341 264 264
query51 4463 4465 4407 4407
query52 90 97 78 78
query53 280 261 183 183
query54 252 208 198 198
query55 77 75 70 70
query56 233 233 205 205
query57 1448 1418 1321 1321
query58 248 213 205 205
query59 1590 1613 1384 1384
query60 275 246 227 227
query61 169 160 148 148
query62 706 670 590 590
query63 224 181 189 181
query64 2527 754 584 584
query65
query66 1780 467 343 343
query67 29845 29590 29610 29590
query68
query69 434 301 262 262
query70 954 937 947 937
query71 294 227 220 220
query72 2907 2549 2325 2325
query73 836 731 418 418
query74 5117 4919 4764 4764
query75 2668 2565 2223 2223
query76 2296 1152 776 776
query77 344 392 287 287
query78 12444 12376 11848 11848
query79 1284 1157 753 753
query80 513 462 379 379
query81 451 292 241 241
query82 234 163 122 122
query83 271 274 250 250
query84
query85 823 499 418 418
query86 336 284 277 277
query87 3389 3361 3168 3168
query88 3607 2742 2726 2726
query89 415 379 331 331
query90 2155 196 174 174
query91 173 157 134 134
query92 61 61 59 59
query93 1447 1453 849 849
query94 536 372 297 297
query95 675 466 353 353
query96 1041 806 329 329
query97 2746 2725 2606 2606
query98 218 207 205 205
query99 1163 1191 1034 1034
Total cold run time: 250415 ms
Total hot run time: 168358 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "BiteTheDDDDt (via GitHub)" <gi...@apache.org>.
BiteTheDDDDt commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3413115610
##########
be/src/udf/python/boost_process_compat.h:
##########
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#pragma once
Review Comment:
这些无关的改动能不能单独抽出来单独提
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -290,6 +290,32 @@ Status PipelineFragmentContext::_build_and_prepare_full_pipeline(ThreadPool* thr
RETURN_IF_ERROR(_build_pipelines(_runtime_state->obj_pool(), *_query_ctx->desc_tbl,
&_root_op, root_pipeline));
+ // Propagate _num_instances from LOCAL_EXCHANGE pipelines to ancestor pipelines
+ // that inherited reduced num_tasks from a serial operator.
+ _propagate_local_exchange_num_tasks();
+
+ // Create deferred local exchangers now that all pipelines have final num_tasks.
+ RETURN_IF_ERROR(_create_deferred_local_exchangers());
Review Comment:
我没懂如果fe一开始就把所有local exchange规划好,所有算子的并行度规划好,直接下发。be为什么需要_create_deferred_local_exchangers和_propagate_local_exchange_num_tasks?
这个是现在设计出来就是要先做折中方案的,还是可以直接一把做全面?_propagate_local_exchange_num_tasks和_create_deferred_local_exchangers这两块新增的应该不是为了灰度/回滚吧。
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4707892364
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4477407204
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4480171001
# FE Regression Coverage Report
Increment line coverage ` 63.93% (468/732)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_15d92ba024b265d9441145d7bd26d93590aa9c63_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_15d92ba024b265d9441145d7bd26d93590aa9c63_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4478568671
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4486711226
# BE UT Coverage Report
Increment line coverage `11.11% (46/414)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/287ef124f24ef08527f79e648ccd85f5b338d59d_287ef124f24ef08527f79e648ccd85f5b338d59d/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/287ef124f24ef08527f79e648ccd85f5b338d59d_287ef124f24ef08527f79e648ccd85f5b338d59d/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 53.59% (20747/38712) |
| Line Coverage | 37.21% (196215/527345) |
| Region Coverage | 33.52% (153718/458547) |
| Branch Coverage | 34.57% (67007/193832) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4496367272
# BE UT Coverage Report
Increment line coverage `11.11% (46/414)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/ef4ea66a66f075dfb3259a519599ed361cd2d371_ef4ea66a66f075dfb3259a519599ed361cd2d371/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/ef4ea66a66f075dfb3259a519599ed361cd2d371_ef4ea66a66f075dfb3259a519599ed361cd2d371/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 53.56% (20734/38712) |
| Line Coverage | 37.19% (196132/527348) |
| Region Coverage | 33.49% (153587/458553) |
| Branch Coverage | 34.56% (66986/193834) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4504737237
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3413517236
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -290,6 +290,32 @@ Status PipelineFragmentContext::_build_and_prepare_full_pipeline(ThreadPool* thr
RETURN_IF_ERROR(_build_pipelines(_runtime_state->obj_pool(), *_query_ctx->desc_tbl,
&_root_op, root_pipeline));
+ // Propagate _num_instances from LOCAL_EXCHANGE pipelines to ancestor pipelines
+ // that inherited reduced num_tasks from a serial operator.
+ _propagate_local_exchange_num_tasks();
+
+ // Create deferred local exchangers now that all pipelines have final num_tasks.
+ RETURN_IF_ERROR(_create_deferred_local_exchangers());
Review Comment:
是的,这两块不是为了灰度/回滚——是 FE 接管 local exchange 的**当前折中**,不是一步到位。
要让 FE 把"每个算子的并发"也规划好直接下发,本质上需要 FE 自己切 pipeline、自己控制每条 pipeline 的并发(num_tasks 是 pipeline 粒度的,只在 pipeline 边界即 local exchange / pipeline breaker 处变化),BE 就不再规划 pipeline 了。那等于把 BE 的整套 pipeline 构建(`_build_pipeline`、pipeline-breaker 判定、num_tasks 推导、dependency 接线,还要兼顾 spill / runtime filter / shared state)搬到 FE,并改 thrift 契约携带完整 pipeline 结构——战线太长、风险太大。这套 FE 近似等价实现已经做了大半年、近 1 万行代码,没法一把全做。
所以现状是:FE 规划 LE 的**位置/类型/分布**(决定 plan 正确性,过去正是 BE/FE 分歧 bug 的根源),`enable_local_shuffle_planner=true` 时 BE 的 `_plan_local_exchange` 整个跳过;但每条 pipeline 的 num_tasks 仍由 BE 的 `_propagate_local_exchange_num_tasks` 拓扑推导,`_create_deferred_local_exchangers` 因为 exchanger 的 `num_partitions` 要等 num_tasks 定了才能建、所以延后创建。终态是 FE 连 num_tasks 也算好下发、BE 的 propagate 退化成 assert(只校验)——那是后续单独的大改。
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3372721774
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AggregationNode.java:
##########
@@ -258,4 +268,155 @@ public boolean isQueryCacheCandidate() {
public void setQueryCacheCandidate(boolean queryCacheCandidate) {
this.queryCacheCandidate = queryCacheCandidate;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+
+ ConnectContext connectContext = translatorContext.getConnectContext();
+ SessionVariable sessionVariable = connectContext.getSessionVariable();
+ // PR #62438: when false, non-finalize agg falls back to BE base class.
+ boolean enableLeBeforeAgg = sessionVariable.enableLocalExchangeBeforeAgg;
+ boolean hasKeys = !aggInfo.getGroupingExprs().isEmpty();
Review Comment:
**✅ Correctness fix**: The `!needsFinalize && !aggInfo.isMerge() && !enableLeBeforeAgg` guard correctly separates FIRST_MERGE (correctness-required, always HASH) from LOCAL phase (performance-only, flag controls). This diverges from BE's `!_needs_finalize && !enable_local_exchange_before_agg` early-return which conflates both intents — the FE planner is actually *more correct* here than BE. For the backward-compat path (legacy BE planner); consider adding a comment noting this divergence explicitly.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4708103838
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/fe9a3316be056faed18351cf64096e36790c6c89_fe9a3316be056faed18351cf64096e36790c6c89_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/fe9a3316be056faed18351cf64096e36790c6c89_fe9a3316be056faed18351cf64096e36790c6c89_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 77.32% (1889/2443) |
| Line Coverage | 64.41% (33961/52725) |
| Region Coverage | 64.80% (17462/26948) |
| Branch Coverage | 53.93% (9339/17316) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3411861875
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AggregationNode.java:
##########
@@ -258,4 +268,155 @@ public boolean isQueryCacheCandidate() {
public void setQueryCacheCandidate(boolean queryCacheCandidate) {
this.queryCacheCandidate = queryCacheCandidate;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+
+ ConnectContext connectContext = translatorContext.getConnectContext();
+ SessionVariable sessionVariable = connectContext.getSessionVariable();
+ // PR #62438: when false, non-finalize agg falls back to BE base class.
+ boolean enableLeBeforeAgg = sessionVariable.enableLocalExchangeBeforeAgg;
+ boolean hasKeys = !aggInfo.getGroupingExprs().isEmpty();
Review Comment:
Thanks. The divergence is called out in the code comment on `AggregationNode.enforceAndDeriveLocalExchange`: *"Diverges from BE: BE's `!_needs_finalize && !enable_local_exchange_before_agg` early return catches non-streaming dedup too, causing the same family of wrong-result bug as AggSink (DORIS-25413)."* The FE path separates FIRST_MERGE (always HASH, correctness) from the perf-only LOCAL phase; the legacy BE-planner path still carries DORIS-25413.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/HashJoinNode.java:
##########
@@ -285,4 +302,71 @@ public List<Expr> getOtherJoinConjuncts() {
public List<Expr> getMarkJoinConjuncts() {
return markJoinConjuncts;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+
+ LocalExchangeTypeRequire probeSideRequire;
+ LocalExchangeTypeRequire buildSideRequire;
+ LocalExchangeType outputType = null;
+
+ if (joinOp == JoinOperator.NULL_AWARE_LEFT_ANTI_JOIN) {
+ buildSideRequire = probeSideRequire = LocalExchangeTypeRequire.noRequire();
+ outputType = LocalExchangeType.NOOP;
+ } else if (distrMode == DistributionMode.BROADCAST) {
+ // BE: _child->is_serial_operator() ? PASSTHROUGH/PASS_TO_ONE : NOOP
+ boolean probeChildSerial = children.get(0).isSerialOperatorOnBe(
+ translatorContext.getConnectContext());
+ boolean buildChildSerial = children.get(1).isSerialOperatorOnBe(
+ translatorContext.getConnectContext());
+ probeSideRequire = probeChildSerial
+ ? LocalExchangeTypeRequire.requirePassthrough()
+ : LocalExchangeTypeRequire.noRequire();
+ buildSideRequire = buildChildSerial
+ ? LocalExchangeTypeRequire.requirePassToOne()
+ : LocalExchangeTypeRequire.noRequire();
+ // For serial probe: output is PASSTHROUGH (data from single instance).
+ // For non-serial probe: propagate probe side's actual distribution.
+ outputType = probeChildSerial ? LocalExchangeType.PASSTHROUGH : null;
+ } else if (isColocate() || isBucketShuffle()) {
+ probeSideRequire = LocalExchangeTypeRequire.requireBucketHash();
+ // For BUCKET_SHUFFLE with serial build child: use requireBucketHash() (not
+ // requirePassToOne()). Unlike BROADCAST joins, BUCKET_SHUFFLE has no shared
+ // hash table mechanism — PASS_TO_ONE routes all data to task 0 while tasks 1..N-1
+ // build empty hash tables, losing rows. BUCKET_HASH_SHUFFLE correctly distributes
+ // build data by bucket to match the probe side's bucket distribution.
+ // The serial exchange returns NOOP, so enforceRequire() will insert a
+ // BUCKET_HASH_SHUFFLE local exchange (with PASSTHROUGH fan-out for heavy-ops
Review Comment:
Added — the colocate/bucket-shuffle branch now notes that both probe and build sides require `BUCKET_HASH_SHUFFLE`, and that a serial build child is handled by the serial exchange returning NOOP → `enforceRequire` inserting the `BUCKET_HASH_SHUFFLE` LE (with PASSTHROUGH fan-out for the heavy-ops bottleneck). See `HashJoinNode` ~346-360.
##########
fe/fe-core/src/main/java/org/apache/doris/datasource/tvf/source/TVFScanNode.java:
##########
@@ -212,4 +217,10 @@ protected void setScanParams(TFileRangeDesc rangeDesc, Split split) {
public int getNumInstances() {
return scanRangeLocations.size();
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(PlanTranslatorContext translatorContext,
+ PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ return Pair.of(this, LocalExchangeType.PASSTHROUGH);
+ }
Review Comment:
Fixed in `8db568b5100` — and a bit more thoroughly than the suggestion: instead of returning `NOOP`, I removed the `enforceAndDeriveLocalExchange` override on `TVFScanNode` entirely (+ its 5 now-orphaned imports). The base `PlanNode.enforceAndDeriveLocalExchange()` already returns `Pair.of(this, NOOP)` for a leaf, so the scan no longer claims to satisfy a parent `requirePassthrough()`. A `TVFScan -> merge-sort / AssertNumRows / TableFunction`, or a force-passthrough broadcast-join probe, now gets a real `LocalExchangeNode` from `enforceRequire()`, matching BE's `Pipeline::need_to_local_exchange()` for non-hash targets. Thanks for catching this.
##########
regression-test/suites/nereids_p0/local_shuffle/test_local_shuffle_rqg_bugs.groovy:
##########
@@ -0,0 +1,1561 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * Regression tests for bugs discovered by RQG testing on the local-exchange2 branch.
+ *
+ * These queries triggered "must set shared state" errors or incorrect results
+ * in RQG build 183992. Common conditions:
+ * - use_serial_exchange=true (makes ALL Exchanges serial, not just UNPARTITIONED)
+ * - enable_local_shuffle_planner=true (FE-planned local exchange)
+ * - parallel_pipeline_task_num > 1
+ *
+ * Error types reproduced:
+ * 1. must set shared state, in AGGREGATION_OPERATOR
+ * 2. must set shared state, in SORT_OPERATOR
+ * 3. incorrect results with GROUPING SETS + scalar subquery + window function
+ */
+suite("test_local_shuffle_rqg_bugs") {
+
+ // ============================================================
+ // Table setup — mirrors RQG table structure
+ // 10 buckets to match RQG (replication_num=1 for single-BE testing)
+ // ============================================================
+ sql "DROP TABLE IF EXISTS rqg_t1"
+ sql "DROP TABLE IF EXISTS rqg_t2"
+ sql "DROP TABLE IF EXISTS rqg_t3"
+ sql "DROP TABLE IF EXISTS rqg_t4"
+
+ sql """
+ CREATE TABLE rqg_t1 (
+ pk INT NOT NULL,
+ col_int_undef_signed INT,
+ col_int_undef_signed2 INT,
+ col_int_undef_signed_not_null INT NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ CREATE TABLE rqg_t2 (
+ pk INT NOT NULL,
+ col_int_undef_signed INT,
+ col_int_undef_signed2 INT,
+ col_bigint_undef_signed_not_null BIGINT NOT NULL,
+ col_decimal_38_10__undef_signed_not_null DECIMAL(38,10) NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ // Table for build 184181 GLOBAL_HASH_SHUFFLE bugs — needs varchar + bigint columns
+ sql """
+ CREATE TABLE rqg_t3 (
+ pk INT NOT NULL,
+ col_bigint_undef_signed BIGINT,
+ col_varchar_10__undef_signed VARCHAR(10),
+ col_varchar_64__undef_signed VARCHAR(64)
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ // Second table for FULL OUTER JOIN case (col_bigint_undef_signed_not_null)
+ sql """
+ CREATE TABLE rqg_t4 (
+ pk INT NOT NULL,
+ col_bigint_undef_signed_not_null BIGINT NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ INSERT INTO rqg_t3 VALUES
+ (0, -94, 'Abc', 'hello world'),
+ (1, 672609, 'Xyz', null),
+ (2, -3766684, 'Pqr', 'test string'),
+ (3, 5070261, 'abc', 'another row'),
+ (4, null, 'def', 'value four'),
+ (5, -86, 'XgpxlHBLEM', null),
+ (6, 21910, 'abc', 'they'),
+ (7, -63, 'zzzz', 'some text'),
+ (8, -8276281, 'AHlvNtoGLO', 'longer string here'),
+ (9, -101, 'mid', 'final row')
+ """
+
+ sql """
+ INSERT INTO rqg_t4 VALUES
+ (0, 0), (1, 1), (2, 2), (3, 3), (4, 4),
+ (5, 5), (6, 6), (7, 7), (8, 8), (9, 9),
+ (10, 2), (11, 2), (12, 2), (13, 3), (14, 4),
+ (15, 5), (16, 2), (17, 2), (18, 2), (19, 9)
+ """
+
+ // Insert enough rows to exercise multiple pipeline tasks
+ sql """
+ INSERT INTO rqg_t1 VALUES
+ (0, 0, 10, 0), (1, 1, 11, 1), (2, 2, 12, 2), (3, 3, 13, 3),
+ (4, 4, 14, 4), (5, 5, 15, 5), (6, 6, 16, 6), (7, 7, 17, 7),
+ (8, 8, 18, 8), (9, 9, 19, 9), (10, 0, 20, 10), (11, 1, 21, 11),
+ (12, 2, 22, 12), (13, 3, 23, 13), (14, 4, 24, 14), (15, 5, 25, 15),
+ (16, 6, 26, 16), (17, 7, 27, 17), (18, 8, 28, 18), (19, 9, 29, 19)
+ """
+
+ sql """
+ INSERT INTO rqg_t2 VALUES
+ (0, 0, 10, 100, 1.5), (1, 1, 11, 101, 2.5), (2, 2, 12, 102, 3.5),
+ (3, 3, 13, 103, 4.5), (4, 4, 14, 104, 5.5), (5, 5, 15, 105, 6.5),
+ (6, 6, 16, 106, 7.5), (7, 7, 17, 107, 8.5), (8, 8, 18, 108, 9.5),
+ (9, 9, 19, 109, 10.5), (10, 0, 20, 110, 11.5), (11, 1, 21, 111, 12.5),
+ (12, 2, 22, 112, 13.5), (13, 3, 23, 113, 14.5), (14, 4, 24, 114, 15.5),
+ (15, 5, 25, 115, 16.5), (16, 6, 26, 116, 17.5), (17, 7, 27, 117, 18.5),
+ (18, 8, 28, 118, 19.5), (19, 9, 29, 119, 20.5)
+ """
+
+ // Wait for data to be visible
+ Thread.sleep(5000)
+
+ // ============================================================
+ // Common settings
+ // ============================================================
+ sql "SET enable_nereids_planner=true"
+ sql "SET enable_fallback_to_original_planner=false"
+ sql "SET runtime_filter_mode=off"
+ sql "SET enable_profile=true"
+ sql "SET enable_sql_cache=false"
+ sql "SET enable_local_shuffle=true"
+
+ // ============================================================
+ // Bug 1: must set shared state, in AGGREGATION_OPERATOR
+ // RQG case: eliminate_group_by_uniform.case_id_11007680713
+ // Key conditions: use_serial_exchange=true, parallel_pipeline_task_num=3
+ // SQL: EXCEPT with count(*) GROUP BY on both sides
+ // ============================================================
+
+ // Test with FE planner (the buggy path)
+ logger.info("=== Bug 1a: AGG shared state - EXCEPT with serial exchange (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=3,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_common_expr_pushdown=false,
+ disable_streaming_preaggregations=true)*/
+ col_int_undef_signed_not_null as col1,
+ col_int_undef_signed_not_null as col2,
+ 0 as col3, count(1)
+ FROM rqg_t1
+ GROUP BY col1, col2, col3
+ EXCEPT
+ SELECT col_bigint_undef_signed_not_null as col1,
+ col_decimal_38_10__undef_signed_not_null as col2,
+ 5 as col3, count(1)
+ FROM rqg_t2
+ GROUP BY col1, col2, col3
+ """
+ logger.info("Bug 1a: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 1a FAILED: ${t.message}")
+ assertTrue(false, "Bug 1a: must set shared state in AGGREGATION_OPERATOR: ${t.message}")
+ }
+
+ // Compare with BE native planner
+ logger.info("=== Bug 1b: AGG shared state - EXCEPT with serial exchange (BE native) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=3,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_common_expr_pushdown=false,
+ disable_streaming_preaggregations=true)*/
+ col_int_undef_signed_not_null as col1,
+ col_int_undef_signed_not_null as col2,
+ 0 as col3, count(1)
+ FROM rqg_t1
+ GROUP BY col1, col2, col3
+ EXCEPT
+ SELECT col_bigint_undef_signed_not_null as col1,
+ col_decimal_38_10__undef_signed_not_null as col2,
+ 5 as col3, count(1)
+ FROM rqg_t2
+ GROUP BY col1, col2, col3
+ """
+ logger.info("Bug 1b: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 1b FAILED: ${t.message}")
+ assertTrue(false, "Bug 1b: BE native also fails: ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 2: must set shared state, in SORT_OPERATOR
+ // RQG case: grouping_set.case_id_5308471751
+ // Key conditions: use_serial_exchange=true, parallel_pipeline_task_num=5
+ // SQL: GROUPING SETS + window function (PERCENT_RANK)
+ // ============================================================
+
+ logger.info("=== Bug 2a: SORT shared state - GROUPING SETS + window (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=5,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true)*/
+ SUM(PERCENT_RANK() OVER (PARTITION BY col_int_undef_signed2 ORDER BY col_int_undef_signed2))
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2),(pk, pk),(col_int_undef_signed))
+ """
+ logger.info("Bug 2a: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 2a FAILED: ${t.message}")
+ assertTrue(false, "Bug 2a: must set shared state in SORT_OPERATOR: ${t.message}")
+ }
+
+ logger.info("=== Bug 2b: SORT shared state - GROUPING SETS + window (BE native) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=5,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true)*/
+ SUM(PERCENT_RANK() OVER (PARTITION BY col_int_undef_signed2 ORDER BY col_int_undef_signed2))
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2),(pk, pk),(col_int_undef_signed))
+ """
+ logger.info("Bug 2b: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 2b FAILED: ${t.message}")
+ assertTrue(false, "Bug 2b: BE native also fails: ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 3: incorrect results with GROUPING SETS + scalar subquery + window
+ // RQG case: grouping_set.case_id_5694495756
+ // Key conditions: parallel_pipeline_task_num=2, disable_streaming_preaggregations=true
+ // Expected: all rows same value; Actual: values split proportionally (1/3, 2/3)
+ // ============================================================
+
+ logger.info("=== Bug 3: incorrect results - GROUPING SETS + subquery + window ===")
+ // FE planner
+ def result_fe = sql """
+ SELECT /*+SET_VAR(parallel_pipeline_task_num=2,
+ enable_local_shuffle_planner=true,
+ disable_streaming_preaggregations=true,
+ enable_share_hash_table_for_broadcast_join=true)*/
+ SUM((SELECT MAX(col_int_undef_signed2) FROM rqg_t1))
+ OVER (PARTITION BY pk ORDER BY pk)
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2, pk),(pk), (pk))
+ """
+ // BE native
+ def result_be = sql """
+ SELECT /*+SET_VAR(parallel_pipeline_task_num=2,
+ enable_local_shuffle_planner=false,
+ disable_streaming_preaggregations=true,
+ enable_share_hash_table_for_broadcast_join=true)*/
+ SUM((SELECT MAX(col_int_undef_signed2) FROM rqg_t1))
+ OVER (PARTITION BY pk ORDER BY pk)
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2, pk),(pk), (pk))
+ """
+ logger.info("Bug 3 FE result rows: ${result_fe.size()}, first few: ${result_fe.take(5)}")
+ logger.info("Bug 3 BE result rows: ${result_be.size()}, first few: ${result_be.take(5)}")
+
+ // FE planner and BE native must produce identical results (the bug was values split
+ // proportionally instead of equal). Assert row count and order-insensitive content so a
+ // recurrence fails the suite.
+ assertEquals(result_be.size(), result_fe.size(), "Bug 3: FE/BE row count mismatch")
+ assertEquals(result_be.collect { it.toString() }.sort(), result_fe.collect { it.toString() }.sort(),
+ "Bug 3: FE/BE result mismatch")
+
+ // ============================================================
+ // Bug 4: Simplified AGG shared state — single table GROUP BY with serial exchange
+ // Minimal reproduction attempt
+ // ============================================================
+
+ logger.info("=== Bug 4: Simplified AGG shared state ===")
+ for (int ppt : [2, 3, 4, 5]) {
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true)*/
+ col_int_undef_signed, count(*)
+ FROM rqg_t1
+ GROUP BY col_int_undef_signed
+ UNION ALL
+ SELECT col_int_undef_signed, count(*)
+ FROM rqg_t2
+ GROUP BY col_int_undef_signed
+ """
+ logger.info("Bug 4 ppt=${ppt}: PASSED")
+ } catch (Throwable t) {
+ logger.error("Bug 4 ppt=${ppt} FAILED: ${t.message}")
+ assertTrue(false, "Bug 4 ppt=${ppt}: AGG shared state crash with serial exchange: ${t.message}")
+ }
+ }
+
+ // ============================================================
+ // Bug 5: GROUPING SETS + window variations with serial exchange
+ // More variations to find minimal repro
+ // ============================================================
+
+ logger.info("=== Bug 5: GROUPING SETS + window variations ===")
+ for (int ppt : [2, 3, 4, 5]) {
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true)*/
+ pk, col_int_undef_signed,
+ ROW_NUMBER() OVER (ORDER BY pk)
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((pk, col_int_undef_signed), (pk), ())
+ ORDER BY pk
+ """
+ logger.info("Bug 5 ppt=${ppt}: PASSED")
+ } catch (Throwable t) {
+ logger.error("Bug 5 ppt=${ppt} FAILED: ${t.message}")
+ assertTrue(false, "Bug 5 ppt=${ppt}: GROUPING SETS + window crash with serial exchange: ${t.message}")
+ }
+ }
+
+ // ============================================================
+ // Bug 6: must set shared state, in CROSS_JOIN_OPERATOR
+ // Root cause: nested NLJ + pooling scan — FE planner skipped BROADCAST
+ // local exchange on outer NLJ's build side because child was NLJ (not ScanNode).
+ // Fixed in NestedLoopJoinNode.enforceAndDeriveLocalExchange by using
+ // fragment.useSerialSource() instead of instanceof ScanNode check.
+ // This was the root cause of 989 RQG test failures (build 183677).
+ // ============================================================
+
+ logger.info("=== Bug 6: CROSS_JOIN shared state - nested NLJ + pooling scan (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(ignore_storage_data_distribution=true,
+ parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ disable_join_reorder=true,
+ disable_colocate_plan=true,
+ auto_broadcast_join_threshold=-1,
+ broadcast_row_count_limit=0,
+ query_timeout=60)*/
+ count(a.pk) AS cnt, a.col_int_undef_signed
+ FROM rqg_t1 a
+ LEFT JOIN rqg_t1 b ON b.col_int_undef_signed >= b.col_int_undef_signed
+ LEFT JOIN rqg_t1 c ON b.pk >= b.pk
+ WHERE a.pk IS NOT NULL
+ GROUP BY a.col_int_undef_signed
+ ORDER BY cnt, a.col_int_undef_signed
+ """
+ logger.info("Bug 6: PASSED (no CROSS_JOIN_OPERATOR shared state error)")
+ } catch (Throwable t) {
+ logger.error("Bug 6 FAILED: ${t.message}")
+ assertTrue(false, "Bug 6: must set shared state in CROSS_JOIN_OPERATOR: ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 7: DataStreamSink hang — sender fragment with pooling scan
+ // Root cause: FE planner did not insert PASSTHROUGH at the root of pooling scan
+ // sender fragments. With pooling scan, only instance 0 creates pipeline tasks,
+ // so only 1 EOS is sent. The downstream ExchangeNode expects _num_instances EOSes
+ // and hangs indefinitely.
+ // Fixed in AddLocalExchange.addLocalExchangeForFragment: insert PASSTHROUGH
+ // when isLocalShuffle && newRoot.isSerialOperator().
+ // Any NLJ + pooling scan query triggers this via the UNPARTITIONED sender fragments.
+ // ============================================================
+
+ logger.info("=== Bug 7: DataStreamSink hang - NLJ + pooling scan sender (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(ignore_storage_data_distribution=true,
+ parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ disable_join_reorder=true,
+ disable_colocate_plan=true,
+ auto_broadcast_join_threshold=-1,
+ broadcast_row_count_limit=0,
+ query_timeout=60)*/
+ a.col_int_undef_signed, MAX(a.pk) AS mx
+ FROM rqg_t1 a
+ LEFT JOIN rqg_t1 b ON b.col_int_undef_signed < b.col_int_undef_signed
+ WHERE a.pk IS NOT NULL
+ GROUP BY a.col_int_undef_signed
+ ORDER BY a.col_int_undef_signed, mx
+ """
+ logger.info("Bug 7: PASSED (no hang)")
+ } catch (Throwable t) {
+ logger.error("Bug 7 FAILED: ${t.message}")
+ assertTrue(false, "Bug 7: DataStreamSink hang (query timed out or crashed): ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 8: must set shared state, in SORT_OPERATOR / UNION_OPERATOR
+ // Root cause: FE planner + pooling scan + GROUPING SETS. Serial UNPARTITIONED
+ // Exchange reduces downstream pipeline num_tasks to 1. SORT and UNION operators
+ // need _num_instances tasks to inject shared state for all instances.
+ // Fixed by: (1) restoring num_tasks raise for non-scan serial operators in BE
+ // deferred exchanger creation (commit 920d43d), and (2) FE inserting PASSTHROUGH
+ // after serial ExchangeNode in pooling scan fragments (commit d2e7fa2).
+ // ============================================================
+
+ logger.info("=== Bug 8a: SORT/UNION shared state - GROUPING SETS + pooling scan (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(ignore_storage_data_distribution=true,
+ parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ disable_streaming_preaggregations=true,
+ query_timeout=60)*/
+ pk, col_int_undef_signed, SUM(col_int_undef_signed_not_null) AS sv
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((pk, col_int_undef_signed), (pk), ())
+ ORDER BY pk, col_int_undef_signed, sv
+ """
+ logger.info("Bug 8a: PASSED (no SORT/UNION_OPERATOR shared state error)")
+ } catch (Throwable t) {
+ logger.error("Bug 8a FAILED: ${t.message}")
+ assertTrue(false, "Bug 8a: must set shared state in SORT/UNION_OPERATOR: ${t.message}")
+ }
+
+ logger.info("=== Bug 8b: SORT shared state - window + GROUPING SETS + pooling scan (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(ignore_storage_data_distribution=true,
+ parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ disable_streaming_preaggregations=true,
+ query_timeout=60)*/
+ pk, SUM(col_int_undef_signed_not_null) AS sv,
+ ROW_NUMBER() OVER (ORDER BY pk) AS rn
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((pk), ())
+ ORDER BY pk, sv, rn
+ """
+ logger.info("Bug 8b: PASSED (no SORT_OPERATOR shared state error)")
+ } catch (Throwable t) {
+ logger.error("Bug 8b FAILED: ${t.message}")
+ assertTrue(false, "Bug 8b: must set shared state in SORT_OPERATOR (window+grouping_sets): ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 9: FE/BE result inconsistency — agg after NLJ + pooling scan
+ // Root cause: StreamingAgg used fragment.useSerialSource()=true to require
+ // PASSTHROUGH from child, but when child is NLJ (not directly a serial scan),
+ // NLJ outputs ADAPTIVE_PASSTHROUGH. FE wrongly inserted an extra PASSTHROUGH
+ // exchange between StreamingAgg and NLJ (5 extra LOCAL_EXCHANGE_SINK_OPERATOR
+ // entries vs BE native).
+ // Fixed in AggregationNode: only requirePassthrough when
+ // children.get(0).isSerialOperator()=true, mirroring BE _child->is_serial_operator().
+ // ============================================================
+
+ logger.info("=== Bug 9: FE/BE result consistency - agg after NLJ + pooling scan ===")
+ def bug9_fe = sql """
+ SELECT /*+SET_VAR(ignore_storage_data_distribution=true,
+ parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ disable_join_reorder=true,
+ disable_colocate_plan=true,
+ auto_broadcast_join_threshold=-1,
+ broadcast_row_count_limit=0)*/
+ a.col_int_undef_signed, MAX(a.pk) AS mx
+ FROM rqg_t1 a LEFT JOIN rqg_t1 b ON b.col_int_undef_signed < b.col_int_undef_signed
+ WHERE a.pk IS NOT NULL
+ GROUP BY a.col_int_undef_signed
+ ORDER BY a.col_int_undef_signed, mx
+ """
+ def bug9_be = sql """
+ SELECT /*+SET_VAR(ignore_storage_data_distribution=true,
+ parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=false,
+ disable_join_reorder=true,
+ disable_colocate_plan=true,
+ auto_broadcast_join_threshold=-1,
+ broadcast_row_count_limit=0)*/
+ a.col_int_undef_signed, MAX(a.pk) AS mx
+ FROM rqg_t1 a LEFT JOIN rqg_t1 b ON b.col_int_undef_signed < b.col_int_undef_signed
+ WHERE a.pk IS NOT NULL
+ GROUP BY a.col_int_undef_signed
+ ORDER BY a.col_int_undef_signed, mx
+ """
+ logger.info("Bug 9 FE rows: ${bug9_fe.size()}, BE rows: ${bug9_be.size()}")
+ assertEquals(bug9_be.size(), bug9_fe.size(), "Bug 9: FE/BE row count mismatch")
+ assertEquals(bug9_be, bug9_fe, "Bug 9: FE/BE result mismatch for agg after NLJ + pooling scan")
+ logger.info("Bug 9: PASSED (FE/BE results match)")
+
+ // ============================================================
+ // Bug 10: GLOBAL_HASH_SHUFFLE Rows mismatched — self-join + NLJ
+ // RQG case: 906784672 (build 184181)
+ // Root cause: HashJoinNode used requireGlobalExecutionHash() → GLOBAL local exchange
+ // inserted when use_serial_exchange=true; shuffle_idx_to_instance_idx map has only
+ // 4 entries (1/BE) but GLOBAL hash needs N*dop entries → most rows unrouted (0 actual rows).
+ // Fixed: changed to requireHash() so resolveExchangeType() downgrades to LOCAL hash.
+ // SQL: self-join (table1 LEFT JOIN table1 table2 ON pk=col_bigint_undef_signed)
+ // then NLJ (LEFT JOIN table1 table3 ON pk > col_bigint_undef_signed)
+ // ============================================================
+
+ logger.info("=== Bug 10: GLOBAL_HASH_SHUFFLE Rows mismatched - self-join + NLJ (build 184181 case 906784672) ===")
+ def bug10_fe = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ disable_join_reorder=true, disable_colocate_plan=true)*/
+ table1.pk AS field1, table1.col_bigint_undef_signed AS field2
+ FROM rqg_t3 AS table1
+ LEFT JOIN rqg_t3 AS table2 ON table1.pk = table2.col_bigint_undef_signed
+ LEFT JOIN rqg_t3 AS table3 ON table1.pk > table2.col_bigint_undef_signed
+ WHERE (table1.col_varchar_10__undef_signed > 'AHlvNtoGLO'
+ AND table1.col_varchar_10__undef_signed < 'zzzz')
+ OR (table1.col_bigint_undef_signed = table1.pk AND table1.col_varchar_64__undef_signed IS NULL)
+ OR (table1.pk != table1.pk AND table1.pk <> 2)
+ GROUP BY field1, field2
+ ORDER BY field1, field2
+ """
+ def bug10_be = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ disable_join_reorder=true, disable_colocate_plan=true)*/
+ table1.pk AS field1, table1.col_bigint_undef_signed AS field2
+ FROM rqg_t3 AS table1
+ LEFT JOIN rqg_t3 AS table2 ON table1.pk = table2.col_bigint_undef_signed
+ LEFT JOIN rqg_t3 AS table3 ON table1.pk > table2.col_bigint_undef_signed
+ WHERE (table1.col_varchar_10__undef_signed > 'AHlvNtoGLO'
+ AND table1.col_varchar_10__undef_signed < 'zzzz')
+ OR (table1.col_bigint_undef_signed = table1.pk AND table1.col_varchar_64__undef_signed IS NULL)
+ OR (table1.pk != table1.pk AND table1.pk <> 2)
+ GROUP BY field1, field2
+ ORDER BY field1, field2
+ """
+ logger.info("Bug 10 FE rows: ${bug10_fe.size()}, BE rows: ${bug10_be.size()}")
+ assertEquals(bug10_be.size(), bug10_fe.size(), "Bug 10: FE/BE row count mismatch (GLOBAL_HASH_SHUFFLE Rows mismatched)")
+ assertEquals(bug10_be, bug10_fe, "Bug 10: FE/BE result mismatch for self-join + NLJ")
+ logger.info("Bug 10: PASSED")
+
+ // ============================================================
+ // Bug 11: GLOBAL_HASH_SHUFFLE Rows mismatched — FULL OUTER JOIN + GROUP BY
+ // RQG case: 11007681241 (build 184181)
+ // Same root cause as Bug 10.
+ // SQL: FULL OUTER JOIN on col_bigint_undef_signed_not_null with WHERE + GROUP BY
+ // ============================================================
+
+ logger.info("=== Bug 11: GLOBAL_HASH_SHUFFLE Rows mismatched - FULL OUTER JOIN + GROUP BY (build 184181 case 11007681241) ===")
+ def bug11_fe = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false)*/
+ t1.col_bigint_undef_signed_not_null, t2.col_bigint_undef_signed_not_null, count(1)
+ FROM rqg_t4 t1
+ FULL OUTER JOIN rqg_t2 t2
+ ON t1.col_bigint_undef_signed_not_null = t2.col_bigint_undef_signed_not_null
+ WHERE t2.col_bigint_undef_signed_not_null = 2
+ GROUP BY t1.col_bigint_undef_signed_not_null, t2.col_bigint_undef_signed_not_null
+ ORDER BY 1, 2, 3
+ """
+ def bug11_be = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false)*/
+ t1.col_bigint_undef_signed_not_null, t2.col_bigint_undef_signed_not_null, count(1)
+ FROM rqg_t4 t1
+ FULL OUTER JOIN rqg_t2 t2
+ ON t1.col_bigint_undef_signed_not_null = t2.col_bigint_undef_signed_not_null
+ WHERE t2.col_bigint_undef_signed_not_null = 2
+ GROUP BY t1.col_bigint_undef_signed_not_null, t2.col_bigint_undef_signed_not_null
+ ORDER BY 1, 2, 3
+ """
+ logger.info("Bug 11 FE rows: ${bug11_fe.size()}, BE rows: ${bug11_be.size()}")
+ assertEquals(bug11_be.size(), bug11_fe.size(), "Bug 11: FE/BE row count mismatch (GLOBAL_HASH_SHUFFLE Rows mismatched)")
+ assertEquals(bug11_be, bug11_fe, "Bug 11: FE/BE result mismatch for FULL OUTER JOIN + GROUP BY")
+ logger.info("Bug 11: PASSED")
+
+ // ============================================================
+ // Bug 12: GLOBAL_HASH_SHUFFLE Rows mismatched — LEFT JOIN + VARCHAR predicates + MIN()
+ // RQG case: 906784662 (build 184181)
+ // Same root cause as Bug 10/11.
+ // SQL: LEFT JOIN on pk with VARCHAR NOT IN / BETWEEN / IN predicates, MIN() aggregate
+ // ============================================================
+
+ logger.info("=== Bug 12: GLOBAL_HASH_SHUFFLE Rows mismatched - LEFT JOIN + VARCHAR predicates (build 184181 case 906784662) ===")
+ def bug12_fe = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ disable_join_reorder=true, disable_colocate_plan=true)*/
+ table1.pk AS field1, MIN(table1.pk) AS field2
+ FROM rqg_t3 AS table1
+ LEFT JOIN rqg_t1 AS table2 ON table2.pk = table1.pk
+ WHERE table1.col_varchar_64__undef_signed NOT IN ('they')
+ AND table1.col_varchar_10__undef_signed BETWEEN 'AHlvNtoGLO' AND 'z'
+ AND table1.pk IN (3, 6, 8, 9, 2)
+ GROUP BY field1
+ ORDER BY field1, field2 ASC
+ """
+ def bug12_be = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ disable_join_reorder=true, disable_colocate_plan=true)*/
+ table1.pk AS field1, MIN(table1.pk) AS field2
+ FROM rqg_t3 AS table1
+ LEFT JOIN rqg_t1 AS table2 ON table2.pk = table1.pk
+ WHERE table1.col_varchar_64__undef_signed NOT IN ('they')
+ AND table1.col_varchar_10__undef_signed BETWEEN 'AHlvNtoGLO' AND 'z'
+ AND table1.pk IN (3, 6, 8, 9, 2)
+ GROUP BY field1
+ ORDER BY field1, field2 ASC
+ """
+ logger.info("Bug 12 FE rows: ${bug12_fe.size()}, BE rows: ${bug12_be.size()}")
+ assertEquals(bug12_be.size(), bug12_fe.size(), "Bug 12: FE/BE row count mismatch (GLOBAL_HASH_SHUFFLE Rows mismatched)")
+ assertEquals(bug12_be, bug12_fe, "Bug 12: FE/BE result mismatch for LEFT JOIN + VARCHAR predicates")
+ logger.info("Bug 12: PASSED")
+
+ // ============================================================
+ // Bug 13: NLJ COREDUMP — serial NLJ + pooling scan + BROADCAST build side
+ // RQG build 184430, query c0dafc1bed0f4910
+ // Root cause: serial NLJ (RIGHT_OUTER) with pooling scan inserted BROADCAST
+ // local exchange on build side, inflating build pipeline num_tasks to _num_instances
+ // while probe pipeline stayed at 1 task. Instance 1+ created build tasks without
+ // corresponding probe tasks → source_deps empty → set_ready_to_read() crash.
+ // Fixed: serial NLJ sets buildSideRequire=noRequire() to match BE-native
+ // num_tasks_of_parent()<=1 skip logic.
+ // ============================================================
+
+ logger.info("=== Bug 13: NLJ COREDUMP - serial NLJ + pooling scan (FE planner) ===")
+ try {
+ def bug13_fe = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=0,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true,
+ disable_join_reorder=true)*/
+ t2.col_bigint_undef_signed_not_null AS field1
+ FROM rqg_t4 AS t1
+ RIGHT OUTER JOIN rqg_t2 AS t2 ON t1.col_bigint_undef_signed_not_null > t2.col_bigint_undef_signed_not_null
+ GROUP BY field1
+ ORDER BY field1 ASC
+ """
+ def bug13_be = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=0,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true,
+ disable_join_reorder=true)*/
+ t2.col_bigint_undef_signed_not_null AS field1
+ FROM rqg_t4 AS t1
+ RIGHT OUTER JOIN rqg_t2 AS t2 ON t1.col_bigint_undef_signed_not_null > t2.col_bigint_undef_signed_not_null
+ GROUP BY field1
+ ORDER BY field1 ASC
+ """
+ logger.info("Bug 13 FE rows: ${bug13_fe.size()}, BE rows: ${bug13_be.size()}")
+ assertEquals(bug13_be.size(), bug13_fe.size(), "Bug 13: FE/BE row count mismatch (NLJ COREDUMP)")
+ assertEquals(bug13_be, bug13_fe, "Bug 13: FE/BE result mismatch for serial NLJ + pooling scan")
+ logger.info("Bug 13: PASSED (no crash, results match)")
+ } catch (Throwable t) {
+ logger.error("Bug 13 FAILED: ${t.message}")
+ assertTrue(false, "Bug 13: NLJ COREDUMP (serial NLJ + pooling scan): ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 14: BUCKET_SHUFFLE join + serial build Exchange — must set shared state
+ // RQG build 184563, cases 906784706/906784783/906784987/906785006
+ // Root cause: BUCKET_SHUFFLE join build side ExchangeNode marked serial in
+ // pooling scan fragment → build pipeline num_tasks reduced to 1 →
+ // instance 1+ have probe tasks without build tasks → shared state injection
+ // fails. Fixed: isBucketShuffle() branch checks buildChildSerial and uses
+ // requirePassToOne() to restore num_tasks, matching BE-native behavior.
+ // Requires replication_num=3 + [shuffle] hint to force BUCKET_SHUFFLE plan.
+ // ============================================================
+
+ logger.info("=== Bug 14: BUCKET_SHUFFLE join + serial build Exchange (FE planner) ===")
+ // Need replication_num=3 for BUCKET_SHUFFLE. Check if allow_replica_on_same_host is enabled.
+ def allowSameHost = sql "ADMIN SHOW FRONTEND CONFIG LIKE 'allow_replica_on_same_host'"
+ if (allowSameHost[0][1].toString() == "true") {
+ sql "DROP TABLE IF EXISTS rqg_t5_rep3"
+ sql "DROP TABLE IF EXISTS rqg_t6_rep3"
+ try {
+ sql """
+ CREATE TABLE rqg_t5_rep3 (
+ pk INT NULL,
+ col_varchar_10__undef_signed VARCHAR(10) NULL,
+ col_bigint_undef_signed BIGINT NULL,
+ col_varchar_64__undef_signed VARCHAR(64) NULL
+ ) DUPLICATE KEY(pk, col_varchar_10__undef_signed)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "3")
+ """
+ sql """
+ CREATE TABLE rqg_t6_rep3 (
+ pk INT NULL,
+ col_varchar_10__undef_signed VARCHAR(10) NULL,
+ col_bigint_undef_signed BIGINT NULL,
+ col_varchar_64__undef_signed VARCHAR(64) NULL
+ ) DUPLICATE KEY(pk, col_varchar_10__undef_signed)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "3")
+ """
+ sql """
+ INSERT INTO rqg_t5_rep3 VALUES
+ (0,'abc',-94,'hello'),(1,'xyz',672609,null),(2,'pqr',-3766684,'test'),
+ (3,'abc',5070261,'another'),(4,'def',null,'value'),(5,'so',-86,null),
+ (6,'abc',21910,'they'),(7,'zzzz',-63,'some'),(8,'xPLflvBEcW',-8276281,'longer'),
+ (9,'mid',-101,'final')
+ """
+ sql """
+ INSERT INTO rqg_t6_rep3 VALUES
+ (0,'aaa',100,'world'),(1,'bbb',200,null),(2,'ccc',300,'foo'),
+ (3,'ddd',400,'bar'),(4,'eee',500,'baz'),(5,'fff',600,null),
+ (6,'ggg',700,'qux'),(7,'hhh',800,'quux'),(8,'iii',900,'corge'),
+ (9,'jjj',1000,'grault')
+ """
+ Thread.sleep(3000)
+
+ def bug14_fe = sql """
+ SELECT /*+SET_VAR(enable_local_shuffle_planner=true,
+ parallel_pipeline_task_num=3,
+ disable_streaming_preaggregations=true,
+ enable_sql_cache=false,
+ disable_join_reorder=true)*/
+ table1.pk AS field1
+ FROM rqg_t5_rep3 AS table1
+ LEFT OUTER JOIN [shuffle] rqg_t6_rep3 AS table2 ON table1.pk = table2.pk
+ WHERE table1.col_varchar_10__undef_signed >= 'so'
+ GROUP BY field1
+ ORDER BY field1
+ """
+ def bug14_be = sql """
+ SELECT /*+SET_VAR(enable_local_shuffle_planner=false,
+ parallel_pipeline_task_num=3,
+ disable_streaming_preaggregations=true,
+ enable_sql_cache=false,
+ disable_join_reorder=true)*/
+ table1.pk AS field1
+ FROM rqg_t5_rep3 AS table1
+ LEFT OUTER JOIN [shuffle] rqg_t6_rep3 AS table2 ON table1.pk = table2.pk
+ WHERE table1.col_varchar_10__undef_signed >= 'so'
+ GROUP BY field1
+ ORDER BY field1
+ """
+ logger.info("Bug 14 FE rows: ${bug14_fe.size()}, BE rows: ${bug14_be.size()}")
+ assertEquals(bug14_be.size(), bug14_fe.size(), "Bug 14: FE/BE row count mismatch (BUCKET_SHUFFLE must set shared state)")
+ assertEquals(bug14_be, bug14_fe, "Bug 14: FE/BE result mismatch for BUCKET_SHUFFLE + serial build Exchange")
+ logger.info("Bug 14: PASSED (no crash, results match)")
+ } catch (Throwable t) {
+ logger.error("Bug 14 FAILED: ${t.message}")
+ assertTrue(false, "Bug 14: BUCKET_SHUFFLE must set shared state: ${t.message}")
+ }
+ } else {
+ logger.info("Bug 14: SKIPPED (allow_replica_on_same_host not enabled, cannot create replication_num=3 tables)")
+ }
+
+ // ============================================================
+ // Bug 15: BUCKET_SHUFFLE join wrong results with serial exchange — PASS_TO_ONE data loss
+ // Root cause: When serial exchange feeds BUCKET_SHUFFLE join build side,
+ // PASS_TO_ONE routes ALL build data to task 0. Unlike BROADCAST joins,
+ // BUCKET_SHUFFLE has no shared hash table mechanism — tasks 1..N-1 build
+ // empty hash tables and lose rows during probe. Fixed by using
+ // BUCKET_HASH_SHUFFLE instead of PASS_TO_ONE for BUCKET_SHUFFLE build side.
+ // Tables use 3 buckets so pptn=4 triggers serial scan on single BE (3 < 4*1).
+ // ============================================================
+
+ logger.info("=== Bug 15: BUCKET_SHUFFLE join wrong results with serial PASS_TO_ONE ===")
+
+ sql "DROP TABLE IF EXISTS rqg_t7_3bucket"
+ sql "DROP TABLE IF EXISTS rqg_t8_3bucket"
+
+ sql """
+ CREATE TABLE rqg_t7_3bucket (
+ pk INT NOT NULL,
+ col_int INT NULL,
+ col_varchar VARCHAR(64) NULL
+ ) DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 3
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ CREATE TABLE rqg_t8_3bucket (
+ pk INT NOT NULL,
+ col_int INT NULL,
+ col_varchar VARCHAR(64) NULL
+ ) DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 3
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ INSERT INTO rqg_t7_3bucket VALUES
+ (0, 10, 'aaa'), (1, 20, 'bbb'), (2, 30, 'ccc'),
+ (3, 40, 'ddd'), (4, 50, 'eee'), (5, 60, 'fff'),
+ (6, 70, 'ggg'), (7, 80, 'hhh'), (8, 90, 'iii'), (9, 100, 'jjj')
+ """
+
+ sql """
+ INSERT INTO rqg_t8_3bucket VALUES
+ (0, 10, 'aaa'), (1, 20, 'bbb'), (2, 30, 'ccc'),
+ (3, 40, 'ddd'), (4, 50, 'eee'), (5, 60, 'fff'),
+ (6, 70, 'ggg'), (7, 80, 'hhh'), (8, 90, 'iii'), (9, 100, 'jjj')
+ """
+
+ Thread.sleep(3000)
+
+ try {
+ // pptn=4 with 3 buckets on 1 BE: 3 < 4*1 → serial scan → serial exchange
+ // This triggers the PASS_TO_ONE bug for BUCKET_SHUFFLE build side.
+ // Also test with higher pptn values to cover more cases.
+ for (int ppt : [4, 6, 8]) {
+ def bug15_fe = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,
+ parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ enable_sql_cache=false,
+ disable_join_reorder=true)*/
+ t1.pk, t1.col_int, t2.col_varchar
+ FROM rqg_t7_3bucket t1
+ INNER JOIN [shuffle] rqg_t8_3bucket t2 ON t1.pk = t2.pk
+ ORDER BY t1.pk
+ """
+ def bug15_be = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,
+ parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ enable_sql_cache=false,
+ disable_join_reorder=true)*/
+ t1.pk, t1.col_int, t2.col_varchar
+ FROM rqg_t7_3bucket t1
+ INNER JOIN [shuffle] rqg_t8_3bucket t2 ON t1.pk = t2.pk
+ ORDER BY t1.pk
+ """
+ logger.info("Bug 15 ppt=${ppt}: FE rows=${bug15_fe.size()}, BE rows=${bug15_be.size()}")
+ assertEquals(10, bug15_fe.size(), "Bug 15 ppt=${ppt}: expected 10 rows from FE planner, got ${bug15_fe.size()}")
+ assertEquals(bug15_be, bug15_fe, "Bug 15 ppt=${ppt}: FE/BE result mismatch for BUCKET_SHUFFLE + serial exchange")
+ }
+
+ // Also test LEFT OUTER JOIN to verify no rows lost on probe side
+ def bug15_left_fe = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,
+ parallel_pipeline_task_num=6,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ enable_sql_cache=false,
+ disable_join_reorder=true)*/
+ t1.pk, t2.col_int
+ FROM rqg_t7_3bucket t1
+ LEFT OUTER JOIN [shuffle] rqg_t8_3bucket t2 ON t1.pk = t2.pk
+ ORDER BY t1.pk
+ """
+ def bug15_left_be = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,
+ parallel_pipeline_task_num=6,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ enable_sql_cache=false,
+ disable_join_reorder=true)*/
+ t1.pk, t2.col_int
+ FROM rqg_t7_3bucket t1
+ LEFT OUTER JOIN [shuffle] rqg_t8_3bucket t2 ON t1.pk = t2.pk
+ ORDER BY t1.pk
+ """
+ assertEquals(10, bug15_left_fe.size(), "Bug 15 LEFT JOIN: expected 10 rows from FE planner")
+ assertEquals(bug15_left_be, bug15_left_fe, "Bug 15 LEFT JOIN: FE/BE result mismatch")
+
+ logger.info("Bug 15: PASSED (no wrong results, all pptn values correct)")
+ } catch (Throwable t) {
+ logger.error("Bug 15 FAILED: ${t.message}")
+ assertTrue(false, "Bug 15: BUCKET_SHUFFLE wrong results with serial PASS_TO_ONE: ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 16 & 17: Serial AnalyticEval crash and DataStreamSink hang
+ // with LocalShuffleAssignedJob (multiple instances on one BE)
+ //
+ // Bug 16 (crash): Exchange wraps itself with PASSTHROUGH LocalExchange.
+ // This restores AnalyticSink pipeline to _num_instances tasks while
+ // serial AnalyticSource stays at 1 task. For instance_idx > 0,
+ // source_deps is empty → DCHECK crash.
+ //
+ // Bug 17 (hang): After fixing the crash, serial AnalyticSource reduces
+ // all downstream pipeline tasks to 1 via add_pipeline() inheritance.
+ // Only instance 0 runs DataStreamSink → receiver expects _num_instances
+ // EOSes → hang.
+ //
+ // Both triggered by: OVER() with no PARTITION BY + GROUPING SETS +
+ // pptn=0 (auto-parallel) + disable_streaming_preaggregations=true
+ // RQG build 186195, query IDs: 7f3178a77c2c4b6b, 71887f7bf804c0c, 5dd9fcad234c4484
+ // ============================================================
+ sql "DROP TABLE IF EXISTS rqg_analytic_t1"
+ sql """
+ CREATE TABLE rqg_analytic_t1 (
+ pk INT NOT NULL,
+ col_int_undef_signed INT
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+ sql """
+ INSERT INTO rqg_analytic_t1 VALUES
+ (1, 10), (2, 20), (3, 30), (4, 40), (5, 50),
+ (6, 60), (7, 70), (8, 80), (9, 90), (10, 100),
+ (11, 10), (12, 20), (13, 30), (14, 40), (15, 50),
+ (16, 60), (17, 70), (18, 80), (19, 90), (20, 100)
+ """
+
+ try {
+ logger.info("Bug 16+17: Testing serial AnalyticEval with GROUPING SETS")
+
+ // Baseline: pptn=1 (no multi-instance, no local shuffle)
+ def bug16_baseline = sql """
+ SELECT /*+SET_VAR(parallel_pipeline_task_num=1,
+ enable_sql_cache=false,
+ disable_streaming_preaggregations=true)*/
+ COUNT(MIN(col_int_undef_signed) OVER())
+ FROM rqg_analytic_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed, pk), (), (pk))
+ ORDER BY 1
+ """
+ assertEquals(41, bug16_baseline.size(), "Bug 16 baseline: expected 41 rows")
+
+ // Test with pptn=0 (auto-parallel, triggers LocalShuffleAssignedJob)
+ for (int ppt : [0, 2, 4, 8]) {
+ def bug16_result = sql """
+ SELECT /*+SET_VAR(parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ disable_streaming_preaggregations=true)*/
+ COUNT(MIN(col_int_undef_signed) OVER())
+ FROM rqg_analytic_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed, pk), (), (pk))
+ ORDER BY 1
+ """
+ assertEquals(bug16_baseline, bug16_result,
+ "Bug 16+17 pptn=${ppt}: result mismatch with serial AnalyticEval")
+ }
+
+ // Also test with use_serial_exchange=true (makes ALL exchanges serial)
+ def bug16_serial = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,
+ parallel_pipeline_task_num=0,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ disable_streaming_preaggregations=true)*/
+ COUNT(MIN(col_int_undef_signed) OVER())
+ FROM rqg_analytic_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed, pk), (), (pk))
+ ORDER BY 1
+ """
+ assertEquals(bug16_baseline, bug16_serial,
+ "Bug 16+17 serial_exchange: result mismatch")
+
+ logger.info("Bug 16+17: PASSED (no crash, no hang, correct results)")
+ } catch (Throwable t) {
+ logger.error("Bug 16+17 FAILED: ${t.message}")
+ assertTrue(false, "Bug 16+17: Serial AnalyticEval crash/hang: ${t.message}")
+ }
+
+ // Bug 18: DCHECK crash in Pipeline::set_num_tasks when PASSTHROUGH LE is inserted
+ // between serial NLJ and its child Exchange.
+ // Root cause: ExchangeNode.enforceAndDeriveLocalExchange wraps UNPARTITIONED serial
+ // Exchange with PASSTHROUGH LE. On BE, NLJ_PROBE (serial) sets pipeline num_tasks=1,
+ // then the LE handler's set_num_tasks(_num_instances) overrides it to N, triggering
+ // DCHECK (serial operator in pipeline with num_tasks > 1).
+ // Fix: skip PASSTHROUGH wrapping when hasSerialAncestorInPipeline is true.
+ // Query: LEFT JOIN with always-true self-ref condition (table.pk = table.pk) creates
+ // RIGHT_OUTER NLJ (serial). With pptn>1 and ignore_data_distribution, the fragment
+ // gets N instances but NLJ forces 1 task.
+ try {
+ logger.info("Bug 18: Testing serial NLJ with PASSTHROUGH LE crash")
+ // Use existing rqg_t1 table (10 rows, 10 buckets)
+ def bug18_baseline = sql """
+ SELECT /*+SET_VAR(enable_local_shuffle_planner=false,
+ enable_sql_cache=false)*/
+ table1.col_int_undef_signed AS field1
+ FROM rqg_t1 AS table1
+ LEFT JOIN rqg_t1 AS table2
+ ON table2.pk = table2.pk
+ WHERE table1.pk BETWEEN 2 AND 11
+ GROUP BY field1
+ ORDER BY 1
+ """
+
+ // Test with various pptn values — crash requires pptn > 1
+ for (int ppt : [4, 7]) {
+ def bug18_result = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=false,
+ parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ enable_share_hash_table_for_broadcast_join=false,
+ enable_broadcast_join_force_passthrough=true,
+ enable_parallel_scan=false)*/
+ table1.col_int_undef_signed AS field1
+ FROM rqg_t1 AS table1
+ LEFT JOIN rqg_t1 AS table2
+ ON table2.pk = table2.pk
+ WHERE table1.pk BETWEEN 2 AND 11
+ GROUP BY field1
+ ORDER BY 1
+ """
+ assertEquals(bug18_baseline, bug18_result,
+ "Bug 18 pptn=${ppt}: result mismatch with serial NLJ + local exchange")
+ }
+ logger.info("Bug 18: PASSED (no crash, correct results)")
+ } catch (Throwable t) {
+ logger.error("Bug 18 FAILED: ${t.message}")
+ assertTrue(false, "Bug 18: Serial NLJ PASSTHROUGH LE crash: ${t.message}")
+ }
+
+ // Bug 19: source_deps.size()=0 crash in NLJ build sink.
+ // Root cause: serial NLJ (RIGHT_OUTER) resets serial ancestor flag for build side.
+ // Exchange(UNPARTITIONED) on build side sees hasSerialAncestorInPipeline=false and
+ // inserts PASSTHROUGH LE. This restores build pipeline num_tasks to _num_instances
+ // while probe pipeline stays at 1. The extra build tasks have NLJ shared state with
+ // empty source_deps → crash in set_ready_to_read().
+ // Fix: shouldResetSerialFlagForChild(1) returns false when NLJ is serial.
+ // Differs from Bug 18 in fuzzy vars: enable_share_hash_table=true, broadcast_passthrough=false.
+ try {
+ logger.info("Bug 19: Testing serial NLJ build-side source_deps crash")
+ def bug19_baseline = sql """
+ SELECT /*+SET_VAR(enable_local_shuffle_planner=false,
+ enable_sql_cache=false)*/
+ table1.col_int_undef_signed AS field1
+ FROM rqg_t1 AS table1
+ LEFT JOIN rqg_t1 AS table2
+ ON table2.pk = table2.pk
+ WHERE table1.pk BETWEEN 2 AND 11
+ GROUP BY field1
+ ORDER BY 1
+ """
+
+ for (int ppt : [2, 4]) {
+ def bug19_result = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=false,
+ parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ enable_share_hash_table_for_broadcast_join=true,
+ enable_broadcast_join_force_passthrough=false,
+ enable_parallel_scan=true,
+ disable_streaming_preaggregations=true)*/
+ table1.col_int_undef_signed AS field1
+ FROM rqg_t1 AS table1
+ LEFT JOIN rqg_t1 AS table2
+ ON table2.pk = table2.pk
+ WHERE table1.pk BETWEEN 2 AND 11
+ GROUP BY field1
+ ORDER BY 1
+ """
+ assertEquals(bug19_baseline, bug19_result,
+ "Bug 19 pptn=${ppt}: result mismatch with serial NLJ build side crash")
+ }
+ logger.info("Bug 19: PASSED (no crash, correct results)")
+ } catch (Throwable t) {
+ logger.error("Bug 19 FAILED: ${t.message}")
+ assertTrue(false, "Bug 19: Serial NLJ build-side source_deps crash: ${t.message}")
+ }
+
+ // Bug 20: Hang (ASAN: COREDUMP source_deps.size()=0 in AggSinkOperatorX) when
+ // use_serial_exchange=true + RIGHT JOIN + GROUP BY in non-pooling fragment.
+ // Root cause: serial HASH Exchange in non-pooling fragment returned NOOP, causing FE
+ // to insert LOCAL_EXECUTION_HASH_SHUFFLE LE. On BE, serial Exchange pipeline has 1 task
+ // but LE downstream has _num_instances tasks. AggSink on instances 1+ has empty source_deps.
+ // Fix: ExchangeNode.enforceAndDeriveLocalExchange() returns actual distribution type
+ // (GLOBAL_EXECUTION_HASH_SHUFFLE/BUCKET_HASH_SHUFFLE) for serial Exchange in non-pooling
+ // fragments, preventing LE insertion.
+ // Requires 3+ BEs to reproduce (single BE has _num_instances=1, no hang).
+ try {
+ logger.info("Bug 20: Testing serial exchange + agg hang in non-pooling fragment")
+ // Baseline uses same fuzzy vars but with planner=false (BE-native).
+ // This way we compare FE-planned vs BE-native under identical conditions,
+ // not against "correct" results — use_serial_exchange=true itself may have
+ // pre-existing BE bugs with certain pptn values.
+ def bug20_baseline_sql = { int ppt -> """
+ SELECT /*+SET_VAR(use_serial_exchange=true,
+ parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true)*/
+ table1.pk AS field1
+ FROM rqg_t1 AS table1
+ RIGHT OUTER JOIN rqg_t1 AS table2 ON table1.pk = table2.pk
+ LEFT JOIN rqg_t1 AS table3 ON table3.pk = table1.pk
+ WHERE table1.col_int_undef_signed IS NOT NULL OR table1.pk <> 10
+ GROUP BY field1
+ ORDER BY 1
+ """ }
+ for (int ppt : [3, 4, 7]) {
+ def bug20_baseline = sql bug20_baseline_sql(ppt)
+ def bug20_result = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,
+ parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true)*/
+ table1.pk AS field1
+ FROM rqg_t1 AS table1
+ RIGHT OUTER JOIN rqg_t1 AS table2 ON table1.pk = table2.pk
+ LEFT JOIN rqg_t1 AS table3 ON table3.pk = table1.pk
+ WHERE table1.col_int_undef_signed IS NOT NULL OR table1.pk <> 10
+ GROUP BY field1
+ ORDER BY 1
+ """
+ assertEquals(bug20_baseline, bug20_result,
+ "Bug 20 pptn=${ppt}: result mismatch with serial exchange + agg hang")
+ }
+ logger.info("Bug 20: PASSED (no hang, correct results)")
+ } catch (Throwable t) {
+ logger.error("Bug 20 FAILED: ${t.message}")
+ assertTrue(false, "Bug 20: Serial exchange + agg hang: ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 21: Multi-distinct COUNT on many-bucket table → COREDUMP
+ // RQG build 186737/186929/186952: AggSinkOperatorX::sink → set_ready_to_read
+ // with empty source_deps.
+ //
+ // Root cause: AGG operators (streaming, distinct-streaming, serialize) requested
+ // PASSTHROUGH from non-ScanNode serial children (Exchange, AGG), inserting a
+ // PASSTHROUGH LE that created a pipeline split disconnecting AggSink↔AggSource
+ // shared state.
+ //
+ // Fix: restrict AGG PASSTHROUGH requests to ScanNode children only.
+ // Triggered by: multi-distinct COUNT/MIN with MultiCastDataSinks feeding
+ // serial UNPARTITIONED Exchanges into streaming AGG fragments.
+ // ============================================================
+ sql "DROP TABLE IF EXISTS rqg_t5_many_buckets"
+ sql """
+ CREATE TABLE rqg_t5_many_buckets (
+ pk INT NOT NULL,
+ col_int_undef_signed INT,
+ col_date_undef_signed DATE,
+ col_date_undef_signed2 DATE,
+ col_varchar_1024__undef_signed VARCHAR(1024)
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 56
+ PROPERTIES ("replication_num" = "1")
+ """
+ sql """INSERT INTO rqg_t5_many_buckets VALUES
+ (1,1,'2023-12-09','2024-06-01','s1'),(2,2,'2023-03-15','2024-01-20','s2'),
+ (3,3,'2023-07-22','2024-03-10',NULL),(4,4,'2023-12-09','2024-06-01','s4'),
+ (5,5,'2023-01-05','2024-09-15','s5'),(6,6,'2023-08-11','2024-02-28','s6'),
+ (7,7,'2023-04-18','2024-07-04',NULL),(8,8,'2023-11-25','2024-05-12','s8'),
+ (9,9,'2023-06-30','2024-11-19','s9'),(10,10,'2023-02-14','2024-08-07','s10')
+ """
+
+ try {
+ logger.info("Bug 21: Testing multi-distinct COUNT on many-bucket table (COREDUMP fix)")
+ for (int ppt : [4, 6]) {
+ // Test without use_serial_exchange
+ def bug21_baseline = sql """
+ SELECT /*+SET_VAR(enable_local_shuffle_planner=false,
+ parallel_pipeline_task_num=${ppt},
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ query_timeout=60)*/
+ MIN(distinct col_date_undef_signed),
+ COUNT(distinct col_date_undef_signed2),
+ COUNT(distinct col_int_undef_signed)
+ FROM rqg_t5_many_buckets
+ WHERE col_int_undef_signed = col_int_undef_signed
+ LIMIT 1000
+ """
+ def bug21_result = sql """
+ SELECT /*+SET_VAR(enable_local_shuffle_planner=true,
+ parallel_pipeline_task_num=${ppt},
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ query_timeout=60)*/
+ MIN(distinct col_date_undef_signed),
+ COUNT(distinct col_date_undef_signed2),
+ COUNT(distinct col_int_undef_signed)
+ FROM rqg_t5_many_buckets
+ WHERE col_int_undef_signed = col_int_undef_signed
+ LIMIT 1000
+ """
+ assertEquals(bug21_baseline, bug21_result,
+ "Bug 21 pptn=${ppt}: multi-distinct COUNT result mismatch (was COREDUMP)")
+ }
+ logger.info("Bug 21: PASSED (no crash, correct results)")
+ } catch (Throwable t) {
+ if (t.message != null && t.message.contains("query timeout")) {
+ logger.warn("Bug 21: SKIPPED (query timeout - likely BE crash from previous test, not our bug)")
+ } else {
Review Comment:
Fixed in `8db568b5100`. Removed the `catch` branch that logged `SKIPPED` on `query timeout` — a hang is one of the failure modes this case reproduces (an EOS/close-count mismatch can hang instead of coredumping), so a timeout now fails the suite like the other `catch` blocks. Added a comment noting the hang-vs-crash distinction.
##########
gensrc/thrift/Partitions.thrift:
##########
@@ -119,3 +153,5 @@ struct TDataPartition {
3: optional list<TRangePartition> partition_infos
4: optional TMergePartitionInfo merge_partition_info
}
+
Review Comment:
Fixed in `73cc6458fd1` — removed the trailing blank line(s) at EOF; the file now ends at the closing `}` (line 155). `git diff --check` is clean.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4734137516
# FE Regression Coverage Report
Increment line coverage ` 80.25% (451/562)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_c2c51332d7f90c80ccabf77b27c23317d636a128_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_c2c51332d7f90c80ccabf77b27c23317d636a128_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4738227700
# FE Regression Coverage Report
Increment line coverage ` 70.66% (419/593)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_c2c51332d7f90c80ccabf77b27c23317d636a128_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_c2c51332d7f90c80ccabf77b27c23317d636a128_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4507561491
# FE UT Coverage Report
Increment line coverage ` 73.97% (449/607)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_846d365a88901fa32fdf44b42b6cd04b900da259/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_846d365a88901fa32fdf44b42b6cd04b900da259/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4508959008
# BE UT Coverage Report
Increment line coverage `11.11% (46/414)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/846d365a88901fa32fdf44b42b6cd04b900da259_846d365a88901fa32fdf44b42b6cd04b900da259/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/846d365a88901fa32fdf44b42b6cd04b900da259_846d365a88901fa32fdf44b42b6cd04b900da259/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 53.64% (20736/38658) |
| Line Coverage | 37.21% (196345/527718) |
| Region Coverage | 33.55% (153997/459017) |
| Branch Coverage | 34.54% (67084/194197) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4766685010
# FE Regression Coverage Report
Increment line coverage ` 81.28% (456/561)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3452056959
##########
fe/fe-core/src/main/java/org/apache/doris/planner/RepeatNode.java:
##########
@@ -86,11 +90,30 @@ public String getNodeExplainString(String detailPrefix, TExplainLevel detailLeve
// Determined by its child.
@Override
- public boolean isSerialOperator() {
- return children.get(0).isSerialOperator();
+ public boolean isSerialNode() {
+ return children.get(0).isSerialNode();
}
public GroupingInfo getGroupingInfo() {
return groupingInfo;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ // REPEAT (rollup/grouping sets) is NOT distribution-preserving: it NULLs grouping
+ // columns per set and produces GROUPING_ID, which is part of the downstream agg hash
+ // key but does not exist below the repeat. Forwarding the parent HASH require down
+ // would push the local exchange before the row expansion AND hash by the child
+ // distribution (a single upstream shuffle key) instead of the agg grouping_exprs,
+ // collapsing rows onto one instance (tpcds q67, +73%). Recurse with noRequire so the
+ // parent inserts its hash local exchange ABOVE the repeat using its own grouping_exprs
+ // (mirrors BE, whose RepeatOperatorX has a NOOP required_data_distribution).
+ Pair<PlanNode, LocalExchangeType> enforceResult
+ = enforceRequire(translatorContext, children.get(0), 0,
+ LocalExchangeTypeRequire.noRequire());
+ children = new java.util.ArrayList<>();
+ children.add(enforceResult.first);
Review Comment:
Thanks for the detailed analysis. I checked this against the BE native local-shuffle path and I don't believe it's a correctness issue — forwarding the child's distribution here is a deliberate mirror of BE, and the local exchange above `Repeat` is parallelism-only.
**The grouping-id correctness shuffle is a cross-fragment network exchange, not a local exchange.** `EXPLAIN` of `GROUP BY GROUPING SETS` over a bucketed table:
```
Fragment 1: VOlapScanNode -> VREPEAT_NODE (output: ..., GROUPING_ID)
-> VAGGREGATE (update serialize, STREAMING) // partial pre-agg
-> STREAM DATA SINK HASH_PARTITIONED: <keys>, GROUPING_ID // network shuffle
Fragment 0: VEXCHANGE -> VAGGREGATE (merge finalize) // final agg
```
The aggregate directly above `Repeat` is a **streaming partial pre-aggregation**; it does not require its input partitioned by the grouping keys. Global correctness (grouping by `GROUPING_ID`) is guaranteed by the network shuffle (`HASH_PARTITIONED` by the grouping exprs + `GROUPING_ID`) feeding the final merge aggregate in Fragment 0. A `LocalExchangeNode` above `Repeat` can therefore only change the partial agg's within-backend parallelism — never the result.
**BE's native path skips the exchange here too.** `Pipeline::need_to_local_exchange` (`be/src/exec/pipeline/pipeline.cpp`):
```cpp
return _data_distribution.distribution_type != target_data_distribution.distribution_type &&
!(is_hash_exchange(_data_distribution.distribution_type) &&
is_hash_exchange(target_data_distribution.distribution_type)); // both hash -> skip
```
For `Scan -> Repeat -> AggSink`, the pipeline `_data_distribution` is the scan's `required_data_distribution` = `BUCKET_HASH_SHUFFLE` (`scan_operator.h`), `RepeatOperatorX` requires `NOOP` (no LE), and the AggSink requires `GLOBAL_EXECUTION_HASH_SHUFFLE` — both hash, so BE skips the local exchange above `Repeat`.
`RequireHash.satisfy(<any hash>) == true` on the FE side is the exact mirror of that BE rule, and `RepeatNode` forwarding the child's distribution is what lets the FE satisfy-check observe the same distribution BE's `_data_distribution` holds. Returning `NOOP` would force a local exchange BE never inserts, diverging from the native path.
So I'll keep the current behavior. Happy to add a coverage case asserting that a hash-distributed child is forwarded and no LE is inserted above `Repeat`, to lock in the parity.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4701476342
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/8db568b5100ed397fc6347033402642e90e5509e_8db568b5100ed397fc6347033402642e90e5509e_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/8db568b5100ed397fc6347033402642e90e5509e_8db568b5100ed397fc6347033402642e90e5509e_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 77.32% (1889/2443) |
| Line Coverage | 64.40% (33955/52725) |
| Region Coverage | 64.76% (17452/26948) |
| Branch Coverage | 53.94% (9341/17316) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4668784622
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4680401624
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/1f614d8cadd1619a8c17dd6f7ec7b12ba6983286_1f614d8cadd1619a8c17dd6f7ec7b12ba6983286_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/1f614d8cadd1619a8c17dd6f7ec7b12ba6983286_1f614d8cadd1619a8c17dd6f7ec7b12ba6983286_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 77.32% (1889/2443) |
| Line Coverage | 64.41% (33958/52725) |
| Region Coverage | 64.80% (17463/26948) |
| Branch Coverage | 53.97% (9345/17316) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4675108661
<details>
<summary>TPC-H: <b>Total hot run time: 28973 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit f056ebbed42446ea468bcc6891c05520bdfb31c3, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17614 4133 4060 4060
q2 q3 10699 1418 807 807
q4 4693 475 354 354
q5 7606 866 586 586
q6 185 173 135 135
q7 790 852 630 630
q8 9393 1551 1576 1551
q9 5673 4506 4511 4506
q10 6750 1806 1545 1545
q11 442 275 250 250
q12 637 424 291 291
q13 18158 3444 2804 2804
q14 264 265 247 247
q15 q16 830 778 708 708
q17 1098 874 981 874
q18 7191 5906 5377 5377
q19 2622 1492 1115 1115
q20 543 424 286 286
q21 6567 2791 2532 2532
q22 455 384 315 315
Total cold run time: 102210 ms
Total hot run time: 28973 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 5033 4887 4937 4887
q2 q3 5054 5188 4613 4613
q4 2108 2285 1396 1396
q5 4875 4779 4824 4779
q6 238 181 131 131
q7 1826 1745 1541 1541
q8 2447 2055 1910 1910
q9 7600 7635 7595 7595
q10 4731 4665 4205 4205
q11 562 414 378 378
q12 734 745 522 522
q13 3014 3376 2831 2831
q14 278 274 258 258
q15 q16 675 694 607 607
q17 1290 1260 1253 1253
q18 7423 6887 6984 6887
q19 1121 1098 1113 1098
q20 2215 2290 1938 1938
q21 5282 4641 4909 4641
q22 526 465 402 402
Total cold run time: 57032 ms
Total hot run time: 51872 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4680255375
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/9f33ce47e1bdfae26100cf19b96a7eab06238d13_9f33ce47e1bdfae26100cf19b96a7eab06238d13_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/9f33ce47e1bdfae26100cf19b96a7eab06238d13_9f33ce47e1bdfae26100cf19b96a7eab06238d13_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 77.32% (1889/2443) |
| Line Coverage | 64.40% (33955/52725) |
| Region Coverage | 64.77% (17454/26948) |
| Branch Coverage | 53.93% (9338/17316) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3397293736
##########
fe/fe-core/src/main/java/org/apache/doris/datasource/tvf/source/TVFScanNode.java:
##########
@@ -212,4 +217,10 @@ protected void setScanParams(TFileRangeDesc rangeDesc, Split split) {
public int getNumInstances() {
return scanRangeLocations.size();
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(PlanTranslatorContext translatorContext,
+ PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ return Pair.of(this, LocalExchangeType.PASSTHROUGH);
+ }
Review Comment:
`PASSTHROUGH` here says the scan already satisfies a parent `requirePassthrough()`, so `PlanNode.enforceRequire()` will not insert a `LocalExchangeNode(PASSTHROUGH)` above this TVF scan. A scan leaf has not done that rebalance; BE local-exchange planning treats non-hash requirements as always needing an exchange (`Pipeline::need_to_local_exchange()` returns true for non-hash target types). A concrete miss is `TVFScan -> merge sort` / `AssertNumRows` / `TableFunction`, or a broadcast-join probe with force-passthrough: FE-planned mode skips the local exchange while BE-planned mode would insert it, leaving the parent on the scan original task distribution. Please report `NOOP` here, or actually wrap the scan in a `LocalExchangeNode` when a parent asks for passthrough, so non-hash requirements are not silently satisfied by a leaf.
```suggestion
return Pair.of(this, LocalExchangeType.NOOP);
```
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4673785565
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4550680970
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4674228779
<details>
<summary>TPC-DS: <b>Total hot run time: 169552 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 7e15d5b3b9109d275feadd1af404dcc86ee9b487, data reload: false
query5 4318 644 480 480
query6 431 192 177 177
query7 4849 558 304 304
query8 367 228 204 204
query9 8754 4108 4096 4096
query10 477 329 297 297
query11 5943 2348 2128 2128
query12 151 114 99 99
query13 1306 614 408 408
query14 6368 5411 5033 5033
query14_1 4403 4431 4371 4371
query15 214 198 190 190
query16 1049 457 435 435
query17 1120 700 602 602
query18 2732 509 362 362
query19 212 189 144 144
query20 114 109 115 109
query21 221 143 122 122
query22 13671 13566 13517 13517
query23 17367 16420 16131 16131
query23_1 16232 16212 16274 16212
query24 7429 1804 1302 1302
query24_1 1319 1317 1316 1316
query25 586 463 420 420
query26 1303 313 174 174
query27 2657 569 347 347
query28 4439 2073 2037 2037
query29 1087 637 529 529
query30 321 239 198 198
query31 1162 1087 965 965
query32 117 67 61 61
query33 535 346 268 268
query34 1170 1155 655 655
query35 773 805 677 677
query36 1395 1373 1248 1248
query37 162 111 95 95
query38 3243 3146 2993 2993
query39 943 923 891 891
query39_1 884 903 870 870
query40 223 132 110 110
query41 76 68 72 68
query42 101 100 98 98
query43 327 331 280 280
query44
query45 211 185 180 180
query46 1119 1220 779 779
query47 2372 2402 2242 2242
query48 408 415 314 314
query49 651 485 369 369
query50 1024 356 258 258
query51 4491 4489 4330 4330
query52 95 93 80 80
query53 251 272 201 201
query54 280 243 218 218
query55 86 81 77 77
query56 260 248 243 243
query57 1464 1384 1319 1319
query58 285 218 210 210
query59 1588 1672 1419 1419
query60 280 246 250 246
query61 163 157 159 157
query62 719 642 587 587
query63 232 190 185 185
query64 2532 776 629 629
query65
query66 1769 483 358 358
query67 29783 29669 29461 29461
query68
query69 419 307 269 269
query70 970 967 951 951
query71 297 216 200 200
query72 2903 2702 2400 2400
query73 864 804 441 441
query74 5152 4987 4774 4774
query75 2654 2564 2236 2236
query76 2324 1145 830 830
query77 393 369 292 292
query78 12627 12532 11853 11853
query79 1416 1025 798 798
query80 1324 477 403 403
query81 530 285 257 257
query82 571 157 119 119
query83 323 269 251 251
query84
query85 930 537 442 442
query86 430 299 275 275
query87 3428 3346 3172 3172
query88 3686 2778 2758 2758
query89 421 383 327 327
query90 1915 185 179 179
query91 180 168 139 139
query92 67 62 58 58
query93 1655 1477 863 863
query94 741 345 293 293
query95 660 472 343 343
query96 1055 781 327 327
query97 2729 2711 2548 2548
query98 213 203 202 202
query99 1133 1199 990 990
Total cold run time: 253032 ms
Total hot run time: 169552 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4687233701
# FE Regression Coverage Report
Increment line coverage ` 79.90% (461/577)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_9ccb7f30b5fb41183c99a77cc3de3311715aa9b6_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_9ccb7f30b5fb41183c99a77cc3de3311715aa9b6_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4686164925
/review
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4733249582
<details>
<summary>TPC-DS: <b>Total hot run time: 175572 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit c2c51332d7f90c80ccabf77b27c23317d636a128, data reload: false
query5 4310 624 478 478
query6 428 190 166 166
query7 4826 579 316 316
query8 361 216 201 201
query9 8745 4094 4085 4085
query10 442 300 254 254
query11 5909 2329 2129 2129
query12 148 102 95 95
query13 1241 611 430 430
query14 6383 5369 5029 5029
query14_1 4350 4358 4363 4358
query15 203 194 175 175
query16 989 453 417 417
query17 938 677 563 563
query18 2489 471 332 332
query19 192 183 143 143
query20 110 107 102 102
query21 216 142 120 120
query22 13662 13669 13397 13397
query23 17373 16523 16119 16119
query23_1 16265 16322 16299 16299
query24 7630 1808 1293 1293
query24_1 1319 1298 1315 1298
query25 536 434 375 375
query26 1323 305 164 164
query27 2707 566 344 344
query28 4501 2039 2051 2039
query29 1079 632 462 462
query30 319 249 199 199
query31 1116 1068 951 951
query32 97 61 55 55
query33 514 302 239 239
query34 1185 1123 681 681
query35 758 789 679 679
query36 1367 1385 1214 1214
query37 160 109 96 96
query38 3249 3138 3067 3067
query39 928 911 901 901
query39_1 866 876 867 867
query40 219 125 105 105
query41 71 67 67 67
query42 98 95 94 94
query43 326 331 283 283
query44 1462 778 782 778
query45 195 186 181 181
query46 1069 1235 779 779
query47 2330 2350 2243 2243
query48 392 429 309 309
query49 641 473 358 358
query50 1050 356 261 261
query51 4449 4380 4357 4357
query52 89 89 78 78
query53 253 267 201 201
query54 301 253 214 214
query55 79 76 72 72
query56 274 242 215 215
query57 1440 1387 1342 1342
query58 257 221 222 221
query59 1573 1647 1476 1476
query60 282 257 231 231
query61 172 172 173 172
query62 702 658 593 593
query63 224 193 203 193
query64 2563 818 628 628
query65 4902 4817 4817 4817
query66 1806 491 360 360
query67 29745 29676 29561 29561
query68 3177 1592 1026 1026
query69 426 306 270 270
query70 1065 959 985 959
query71 305 245 214 214
query72 2850 2611 2340 2340
query73 811 751 437 437
query74 5170 4947 4804 4804
query75 2622 2598 2218 2218
query76 2333 1184 799 799
query77 363 376 299 299
query78 12414 12506 11724 11724
query79 1426 1197 807 807
query80 1272 490 388 388
query81 510 284 235 235
query82 562 162 118 118
query83 330 273 248 248
query84 307 148 109 109
query85 902 508 409 409
query86 420 298 297 297
query87 3383 3330 3195 3195
query88 3760 2831 2798 2798
query89 432 372 340 340
query90 1959 178 179 178
query91 177 161 132 132
query92 63 58 55 55
query93 1498 1576 889 889
query94 695 391 304 304
query95 684 392 346 346
query96 1086 809 326 326
query97 2696 2671 2556 2556
query98 212 206 197 197
query99 1170 1193 1026 1026
Total cold run time: 261645 ms
Total hot run time: 175572 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4710090655
# FE Regression Coverage Report
Increment line coverage ` 80.25% (451/562)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_832ba5f0bf8ab5846bb316e94f1099ae58fe466f_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_832ba5f0bf8ab5846bb316e94f1099ae58fe466f_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4741464159
<details>
<summary>TPC-DS: <b>Total hot run time: 176552 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 0436cc4cb573b104a59408a6169bd145bcfeff6c, data reload: false
query5 4318 652 501 501
query6 433 197 171 171
query7 4834 558 302 302
query8 363 219 208 208
query9 8785 4106 4122 4106
query10 482 303 253 253
query11 5955 2353 2143 2143
query12 153 103 102 102
query13 1289 611 415 415
query14 6461 5395 5052 5052
query14_1 4394 4410 4353 4353
query15 207 200 175 175
query16 1033 451 413 413
query17 1108 697 545 545
query18 2705 477 335 335
query19 221 180 139 139
query20 114 106 107 106
query21 216 142 123 123
query22 13806 13617 13455 13455
query23 17412 16587 16222 16222
query23_1 16314 16376 16297 16297
query24 7466 1785 1367 1367
query24_1 1376 1364 1317 1317
query25 571 458 407 407
query26 1343 308 178 178
query27 2628 580 343 343
query28 4440 2051 2038 2038
query29 1093 655 507 507
query30 321 241 208 208
query31 1118 1078 960 960
query32 112 64 63 63
query33 535 331 261 261
query34 1194 1149 693 693
query35 755 800 692 692
query36 1420 1402 1199 1199
query37 156 103 97 97
query38 3239 3149 3083 3083
query39 945 921 907 907
query39_1 878 911 885 885
query40 222 130 104 104
query41 70 70 70 70
query42 100 97 98 97
query43 340 344 295 295
query44 1514 788 798 788
query45 198 189 182 182
query46 1095 1248 749 749
query47 2352 2379 2240 2240
query48 400 426 298 298
query49 646 491 371 371
query50 1024 376 275 275
query51 4472 4441 4397 4397
query52 91 94 89 89
query53 262 284 199 199
query54 277 239 212 212
query55 87 78 73 73
query56 244 243 238 238
query57 1446 1442 1309 1309
query58 250 232 223 223
query59 1602 1715 1509 1509
query60 270 238 222 222
query61 154 146 151 146
query62 704 649 588 588
query63 230 191 193 191
query64 2517 747 601 601
query65 4882 4813 4805 4805
query66 1724 470 343 343
query67 29882 29803 29575 29575
query68 3548 1559 898 898
query69 421 313 267 267
query70 1093 984 975 975
query71 309 242 211 211
query72 2975 2637 2397 2397
query73 912 801 442 442
query74 5141 4991 4759 4759
query75 2671 2565 2228 2228
query76 2337 1183 811 811
query77 363 376 280 280
query78 12395 12531 11922 11922
query79 1395 1207 780 780
query80 1280 484 398 398
query81 529 284 242 242
query82 563 162 128 128
query83 326 295 265 265
query84 260 150 112 112
query85 902 512 408 408
query86 430 316 282 282
query87 3414 3352 3211 3211
query88 3735 2792 2810 2792
query89 435 381 326 326
query90 1932 188 176 176
query91 173 161 134 134
query92 66 62 53 53
query93 1466 1533 914 914
query94 700 348 346 346
query95 669 382 331 331
query96 1074 792 351 351
query97 2691 2685 2580 2580
query98 224 211 200 200
query99 1171 1148 1022 1022
Total cold run time: 263308 ms
Total hot run time: 176552 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4708799564
<details>
<summary>TPC-H: <b>Total hot run time: 28865 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 14ff5b48070e26ba3f1518c301fce97c36998331, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17792 4097 4116 4097
q2 q3 10919 1427 835 835
q4 4747 477 338 338
q5 8470 868 592 592
q6 356 175 135 135
q7 904 845 620 620
q8 10888 1658 1676 1658
q9 7158 4525 4490 4490
q10 6808 1825 1514 1514
q11 449 276 244 244
q12 644 430 290 290
q13 18276 3421 2808 2808
q14 260 255 248 248
q15 q16 819 770 705 705
q17 926 835 891 835
q18 6909 5720 5559 5559
q19 1310 1298 1100 1100
q20 521 415 265 265
q21 5507 2608 2232 2232
q22 434 355 300 300
Total cold run time: 104097 ms
Total hot run time: 28865 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4429 4376 4423 4376
q2 q3 4572 5023 4309 4309
q4 2092 2173 1377 1377
q5 4504 4361 4374 4361
q6 227 179 147 147
q7 2223 1852 1670 1670
q8 2502 2182 2291 2182
q9 8127 8092 8085 8085
q10 4841 4803 4379 4379
q11 582 428 380 380
q12 755 744 537 537
q13 3325 3630 3021 3021
q14 315 285 272 272
q15 q16 708 720 637 637
q17 1373 1374 1392 1374
q18 7973 7433 6985 6985
q19 1108 1157 1167 1157
q20 2232 2282 1943 1943
q21 5239 4594 4462 4462
q22 516 474 412 412
Total cold run time: 57643 ms
Total hot run time: 52066 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3413407415
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PlanTranslatorContext.java:
##########
@@ -109,10 +109,36 @@ public class PlanTranslatorContext {
private final Map<ScanNode, Set<SlotId>> statsUnknownColumnsMap = Maps.newHashMap();
+ // Per-node "is there a serial operator between me and the pipeline's sink" flag.
+ // Mirrors BE's any_of(operators[idx..end], is_serial_operator) check used by
+ // _add_local_exchange / need_to_local_exchange to skip LE insertion when an ancestor
+ // in the same pipeline is already serial (the whole pipeline runs with 1 task, so an
+ // extra LE would be a no-op). Written by AddLocalExchange entry + PlanNode.enforceRequire
+ // step 1 (root → leaf during traversal). Read by PlanNode.enforceRequire step 4 (Layer 1
+ // skip) and by child overrides that compute their require. Reset to false at fragment
+ // root and across pipeline boundaries (see shouldResetSerialFlagForChild).
+ private final Map<PlanNodeId, Boolean> serialAncestorInPipelineMap = Maps.newHashMap();
+
+ // Per-node "is there a downstream operator that depends on hash distribution for
+ // correctness, with HASH/NOOP path connecting it to me" flag. Mirrors BE's
+ // _followed_by_shuffled_operator propagation in pipeline_fragment_context.cpp.
+ // Written by PlanNode.enforceRequire step 1b (root → leaf). Read by SetOperationNode
+ // to decide whether to propagate hash requirement to its inputs (only when downstream
+ // needs shuffle for correctness, not just for performance like StreamingAgg pre-agg).
+ private final Map<PlanNodeId, Boolean> shuffledAncestorMap = Maps.newHashMap();
+
+ // Whether the current fragment uses LocalShuffleAssignedJob (pooling scan with
+ // ignoreDataDistribution → _parallel_instances=1 in BE). When true, serial operators
+ // indicate real pipeline bottlenecks needing PASSTHROUGH fan-out (heavy_ops).
private boolean isTopMaterializeNode = true;
private final Set<SlotId> virtualColumnIds = Sets.newHashSet();
+ // Used by AddLocalExchange: tracks whether any serial operator exists
Review Comment:
Done in `fe9a3316be0` — removed the field and its getter/setter. The live serial-ancestor propagation uses `serialAncestorInPipelineMap` (set at `AddLocalExchange:94` / `PlanNode:1108`), so this was indeed dead.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -939,4 +1026,261 @@ private String mergeIcebergAccessPathsWithId(
}
return StringUtils.join(mergeDisplayAccessPaths, ", ");
}
+
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ ArrayList<PlanNode> newChildren = Lists.newArrayList();
+ for (int i = 0; i < children.size(); i++) {
+ Pair<PlanNode, LocalExchangeType> childOutput
+ = enforceRequire(translatorContext, children.get(i), i, LocalExchangeTypeRequire.noRequire());
+ newChildren.add(childOutput.first);
+ }
+ this.children = newChildren;
+ return Pair.of(this, LocalExchangeType.NOOP);
+ }
+
+ /**
+ * Unified framework method: propagate serial flag → recurse child → satisfy check → Layer 1 skip → insert LE.
+ * Replaces the old enforceChild/enforceChildExchange/forceEnforceChildExchange trio.
+ *
+ * <h3>Data flow</h3>
+ * <ul>
+ * <li><b>serial-ancestor flag</b> ({@link PlanTranslatorContext#hasSerialAncestorInPipeline})
+ * — flows root → leaf during traversal. Mirrors BE's
+ * {@code any_of(operators[idx..end], is_serial_operator)} check used by
+ * {@code _add_local_exchange} to skip LE insertion when an ancestor in the same
+ * pipeline is already serial. Reset at pipeline boundaries via
+ * {@link #shouldResetSerialFlagForChild}.</li>
+ * <li><b>shuffle-for-correctness flag</b>
+ * ({@link PlanTranslatorContext#hasShuffleForCorrectnessAncestor}) — also flows
+ * root → leaf. Mirrors BE's {@code _followed_by_shuffled_operator}: tells a
+ * child whether some downstream operator depends on hash distribution for
+ * correctness, so {@code SetOperationNode} can pre-shuffle union branches.</li>
+ * <li><b>return value</b> {@code Pair<PlanNode, LocalExchangeType>} — first is the
+ * (possibly LE-wrapped) child; second is the actual output distribution as
+ * observed by the parent. Caller's {@code require.satisfy(output)} decides
+ * whether more LE is needed.</li>
+ * <li><b>parent.require</b> describes the constraint on the child output —
+ * computed inside the parent's {@code enforceAndDeriveLocalExchange} per child.</li>
+ * </ul>
+ *
+ * <h3>Invariants</h3>
+ * <ul>
+ * <li>Every serial → non-serial transition has an LE somewhere between them
+ * (enforced by framework step 3 below, validated by
+ * {@link AddLocalExchange#validateNoSerialWithoutLocalExchange}).</li>
Review Comment:
Done in `fe9a3316be0`. Removed the dead `@link` to `validateNoSerialWithoutLocalExchange` and rewrote the bullet to the narrower truth: framework step 3 inserts the LE only where a serial→non-serial transition actually needs redistribution; a serial child feeding a parent that requires PASSTHROUGH/noRequire (TableFunction, NLJ, Agg) is already correct and needs none — so it is intentionally not validated by a post-pass.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -939,4 +1026,261 @@ private String mergeIcebergAccessPathsWithId(
}
return StringUtils.join(mergeDisplayAccessPaths, ", ");
}
+
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ ArrayList<PlanNode> newChildren = Lists.newArrayList();
+ for (int i = 0; i < children.size(); i++) {
+ Pair<PlanNode, LocalExchangeType> childOutput
+ = enforceRequire(translatorContext, children.get(i), i, LocalExchangeTypeRequire.noRequire());
+ newChildren.add(childOutput.first);
+ }
+ this.children = newChildren;
+ return Pair.of(this, LocalExchangeType.NOOP);
+ }
+
+ /**
+ * Unified framework method: propagate serial flag → recurse child → satisfy check → Layer 1 skip → insert LE.
+ * Replaces the old enforceChild/enforceChildExchange/forceEnforceChildExchange trio.
+ *
+ * <h3>Data flow</h3>
+ * <ul>
+ * <li><b>serial-ancestor flag</b> ({@link PlanTranslatorContext#hasSerialAncestorInPipeline})
+ * — flows root → leaf during traversal. Mirrors BE's
+ * {@code any_of(operators[idx..end], is_serial_operator)} check used by
+ * {@code _add_local_exchange} to skip LE insertion when an ancestor in the same
+ * pipeline is already serial. Reset at pipeline boundaries via
+ * {@link #shouldResetSerialFlagForChild}.</li>
+ * <li><b>shuffle-for-correctness flag</b>
+ * ({@link PlanTranslatorContext#hasShuffleForCorrectnessAncestor}) — also flows
+ * root → leaf. Mirrors BE's {@code _followed_by_shuffled_operator}: tells a
+ * child whether some downstream operator depends on hash distribution for
+ * correctness, so {@code SetOperationNode} can pre-shuffle union branches.</li>
+ * <li><b>return value</b> {@code Pair<PlanNode, LocalExchangeType>} — first is the
+ * (possibly LE-wrapped) child; second is the actual output distribution as
+ * observed by the parent. Caller's {@code require.satisfy(output)} decides
+ * whether more LE is needed.</li>
+ * <li><b>parent.require</b> describes the constraint on the child output —
+ * computed inside the parent's {@code enforceAndDeriveLocalExchange} per child.</li>
+ * </ul>
+ *
+ * <h3>Invariants</h3>
+ * <ul>
+ * <li>Every serial → non-serial transition has an LE somewhere between them
+ * (enforced by framework step 3 below, validated by
+ * {@link AddLocalExchange#validateNoSerialWithoutLocalExchange}).</li>
Review Comment:
Done in `fe9a3316be0` (same fix as the other thread on this line): the `@link` to the removed `validateNoSerialWithoutLocalExchange` is gone and the invariant is reworded to the narrower condition rather than claiming every serial→non-serial transition has an LE.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -142,7 +145,8 @@ public abstract class PlanNode extends TreeNode<PlanNode> {
protected int nereidsId = -1;
- private List<List<Expr>> childrenDistributeExprLists = new ArrayList<>();
+ protected List<List<Expr>> childrenDistributeExprLists = new ArrayList<>();
+ protected List<Expr> distributeExprLists = new ArrayList<>();
Review Comment:
Done in `fe9a3316be0` — added comments: `childrenDistributeExprLists.get(i)` is the per-child hash-key expr list used to repartition the i-th child's input; `distributeExprLists` is this node's own output hash-key exprs serialized to BE.
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -1796,6 +2011,89 @@ Status PipelineFragmentContext::_create_operator(ObjectPool* pool, const TPlanNo
RETURN_IF_ERROR(cur_pipe->add_operator(op, _parallel_instances));
break;
}
+ case TPlanNodeType::LOCAL_EXCHANGE_NODE: {
+ op = std::make_shared<LocalExchangeSourceOperatorX>(pool, tnode, next_operator_id(), descs);
+ // The downstream pipeline (containing LocalExchangeSource) must have
+ // _num_instances tasks — matching BE-native _inherit_pipeline_properties
+ // which sets pipe_with_source.set_num_tasks(_num_instances).
+ // Without this, when the parent pipeline was reduced by a serial operator
+ // (e.g., serial Exchange with use_serial_exchange=true, or UNPARTITIONED
+ // Exchange), the downstream inherits the reduced num_tasks via
+ // add_pipeline(parent). The deferred exchanger creates _num_instances
+ // channels but only fewer source tasks initialize mem_counters — the
+ // sink round-robins to all channels and crashes on uninitialized ones.
+ auto downstream_num_tasks = _num_instances;
Review Comment:
Done in `fe9a3316be0` — inlined to `cur_pipe->set_num_tasks(_num_instances)`.
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -690,6 +716,188 @@ Status PipelineFragmentContext::_build_pipelines(ObjectPool* pool, const Descrip
return Status::OK();
}
+Status PipelineFragmentContext::_create_deferred_local_exchangers() {
+ for (auto& info : _deferred_exchangers) {
+ // DANGER ZONE — do not "fix" this line without reading the history.
+ //
+ // sender_count seeds Exchanger::_running_sink_operators, which the source side
+ // waits to reach 0 via sub_running_sink_operators on each sink LocalState close.
+ // The correct value is THIS pipeline-instance's sink task count, which is exactly
+ // info.upstream_pipe->num_tasks() — one PipelineTask per task, one close per task.
+ //
+ // Tempting wrong fix #1: `std::max(num_tasks, _num_instances)` to mirror the
+ // BE-planned path in _add_local_exchange_impl (~line 1023). THIS BREAKS the
+ // common FE-planned shape of `serial scan → LE(PT) → ...`: upstream_pipe
+ // genuinely has num_tasks=1, only 1 close arrives, but seed becomes
+ // _num_instances so _running_sink_operators never reaches 0 — downstream
+ // sources hang on SHUFFLE_DATA_DEPENDENCY (e.g. MTMV refresh from
+ // mtmv_up_down_job_p0/load.groovy stays at Status=RUNNING; build 949402
Review Comment:
Done in `fe9a3316be0` — dropped the internal build id from the comment (kept the test reference).
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3411860865
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AddLocalExchange.java:
##########
@@ -0,0 +1,182 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.planner;
+
+import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.glue.translator.PlanTranslatorContext;
+import org.apache.doris.nereids.trees.plans.distribute.DistributedPlan;
+import org.apache.doris.nereids.trees.plans.distribute.FragmentIdMapping;
+import org.apache.doris.nereids.trees.plans.distribute.PipelineDistributedPlan;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeType;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeTypeRequire;
+import org.apache.doris.planner.LocalExchangeNode.RequireHash;
+
+/**
+ * FE-side local exchange planner — inserts {@link LocalExchangeNode} into each fragment's
+ * plan tree so that within-fragment data redistribution is decided at planning time
+ * instead of at BE pipeline-build time.
+ *
+ * <h3>When this runs</h3>
+ * Invoked from {@code NereidsPlanner.addLocalExchangeAfterDistribute()} right after
+ * {@code DistributePlanner} has assigned instances to fragments and before the plan is
+ * serialized to BE. Gated by session variable {@code enable_local_shuffle_planner}
+ * (default true) and {@code enable_local_shuffle}; when either is off this pass is
+ * skipped entirely and BE falls back to its own {@code _plan_local_exchange}. The two
+ * paths are mutually exclusive: BE consults {@code runtime_state.h::plan_local_shuffle()}
+ * to know whether it should plan LE itself.
+ *
+ * <h3>What it changes</h3>
+ * <ul>
+ * <li>For each fragment with {@code maxPerBeInstances > 1}, walks the plan tree
+ * bottom-up via {@link PlanNode#enforceAndDeriveLocalExchange} and inserts
+ * LocalExchangeNodes where children's output distribution doesn't satisfy the
+ * parent's requirement.</li>
+ * <li>May wrap the fragment root with an extra PASSTHROUGH LE so the data sink
+ * (DataStreamSink / OlapTableSink) runs with the full instance count even when
+ * the root operator is serial — see {@link #addLocalExchangeForFragment}.</li>
+ * <li>Does NOT modify the fragment sink itself, fragment boundaries, or instance
+ * assignment.</li>
+ * </ul>
+ *
+ * <h3>Per-BE instance semantics</h3>
+ * Skips fragments where every BE has at most 1 instance. Using a global instance count
+ * would insert LE for "2 BEs × 1 instance" cases, which BE's own
+ * {@code _plan_local_exchange} would not — leading to pipeline task-count mismatch and
+ * deadlock. See {@link #addLocalExchange}.
+ *
+ * <h3>Reading order</h3>
+ * Start with {@link PlanNode#enforceRequire} (the recursion engine), then individual
+ * {@code enforceAndDeriveLocalExchange} overrides on PlanNode subclasses.
+ */
+public class AddLocalExchange {
+ /** addLocalExchange with distributed plans, skipping single-instance fragments.
+ * BE's _plan_local_exchange checks _num_instances which is the per-BE instance count.
+ * With _num_instances<=1 all pipelines on that BE have 1 task so local exchange is a no-op.
+ * We must use the same per-BE semantics: skip when every BE has at most 1 instance.
+ * Using global instanceCount would insert LE for fragments where 2 BEs each have 1 instance
+ * (global=2, per-BE=1), causing pipeline task mismatch and deadlock. */
+ public void addLocalExchange(FragmentIdMapping<DistributedPlan> distributedPlans,
+ PlanTranslatorContext context) {
+ for (DistributedPlan plan : distributedPlans.values()) {
+ PipelineDistributedPlan pipePlan = (PipelineDistributedPlan) plan;
+ long maxPerBeInstances = pipePlan.getInstanceJobs().stream()
+ .collect(java.util.stream.Collectors.groupingBy(
+ j -> j.getAssignedWorker().id(), java.util.stream.Collectors.counting()))
+ .values().stream().mapToLong(Long::longValue).max().orElse(0);
+ if (maxPerBeInstances <= 1) {
+ continue;
+ }
+ PlanFragment fragment = pipePlan.getFragmentJob().getFragment();
+ addLocalExchangeForFragment(fragment, context);
+ }
+ }
+
+ private void addLocalExchangeForFragment(PlanFragment fragment, PlanTranslatorContext context) {
+ DataSink sink = fragment.getSink();
+ LocalExchangeTypeRequire require = sink == null
+ ? LocalExchangeTypeRequire.noRequire() : sink.getLocalExchangeTypeRequire();
+ PlanNode root = fragment.getPlanRoot();
+ context.setHasSerialAncestorInPipeline(root, false);
+ Pair<PlanNode, LocalExchangeType> output = root
+ .enforceAndDeriveLocalExchange(context, null, require);
+ PlanNode newRoot = output.first;
+ // The fragment data sink (DataStreamSink, OlapTableSink) runs in the same pipeline
+ // as the root. If the root will be serial on BE, the sink pipeline has 1 task —
+ // only instance 0 sends data, others hang or miss writes.
+ // Insert PASSTHROUGH fan-out so sink runs with _num_instances tasks.
+ // This matches BE-native's default required_data_distribution():
+ // _child->is_serial_operator() ? PASSTHROUGH : NOOP
+ if (newRoot.isSerialOperatorOnBe(context.getConnectContext())) {
+ newRoot = new LocalExchangeNode(context.nextPlanNodeId(), newRoot,
+ LocalExchangeType.PASSTHROUGH, null);
+ }
+ if (newRoot != root) {
+ fragment.setPlanRoot(newRoot);
+ }
+ validateNoSerialWithoutLocalExchange(fragment.getPlanRoot(), context.getConnectContext());
+ }
+
+ /**
+ * In a local-shuffle fragment, the root check above guarantees the root pipeline
+ * has N tasks. Any serial operator reduces its pipeline to 1 task. If this serial
+ * operator feeds into a non-serial parent without LocalExchangeNode in between,
+ * some pipelines have 1 task while others have N → shared_state mismatch, data loss.
+ *
+ * Serial→serial chains are fine (all at 1 task, consistent). Only the transition
+ * from serial to non-serial needs LE to restore parallelism.
+ */
+ private void validateNoSerialWithoutLocalExchange(PlanNode node,
+ org.apache.doris.qe.ConnectContext context) {
+ for (PlanNode child : node.getChildren()) {
+ validateNoSerialWithoutLocalExchange(child, context);
+ if (child.isSerialOperatorOnBe(context)
+ && !(child instanceof LocalExchangeNode)
+ && !(node instanceof LocalExchangeNode)
+ && !(node instanceof ExchangeNode)
+ && !node.isSerialOperatorOnBe(context)) {
+ org.apache.logging.log4j.LogManager.getLogger(AddLocalExchange.class).warn(
+ "Serial " + child.getClass().getSimpleName() + "(id=" + child.getId()
+ + ") feeds into non-serial " + node.getClass().getSimpleName()
+ + "(id=" + node.getId() + ") without LocalExchangeNode"
+ + " in fragment " + node.getFragment().getFragmentId()
+ + ". FE should insert LocalExchangeNode to restore parallelism.");
+ }
+ }
+ }
+
+ public static boolean isColocated(PlanNode plan) {
+ if (plan instanceof AggregationNode) {
+ return ((AggregationNode) plan).isColocate() && isColocated(plan.getChild(0));
+ } else if (plan instanceof OlapScanNode) {
+ return true;
+ } else if (plan instanceof SelectNode) {
+ return isColocated(plan.getChild(0));
+ } else if (plan instanceof HashJoinNode) {
+ return ((HashJoinNode) plan).isColocate()
+ && (isColocated(plan.getChild(0)) || isColocated(plan.getChild(1)));
+ } else if (plan instanceof SetOperationNode) {
+ if (!((SetOperationNode) plan).isColocate()) {
+ return false;
+ }
+ for (PlanNode child : plan.getChildren()) {
+ if (isColocated(child)) {
+ return true;
+ }
Review Comment:
Done — removed all three unused params (`translatorContext`/`parent`/`child`); `resolveExchangeType(LocalExchangeTypeRequire require)` now reads only `require`. Updated all call sites + the unit test (this also resolves the `SetOperationNode` null-`firstChild` note below).
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AddLocalExchange.java:
##########
@@ -0,0 +1,182 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.planner;
+
+import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.glue.translator.PlanTranslatorContext;
+import org.apache.doris.nereids.trees.plans.distribute.DistributedPlan;
+import org.apache.doris.nereids.trees.plans.distribute.FragmentIdMapping;
+import org.apache.doris.nereids.trees.plans.distribute.PipelineDistributedPlan;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeType;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeTypeRequire;
+import org.apache.doris.planner.LocalExchangeNode.RequireHash;
+
+/**
+ * FE-side local exchange planner — inserts {@link LocalExchangeNode} into each fragment's
+ * plan tree so that within-fragment data redistribution is decided at planning time
+ * instead of at BE pipeline-build time.
+ *
+ * <h3>When this runs</h3>
+ * Invoked from {@code NereidsPlanner.addLocalExchangeAfterDistribute()} right after
+ * {@code DistributePlanner} has assigned instances to fragments and before the plan is
+ * serialized to BE. Gated by session variable {@code enable_local_shuffle_planner}
+ * (default true) and {@code enable_local_shuffle}; when either is off this pass is
+ * skipped entirely and BE falls back to its own {@code _plan_local_exchange}. The two
+ * paths are mutually exclusive: BE consults {@code runtime_state.h::plan_local_shuffle()}
+ * to know whether it should plan LE itself.
+ *
+ * <h3>What it changes</h3>
+ * <ul>
+ * <li>For each fragment with {@code maxPerBeInstances > 1}, walks the plan tree
+ * bottom-up via {@link PlanNode#enforceAndDeriveLocalExchange} and inserts
+ * LocalExchangeNodes where children's output distribution doesn't satisfy the
+ * parent's requirement.</li>
+ * <li>May wrap the fragment root with an extra PASSTHROUGH LE so the data sink
+ * (DataStreamSink / OlapTableSink) runs with the full instance count even when
+ * the root operator is serial — see {@link #addLocalExchangeForFragment}.</li>
+ * <li>Does NOT modify the fragment sink itself, fragment boundaries, or instance
+ * assignment.</li>
+ * </ul>
+ *
+ * <h3>Per-BE instance semantics</h3>
+ * Skips fragments where every BE has at most 1 instance. Using a global instance count
+ * would insert LE for "2 BEs × 1 instance" cases, which BE's own
+ * {@code _plan_local_exchange} would not — leading to pipeline task-count mismatch and
+ * deadlock. See {@link #addLocalExchange}.
+ *
+ * <h3>Reading order</h3>
+ * Start with {@link PlanNode#enforceRequire} (the recursion engine), then individual
+ * {@code enforceAndDeriveLocalExchange} overrides on PlanNode subclasses.
+ */
+public class AddLocalExchange {
+ /** addLocalExchange with distributed plans, skipping single-instance fragments.
+ * BE's _plan_local_exchange checks _num_instances which is the per-BE instance count.
+ * With _num_instances<=1 all pipelines on that BE have 1 task so local exchange is a no-op.
+ * We must use the same per-BE semantics: skip when every BE has at most 1 instance.
+ * Using global instanceCount would insert LE for fragments where 2 BEs each have 1 instance
+ * (global=2, per-BE=1), causing pipeline task mismatch and deadlock. */
+ public void addLocalExchange(FragmentIdMapping<DistributedPlan> distributedPlans,
+ PlanTranslatorContext context) {
+ for (DistributedPlan plan : distributedPlans.values()) {
+ PipelineDistributedPlan pipePlan = (PipelineDistributedPlan) plan;
+ long maxPerBeInstances = pipePlan.getInstanceJobs().stream()
+ .collect(java.util.stream.Collectors.groupingBy(
+ j -> j.getAssignedWorker().id(), java.util.stream.Collectors.counting()))
+ .values().stream().mapToLong(Long::longValue).max().orElse(0);
+ if (maxPerBeInstances <= 1) {
+ continue;
+ }
+ PlanFragment fragment = pipePlan.getFragmentJob().getFragment();
+ addLocalExchangeForFragment(fragment, context);
+ }
+ }
+
+ private void addLocalExchangeForFragment(PlanFragment fragment, PlanTranslatorContext context) {
+ DataSink sink = fragment.getSink();
+ LocalExchangeTypeRequire require = sink == null
+ ? LocalExchangeTypeRequire.noRequire() : sink.getLocalExchangeTypeRequire();
+ PlanNode root = fragment.getPlanRoot();
+ context.setHasSerialAncestorInPipeline(root, false);
+ Pair<PlanNode, LocalExchangeType> output = root
+ .enforceAndDeriveLocalExchange(context, null, require);
+ PlanNode newRoot = output.first;
+ // The fragment data sink (DataStreamSink, OlapTableSink) runs in the same pipeline
+ // as the root. If the root will be serial on BE, the sink pipeline has 1 task —
+ // only instance 0 sends data, others hang or miss writes.
+ // Insert PASSTHROUGH fan-out so sink runs with _num_instances tasks.
+ // This matches BE-native's default required_data_distribution():
+ // _child->is_serial_operator() ? PASSTHROUGH : NOOP
+ if (newRoot.isSerialOperatorOnBe(context.getConnectContext())) {
+ newRoot = new LocalExchangeNode(context.nextPlanNodeId(), newRoot,
+ LocalExchangeType.PASSTHROUGH, null);
+ }
+ if (newRoot != root) {
+ fragment.setPlanRoot(newRoot);
+ }
+ validateNoSerialWithoutLocalExchange(fragment.getPlanRoot(), context.getConnectContext());
+ }
+
+ /**
+ * In a local-shuffle fragment, the root check above guarantees the root pipeline
+ * has N tasks. Any serial operator reduces its pipeline to 1 task. If this serial
+ * operator feeds into a non-serial parent without LocalExchangeNode in between,
+ * some pipelines have 1 task while others have N → shared_state mismatch, data loss.
+ *
+ * Serial→serial chains are fine (all at 1 task, consistent). Only the transition
+ * from serial to non-serial needs LE to restore parallelism.
+ */
+ private void validateNoSerialWithoutLocalExchange(PlanNode node,
+ org.apache.doris.qe.ConnectContext context) {
+ for (PlanNode child : node.getChildren()) {
+ validateNoSerialWithoutLocalExchange(child, context);
+ if (child.isSerialOperatorOnBe(context)
+ && !(child instanceof LocalExchangeNode)
Review Comment:
Removed the method entirely (commit `3630071a131`) rather than adding a throw/DCHECK/metric — see the longer reply on the [P1] thread at `AddLocalExchange:132`. Short version: the invariant is self-referential (it reads the same `isSerialOperatorOnBe` flag it validates), so it can't catch the one bug class that matters, and it false-fires on legitimate serial→{TableFunction, NLJ, Agg} plans. The regression suite + RQG differential are the actual guard.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/SetOperationNode.java:
##########
@@ -193,4 +198,52 @@ public int getNumInstances() {
public boolean isBucketShuffle() {
return distributionMode.equals(DistributionMode.BUCKET_SHUFFLE);
}
+
+ public boolean isColocate() {
+ return isColocate;
+ }
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(PlanTranslatorContext translatorContext,
+ PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ LocalExchangeTypeRequire requireChild;
+ LocalExchangeType outputType;
+ PlanNode firstChild = children.isEmpty() ? null : children.get(0);
+ if (this instanceof UnionNode) {
+ // Propagate parent's hash requirement to children ONLY when a downstream operator
+ // requires shuffle for correctness (not just performance optimization). Matches BE's
Review Comment:
Resolved by removing the unused `child` param from `resolveExchangeType` — `SetOperationNode` no longer passes `firstChild` (only `requireChild` at `SetOperationNode.java:222/239`), so no null can reach it.
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -690,6 +716,199 @@ Status PipelineFragmentContext::_build_pipelines(ObjectPool* pool, const Descrip
return Status::OK();
}
+Status PipelineFragmentContext::_create_deferred_local_exchangers() {
+ for (auto& info : _deferred_exchangers) {
+ // DANGER ZONE — do not "fix" this line without reading the history.
+ //
+ // sender_count seeds Exchanger::_running_sink_operators, which the source side
+ // waits to reach 0 via sub_running_sink_operators on each sink LocalState close.
+ // The correct value is THIS pipeline-instance's sink task count, which is exactly
+ // info.upstream_pipe->num_tasks() — one PipelineTask per task, one close per task.
+ //
+ // Tempting wrong fix #1: `std::max(num_tasks, _num_instances)` to mirror the
+ // BE-planned path in _add_local_exchange_impl (~line 1023). THIS BREAKS the
+ // common FE-planned shape of `serial scan → LE(PT) → ...`: upstream_pipe
+ // genuinely has num_tasks=1, only 1 close arrives, but seed becomes
+ // _num_instances so _running_sink_operators never reaches 0 — downstream
+ // sources hang on SHUFFLE_DATA_DEPENDENCY (e.g. MTMV refresh from
+ // mtmv_up_down_job_p0/load.groovy stays at Status=RUNNING; build 949402
+ // regressed exactly this way). BE-planned mode uses max() because its
+ // `cur_pipe` is the source-side pipeline (always raised to _num_instances by
+ // add_pipeline) — not analogous to our `upstream_pipe` here, which is the
+ // sink-side pipeline that may legitimately stay at 1 for serial sources.
+ //
+ // Tempting wrong fix #2: multiply by _num_instances on the theory shared_state
+ // is shared across all instances. Same hang — each fragment-instance
+ // PipelineFragmentContext has its OWN _op_id_to_shared_state map, so the
+ // exchanger is per-instance, not per-BE. num_tasks() is already the right
+ // close-count for one instance.
+ //
+ // If a hang shows up with `_running_sink_operators < 0`, the bug is upstream:
+ // _propagate_local_exchange_num_tasks left num_tasks too low (or too high) for
+ // this fragment shape. Fix THAT pass, not this seed value.
+ const int sender_count = info.upstream_pipe->num_tasks();
Review Comment:
Replaced the two iterative `while (changed)` passes in `_propagate_local_exchange_num_tasks()` with a single Kahn topological sweep over the pipeline DAG (commit `a97d3347789`): each pipeline is visited once, after all of its upstreams, so `num_tasks` is set in one pass and the loop can't run unbounded. Added a cycle `DCHECK` instead of a max-iteration guard.
##########
be/src/exec/pipeline/pipeline_fragment_context.h:
##########
@@ -320,8 +322,11 @@ class PipelineFragmentContext : public TaskExecutionContext {
std::mutex _state_map_lock;
- int _operator_id = 0;
- int _sink_operator_id = 0;
+ // Start from -1 so all operator IDs are negative. This avoids collision with
Review Comment:
Thanks — confirmed. Operator IDs are only used as map keys / `dest_id` equality and printed as-is in profiles; nothing enumerates them expecting non-negative. The only hardcoded `0` was the unpaired sink `dest_id=0`, which is exactly the collision the `-1` start avoids. No change needed.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4707388930
<details>
<summary>TPC-H: <b>Total hot run time: 28712 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 1d7cf6dbd6095ee1fb5860efe258b0ce99242ae6, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17658 4065 4030 4030
q2 q3 10957 1469 834 834
q4 4747 475 341 341
q5 8273 899 588 588
q6 321 171 136 136
q7 896 840 632 632
q8 10931 1566 1728 1566
q9 7277 4521 4519 4519
q10 6878 1794 1545 1545
q11 445 278 243 243
q12 634 430 295 295
q13 18112 3405 2723 2723
q14 267 258 239 239
q15 q16 827 776 710 710
q17 901 876 983 876
q18 6824 5756 5537 5537
q19 1144 1196 963 963
q20 518 396 256 256
q21 5593 2659 2366 2366
q22 450 352 313 313
Total cold run time: 103653 ms
Total hot run time: 28712 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4336 4257 4252 4252
q2 q3 4570 4919 4295 4295
q4 2104 2194 1396 1396
q5 4437 4323 4339 4323
q6 229 176 130 130
q7 2126 2034 1666 1666
q8 2546 2210 2219 2210
q9 8333 8174 8228 8174
q10 4853 4740 4345 4345
q11 577 417 403 403
q12 745 770 559 559
q13 3364 3605 2964 2964
q14 309 308 270 270
q15 q16 705 737 641 641
q17 1342 1456 1339 1339
q18 8011 7466 7109 7109
q19 1084 1094 1116 1094
q20 2229 2206 1937 1937
q21 5296 4575 4485 4485
q22 536 446 408 408
Total cold run time: 57732 ms
Total hot run time: 52000 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3413517236
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -290,6 +290,32 @@ Status PipelineFragmentContext::_build_and_prepare_full_pipeline(ThreadPool* thr
RETURN_IF_ERROR(_build_pipelines(_runtime_state->obj_pool(), *_query_ctx->desc_tbl,
&_root_op, root_pipeline));
+ // Propagate _num_instances from LOCAL_EXCHANGE pipelines to ancestor pipelines
+ // that inherited reduced num_tasks from a serial operator.
+ _propagate_local_exchange_num_tasks();
+
+ // Create deferred local exchangers now that all pipelines have final num_tasks.
+ RETURN_IF_ERROR(_create_deferred_local_exchangers());
Review Comment:
是的,这两块不是为了灰度/回滚——是 FE 接管 local exchange 的**当前折中**,不是一步到位。
要让 FE 把"每个算子的并发"也规划好直接下发,本质上需要 FE 自己切 pipeline、自己控制每条 pipeline 的并发(num_tasks 是 pipeline 粒度的,只在 pipeline 边界即 local exchange / pipeline breaker 处变化),BE 就不再规划 pipeline 了。那等于把 BE 的整套 pipeline 构建(`_build_pipeline`、pipeline-breaker 判定、num_tasks 推导、dependency 接线,还要兼顾 spill / runtime filter / shared state)搬到 FE,并改 thrift 契约携带完整 pipeline 结构——战线太长、风险太大。这套 FE 近似等价实现已经做了大半年、近 1 万行代码,没法一把全做。
所以现状是:FE 规划 LE 的**位置/类型/分布**(决定 plan 正确性,过去正是 BE/FE 分歧 bug 的根源),`enable_local_shuffle_planner=true` 时 BE 的 `_plan_local_exchange` 整个跳过;但每条 pipeline 的 num_tasks 仍由 BE 的 `_propagate_local_exchange_num_tasks` 拓扑推导,`_create_deferred_local_exchangers` 因为 exchanger 的 `num_partitions` 要等 num_tasks 定了才能建、所以延后创建。终态是 FE 连 num_tasks 也算好下发、BE 的 propagate 退化成 assert(只校验)——那是后续单独的大改。
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3411827399
##########
gensrc/thrift/Partitions.thrift:
##########
@@ -55,6 +55,40 @@ enum TPartitionType {
MERGE_PARTITIONED = 9
}
+enum TLocalPartitionType {
+ NOOP = 0,
+ // used to resume the global hash distribution because other distribution break the global hash distribution,
+ // such as PASSTHROUGH. and then JoinNode can shuffle data by the same hash distribution.
+ //
+ // for example: look here, need resume to GLOBAL_EXECUTION_HASH_SHUFFLE
+ // ↓
+ // Node -> LocalExchangeNode(PASSTHROUGH) → JoinNode → LocalExchangeNode(GLOBAL_EXECUTION_HASH_SHUFFLE) → JoinNode
+ // ExchangeNode(BROADCAST) ↗ ↑
+ // ExchangeNode(GLOBAL_EXECUTION_HASH_SHUFFLE)
+ GLOBAL_EXECUTION_HASH_SHUFFLE = 1,
+ // used to rebalance data for rebalance data and add parallelism
+ //
+ // for example: look here, need use LOCAL_EXECUTION_HASH_SHUFFLE to rebalance data
+ // ↓
+ // Scan(hash(id)) -> LocalExchangeNode(LOCAL_EXECUTION_HASH_SHUFFLE(id, name)) → AggregationNode(group by(id,name))
Review Comment:
This example is not very rigorous. The `hash(id)` function also ensures that tuples with the same `id` and `name` are assigned to the same instance. It would be more reasonable to change it to "scan(hash(id, name))" and "agg(hash(id))".
##########
gensrc/thrift/Partitions.thrift:
##########
@@ -55,6 +55,40 @@ enum TPartitionType {
MERGE_PARTITIONED = 9
}
+enum TLocalPartitionType {
+ NOOP = 0,
+ // used to resume the global hash distribution because other distribution break the global hash distribution,
+ // such as PASSTHROUGH. and then JoinNode can shuffle data by the same hash distribution.
+ //
+ // for example: look here, need resume to GLOBAL_EXECUTION_HASH_SHUFFLE
+ // ↓
+ // Node -> LocalExchangeNode(PASSTHROUGH) → JoinNode → LocalExchangeNode(GLOBAL_EXECUTION_HASH_SHUFFLE) → JoinNode
+ // ExchangeNode(BROADCAST) ↗ ↑
+ // ExchangeNode(GLOBAL_EXECUTION_HASH_SHUFFLE)
+ GLOBAL_EXECUTION_HASH_SHUFFLE = 1,
+ // used to rebalance data for rebalance data and add parallelism
+ //
+ // for example: look here, need use LOCAL_EXECUTION_HASH_SHUFFLE to rebalance data
+ // ↓
+ // Scan(hash(id)) -> LocalExchangeNode(LOCAL_EXECUTION_HASH_SHUFFLE(id, name)) → AggregationNode(group by(id,name))
+ //
+ // the LOCAL_EXECUTION_HASH_SHUFFLE is necessary because the hash distribution of scan node is based on id,
+ // but the hash distribution of aggregation node is based on id and name, so we need to rebalance data by both
+ // id and name to make sure the data with same id and name can be sent to the same instance of aggregation node.
+ // and we can not use GLOBAL_EXECUTION_HASH_SHUFFLE(id, name) here, because
+ // `TPipelineFragmentParams.shuffle_idx_to_instance_idx` is used to mapping partial global instance index to local
+ // instance index, and discard the other backend's instance index, the data not belong to the local instance will be
+ // discarded, which cause data loss.
+ LOCAL_EXECUTION_HASH_SHUFFLE = 2,
+ BUCKET_HASH_SHUFFLE = 3,
+ // round-robin partition, used to rebalance data for rebalance data and add parallelism
+ PASSTHROUGH = 4,
+ ADAPTIVE_PASSTHROUGH = 5,
+ BROADCAST = 6,
+ PASS_TO_ONE = 7,
+ LOCAL_MERGE_SORT = 8
Review Comment:
all of these enum value should add comment to explain what it is
##########
gensrc/thrift/PlanNodes.thrift:
##########
@@ -1427,6 +1428,24 @@ struct TExchangeNode {
4: optional Partitions.TPartitionType partition_type
}
+struct TLocalExchangeNode {
+ 1: required Partitions.TLocalPartitionType partition_type
Review Comment:
all fields should be optional
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4766920552
# BE Regression && UT Coverage Report
Increment line coverage `90.15% (366/406)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b_merge/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b_merge/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 74.09% (28397/38326) |
| Line Coverage | 58.01% (309698/533900) |
| Region Coverage | 54.80% (259297/473128) |
| Branch Coverage | 56.16% (112650/200588) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4766610456
# BE Regression && UT Coverage Report
Increment line coverage `90.15% (366/406)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b_merge/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b_merge/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 74.15% (28417/38324) |
| Line Coverage | 58.03% (309827/533892) |
| Region Coverage | 54.83% (259396/473126) |
| Branch Coverage | 56.18% (112693/200588) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4680860551
# FE UT Coverage Report
Increment line coverage ` 74.48% (429/576)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_1f614d8cadd1619a8c17dd6f7ec7b12ba6983286/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_1f614d8cadd1619a8c17dd6f7ec7b12ba6983286/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4682363000
# FE Regression Coverage Report
Increment line coverage ` 79.51% (458/576)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_784476ead971eeebeb0b637f6da4a434e380ce36_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_784476ead971eeebeb0b637f6da4a434e380ce36_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4479943101
<details>
<summary>TPC-H: <b>Total hot run time: 31734 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 15d92ba024b265d9441145d7bd26d93590aa9c63, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17803 3914 3926 3914
q2 q3 10929 1434 801 801
q4 4816 481 340 340
q5 10658 2257 2105 2105
q6 397 175 139 139
q7 914 793 617 617
q8 9633 1832 1667 1667
q9 6923 5130 4985 4985
q10 6518 2090 1770 1770
q11 430 268 239 239
q12 668 423 298 298
q13 18212 3499 2774 2774
q14 260 253 239 239
q15 q16 823 779 721 721
q17 918 940 963 940
q18 7080 5849 6162 5849
q19 1241 1333 1062 1062
q20 503 407 258 258
q21 6216 2696 2699 2696
q22 458 385 320 320
Total cold run time: 105400 ms
Total hot run time: 31734 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4514 4779 4621 4621
q2 q3 4801 5222 4621 4621
q4 2181 2213 1441 1441
q5 4809 4669 4682 4669
q6 232 191 134 134
q7 1807 1659 1400 1400
q8 2178 1885 1875 1875
q9 7379 7481 7388 7388
q10 4474 4422 3979 3979
q11 540 377 348 348
q12 725 726 508 508
q13 2994 3423 2767 2767
q14 270 277 253 253
q15 q16 670 702 606 606
q17 1267 1240 1233 1233
q18 7261 6876 6773 6773
q19 1096 1105 1118 1105
q20 2217 2232 1921 1921
q21 5321 4605 4449 4449
q22 523 482 399 399
Total cold run time: 55259 ms
Total hot run time: 50490 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4484994044
<details>
<summary>TPC-H: <b>Total hot run time: 31488 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 4affd222d6550dd103b7829a6fced0b7d45d317a, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17594 3891 3973 3891
q2 q3 10951 1405 844 844
q4 4808 482 346 346
q5 10383 2275 2135 2135
q6 365 183 144 144
q7 936 776 645 645
q8 9783 1852 1610 1610
q9 7005 4945 4940 4940
q10 6473 2240 1807 1807
q11 428 262 242 242
q12 653 436 297 297
q13 18141 3324 2779 2779
q14 270 250 237 237
q15 q16 811 768 721 721
q17 908 941 925 925
q18 7073 5714 5921 5714
q19 1229 1310 1127 1127
q20 519 416 272 272
q21 5733 2807 2498 2498
q22 451 371 314 314
Total cold run time: 104514 ms
Total hot run time: 31488 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4640 4749 4525 4525
q2 q3 4851 5185 4650 4650
q4 2126 2222 1435 1435
q5 4774 4715 4650 4650
q6 224 166 119 119
q7 1673 1601 1396 1396
q8 2223 1908 1912 1908
q9 7411 7410 7408 7408
q10 4480 4414 3997 3997
q11 522 382 348 348
q12 713 720 507 507
q13 3056 3419 2783 2783
q14 282 275 251 251
q15 q16 678 701 610 610
q17 1267 1251 1255 1251
q18 7354 6931 6785 6785
q19 1154 1100 1150 1100
q20 2228 2221 1933 1933
q21 5311 4653 4496 4496
q22 530 453 396 396
Total cold run time: 55497 ms
Total hot run time: 50548 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4499368773
# FE Regression Coverage Report
Increment line coverage ` 80.70% (485/601)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_3ac8525eaac942c0bbeb93cf46d4c95ecf353156_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_3ac8525eaac942c0bbeb93cf46d4c95ecf353156_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3259079337
##########
fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java:
##########
@@ -1598,6 +1600,12 @@ public enum IgnoreSplitType {
"Whether to enable local shuffle on pipelineX engine."}, needForward = true)
private boolean enableLocalShuffle = true;
+ @VarAttrDef.VarAttr(
+ name = ENABLE_LOCAL_SHUFFLE_PLANNER, fuzzy = false, varType = VariableAnnotation.EXPERIMENTAL,
+ description = {"是否在FE规划Local Shuffle",
+ "Whether to plan local shuffle in frontend"}, needForward = true)
+ private boolean enableLocalShufflePlanner = true;
+
Review Comment:
This default enables the FE planner to serialize the new `LOCAL_EXCHANGE_NODE`/`TLocalPartitionType` protocol to every BE as soon as the FE is upgraded. During a rolling upgrade, an old BE does not have `TPlanNodeType::LOCAL_EXCHANGE_NODE` handling in `_create_operator` and will reject/fail such fragments, while `RuntimeState::plan_local_shuffle()` also disables the legacy BE planner because `enable_local_shuffle_planner` is set. Please gate this path on BE capability/version (or keep the default off until all BEs support the new node) so mixed FE/BE deployments continue to execute queries during upgrade.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -933,4 +1020,229 @@ private String mergeIcebergAccessPathsWithId(
}
return StringUtils.join(mergeDisplayAccessPaths, ", ");
}
+
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ ArrayList<PlanNode> newChildren = Lists.newArrayList();
+ for (int i = 0; i < children.size(); i++) {
+ Pair<PlanNode, LocalExchangeType> childOutput
+ = enforceRequire(translatorContext, children.get(i), i, LocalExchangeTypeRequire.noRequire());
+ newChildren.add(childOutput.first);
+ }
+ this.children = newChildren;
+ return Pair.of(this, LocalExchangeType.NOOP);
+ }
+
+ /**
+ * Unified framework method: propagate serial flag → recurse child → satisfy check → Layer 1 skip → insert LE.
+ * Replaces the old enforceChild/enforceChildExchange/forceEnforceChildExchange trio.
+ *
+ * <h3>Data flow</h3>
+ * <ul>
+ * <li><b>serial-ancestor flag</b> ({@link PlanTranslatorContext#hasSerialAncestorInPipeline})
+ * — flows root → leaf during traversal. Mirrors BE's
+ * {@code any_of(operators[idx..end], is_serial_operator)} check used by
+ * {@code _add_local_exchange} to skip LE insertion when an ancestor in the same
+ * pipeline is already serial. Reset at pipeline boundaries via
+ * {@link #shouldResetSerialFlagForChild}.</li>
+ * <li><b>shuffle-for-correctness flag</b>
+ * ({@link PlanTranslatorContext#hasShuffleForCorrectnessAncestor}) — also flows
+ * root → leaf. Mirrors BE's {@code _followed_by_shuffled_operator}: tells a
+ * child whether some downstream operator depends on hash distribution for
+ * correctness, so {@code SetOperationNode} can pre-shuffle union branches.</li>
+ * <li><b>return value</b> {@code Pair<PlanNode, LocalExchangeType>} — first is the
+ * (possibly LE-wrapped) child; second is the actual output distribution as
+ * observed by the parent. Caller's {@code require.satisfy(output)} decides
+ * whether more LE is needed.</li>
+ * <li><b>parent.require</b> describes the constraint on the child output —
+ * computed inside the parent's {@code enforceAndDeriveLocalExchange} per child.</li>
+ * </ul>
+ *
+ * <h3>Invariants</h3>
+ * <ul>
+ * <li>Every serial → non-serial transition has an LE somewhere between them
+ * (enforced by framework step 3 below, validated by
+ * {@link AddLocalExchange#validateNoSerialWithoutLocalExchange}).</li>
+ * <li>{@code LocalExchangeNode} itself is always non-serial — setting it serial
+ * would defeat its purpose of fanning a 1-task pipeline back to N tasks.</li>
+ * <li>For pipeline-breaking parents ({@code shouldResetSerialFlagForChild=true}),
+ * the child starts a fresh pipeline so {@code hasSerialAncestor} is reset; the
+ * node's own {@code isSerialNode()} still composes in for the child's view.</li>
+ * <li>{@code RequireHash} accepts any hash flavour; {@code RequireSpecific} demands
+ * an exact match (with the one PASSTHROUGH/ADAPTIVE_PASSTHROUGH compatibility).
+ * Pick the looser one whenever correctness allows — see
+ * {@link LocalExchangeNode.LocalExchangeTypeRequire}.</li>
+ * </ul>
+ *
+ * <h3>Layers</h3>
+ * Layer 1 (shouldSkipLE): mirrors BE's need_to_local_exchange — skip when this node or
+ * an ancestor in the same pipeline is serial (operators[idx..end] has serial → skip).
+ * Layer 2 (require/output): each Node declares require and output in enforceAndDeriveLocalExchange.
+ */
+ protected Pair<PlanNode, LocalExchangeType> enforceRequire(
+ PlanTranslatorContext translatorContext, PlanNode child, int childIndex,
+ LocalExchangeTypeRequire require) {
+ // 1. Propagate serial-ancestor flag to child.
+ // For pipeline-splitting operators (shouldReset=true, e.g. non-streaming AGG):
+ // Drop inherited serial flag from parent (parent is in a different pipeline),
+ // but keep this node's own serial status (child is in the same pipeline as this
+ // node's sink, e.g. Exchange is in AGG_Sink pipeline).
+ // For non-splitting operators (shouldReset=false, e.g. streaming AGG):
+ // Inherit parent's serial flag + this node's own.
+ boolean inheritedSerial = shouldResetSerialFlagForChild(childIndex)
+ ? false : translatorContext.hasSerialAncestorInPipeline(this);
+ boolean childHasSerialAncestor = inheritedSerial || isSerialNode();
+ translatorContext.setHasSerialAncestorInPipeline(child, childHasSerialAncestor);
+
+ // 1b. Propagate shuffle-for-correctness-ancestor flag to child.
+ // Mirrors BE's _followed_by_shuffled_operator: a downstream operator needs hash
+ // distribution for correctness, and the chain to here goes through HASH or NOOP
+ // requirements (so the dependency is preserved).
+ // propagate = ((inheritedShuffled || self.requiresShuffleForCorrectness)
+ // && require is hash)
+ // || (inheritedShuffled && require is noop/passthrough)
+ boolean inheritedShuffled = translatorContext.hasShuffleForCorrectnessAncestor(this);
+ boolean selfOrInheritedShuffled = inheritedShuffled || requiresShuffleForCorrectness();
+ boolean requireIsHash = require.preferType().isHashShuffle();
+ boolean requireIsNoop = require.preferType() == LocalExchangeNode.LocalExchangeType.NOOP;
+ boolean childShuffledAncestor = (selfOrInheritedShuffled && requireIsHash)
+ || (inheritedShuffled && requireIsNoop);
+ translatorContext.setHasShuffleForCorrectnessAncestor(child, childShuffledAncestor);
+
+ // 2. Recurse child (Layer 2: child declares its own require/output)
+ Pair<PlanNode, LocalExchangeType> childOutput =
+ child.enforceAndDeriveLocalExchange(translatorContext, this, require);
+
+ // Steps 2.5 and 3 both react to a serial child but address different concerns:
+ // - Step 2.5 rewrites the OUTPUT-side view (what we tell satisfy/parent about
+ // the child's actual distribution). A serial pipeline runs with 1 task so
+ // its distribution claim is meaningless — flatten to NOOP so the satisfy
+ // check below doesn't get fooled by a stale "I output BUCKET_HASH" claim.
+ // - Step 3 rewrites the REQUIRE-side decision (what we want from the child).
+ // If we previously asked for nothing (noRequire) but the child turns out
+ // to be serial and we're not, upgrade to requirePassthrough so an LE is
+ // inserted to restore parallelism.
+
+ // 2.5. Serial child override (output side): if child is serial on BE, force its
+ // reported output to NOOP. Distribution is irrelevant when the child runs
+ // with 1 task; downstream parallelism is restored either by step 3 (LE
+ // insertion) or skipped entirely by step 4b (we're also serial).
+ if (childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ childOutput = Pair.of(childOutput.first, LocalExchangeType.NOOP);
+ }
+
+ // 3. Framework-level serial child check (require side, mirrors BE base class
+ // required_data_distribution): if child will be serial on BE but this node is
+ // not serial, the pipeline has a 1-task serial child feeding an N-task non-serial
+ // parent. Without LE, pipeline splits (AGG/JOIN) create paired pipelines with
+ // mismatched num_tasks → crash. Upgrade noRequire to requirePassthrough so an
+ // LE is inserted below to restore parallelism.
+ if (require instanceof LocalExchangeNode.NoRequire
+ && childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())
+ && !isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ require = LocalExchangeTypeRequire.requirePassthrough();
+ }
+
+ // 4. Satisfy check: child output meets requirement → done
+ if (require.satisfy(childOutput.second)) {
+ return childOutput;
+ }
+
+ // 4. Layer 1: skip LE when serial operator or ancestor in same pipeline
+ // Equivalent to BE's need_to_local_exchange: any_of(operators[idx..end], is_serial) → skip
+ if (translatorContext.hasSerialAncestorInPipeline(this) || isSerialNode()) {
+ return childOutput;
Review Comment:
This skip uses `isSerialNode()` even though the comment above `isSerialOperatorOnBe()` says an `isSerialNode()` only actually runs with one BE task when `fragment.useSerialSource(context)` is true. For fragments where `useSerialSource` is false (for example `ignore_storage_data_distribution=false`, query cache, or NAAJ), a node such as a scalar aggregate or unpartitioned exchange can still return `isSerialNode()==true` but BE will execute it with normal parallelism (`is_serial_operator=false` in thrift). In that case this branch skips a required LocalExchange even though BE would not consider the ancestor serial, so downstream hash/passthrough requirements can be silently dropped. The serial-ancestor propagation at line 1094 has the same issue. Please base these planner decisions on `isSerialOperatorOnBe(translatorContext.getConnectContext())`, not the syntactic `isSerialNode()`, except for the explicitly documented heavy-op/local-fragment cases.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4508171612
# FE UT Coverage Report
Increment line coverage ` 73.97% (449/607)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_846d365a88901fa32fdf44b42b6cd04b900da259/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_846d365a88901fa32fdf44b42b6cd04b900da259/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4504902749
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/f2d43a29db5822a72153fd88cbcd8bead33cbc6d_f2d43a29db5822a72153fd88cbcd8bead33cbc6d_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/f2d43a29db5822a72153fd88cbcd8bead33cbc6d_f2d43a29db5822a72153fd88cbcd8bead33cbc6d_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 78.06% (1854/2375) |
| Line Coverage | 64.53% (33336/51662) |
| Region Coverage | 65.23% (16531/25341) |
| Branch Coverage | 55.73% (8834/15852) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4480118080
# BE Regression && UT Coverage Report
Increment line coverage `72.45% (305/421)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_15d92ba024b265d9441145d7bd26d93590aa9c63_merge/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_15d92ba024b265d9441145d7bd26d93590aa9c63_merge/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 63.48% (23997/37804) |
| Line Coverage | 47.20% (247315/523994) |
| Region Coverage | 44.16% (203214/460125) |
| Branch Coverage | 45.44% (87932/193505) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4498830486
# BE UT Coverage Report
Increment line coverage `11.11% (46/414)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/3ac8525eaac942c0bbeb93cf46d4c95ecf353156_3ac8525eaac942c0bbeb93cf46d4c95ecf353156/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/3ac8525eaac942c0bbeb93cf46d4c95ecf353156_3ac8525eaac942c0bbeb93cf46d4c95ecf353156/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 53.56% (20747/38735) |
| Line Coverage | 37.18% (196268/527904) |
| Region Coverage | 33.48% (153729/459147) |
| Branch Coverage | 34.52% (67039/194213) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4486958816
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4497974447
# FE UT Coverage Report
Increment line coverage ` 74.54% (448/601)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_3ac8525eaac942c0bbeb93cf46d4c95ecf353156/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_3ac8525eaac942c0bbeb93cf46d4c95ecf353156/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3412495881
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java:
##########
@@ -3314,7 +3349,7 @@ private boolean findOlapScanNodesByPassExchangeNode(PlanNode root) {
return false;
}
- private List<List<Expr>> getDistributeExprs(Plan ... children) {
+ private List<List<Expr>> getChildrenDistributeExprs(Plan ... children) {
Review Comment:
this function's name is not very suitable. If you want to obtain the DistributeExprs of children, the input should be the parent node rather than the children.
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java:
##########
@@ -634,7 +636,8 @@ protected void splitFragments(PhysicalPlan resultPlan) {
return;
}
- PlanTranslatorContext planTranslatorContext = new PlanTranslatorContext(cascadesContext);
+ this.planTranslatorContext = new PlanTranslatorContext(cascadesContext);
+ PlanTranslatorContext planTranslatorContext = this.planTranslatorContext;
Review Comment:
useless local variable
##########
fe/fe-core/src/main/java/org/apache/doris/qe/runtime/ThriftPlansBuilder.java:
##########
@@ -277,7 +277,8 @@ private static Multiset<DistributedPlanWorker> computeInstanceNumPerWorker(
return workerCounter;
}
- private static Map<Integer, Integer> computeExchangeSenderNum(PipelineDistributedPlan distributedPlan) {
+ private static Map<Integer, Integer> computeExchangeSenderNum(
+ PipelineDistributedPlan distributedPlan) {
Review Comment:
why change this?
##########
fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java:
##########
@@ -1130,8 +1130,10 @@ public boolean isProfileSafeStmt() {
// 1. CreateTableCommand(mainly for create as select).
// 2. LoadCommand.
// 3. InsertOverwriteTableCommand.
+ // 4. MergeIntoCommand (merge into ... using ...).
if ((plan instanceof Command) && !(plan instanceof LoadCommand)
- && !(plan instanceof CreateTableCommand) && !(plan instanceof InsertOverwriteTableCommand)) {
+ && !(plan instanceof CreateTableCommand) && !(plan instanceof InsertOverwriteTableCommand)
+ && !(plan instanceof org.apache.doris.nereids.trees.plans.commands.merge.MergeIntoCommand)) {
Review Comment:
use import, not use qualified name
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java:
##########
@@ -740,6 +744,18 @@ protected void distribute(PhysicalPlan physicalPlan, ExplainLevel explainLevel)
splitFragments(physicalPlan);
doDistribute(canUseNereidsDistributePlanner, explainLevel);
+
+ SessionVariable sessionVariable = cascadesContext.getConnectContext().getSessionVariable();
+ if (sessionVariable.isEnableLocalShufflePlanner() && sessionVariable.isEnableLocalShuffle()) {
+ addLocalExchangeAfterDistribute();
+ }
Review Comment:
the if jugdement should move into `addLocalExchangeAfterDistribute`
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java:
##########
@@ -657,7 +660,8 @@ protected void splitFragments(PhysicalPlan resultPlan) {
scanNodeList.addAll(planTranslatorContext.getScanNodes());
physicalRelations.addAll(planTranslatorContext.getPhysicalRelations());
descTable = planTranslatorContext.getDescTable();
- fragments = new ArrayList<>(planTranslatorContext.getPlanFragments());
+ List<PlanFragment> planFragments = planTranslatorContext.getPlanFragments();
Review Comment:
useless local variable
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -142,7 +145,8 @@ public abstract class PlanNode extends TreeNode<PlanNode> {
protected int nereidsId = -1;
- private List<List<Expr>> childrenDistributeExprLists = new ArrayList<>();
+ protected List<List<Expr>> childrenDistributeExprLists = new ArrayList<>();
+ protected List<Expr> distributeExprLists = new ArrayList<>();
Review Comment:
add comment to these two variables
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3411859421
##########
be/src/exec/exchange/local_exchange_sink_operator.cpp:
##########
@@ -37,24 +37,30 @@ std::vector<Dependency*> LocalExchangeSinkLocalState::dependencies() const {
return deps;
}
-Status LocalExchangeSinkOperatorX::init(RuntimeState* state, ExchangeType type,
- const int num_buckets, const bool use_global_hash_shuffle,
+Status LocalExchangeSinkOperatorX::init(RuntimeState* state, TLocalPartitionType::type type,
+ const int num_buckets,
const std::map<int, int>& shuffle_idx_to_instance_idx) {
+ DCHECK(!_planned_by_fe);
_name = "LOCAL_EXCHANGE_SINK_OPERATOR(" + get_exchange_type_name(type) + ")";
_type = type;
- if (_type == ExchangeType::HASH_SHUFFLE) {
- _shuffle_idx_to_instance_idx.clear();
- _use_global_shuffle = use_global_hash_shuffle;
+ if (_type == TLocalPartitionType::GLOBAL_EXECUTION_HASH_SHUFFLE) {
// For shuffle join, if data distribution has been broken by previous operator, we
// should use a HASH_SHUFFLE local exchanger to shuffle data again. To be mentioned,
// we should use map shuffle idx to instance idx because all instances will be
// distributed to all BEs. Otherwise, we should use shuffle idx directly.
- if (use_global_hash_shuffle) {
- _shuffle_idx_to_instance_idx = shuffle_idx_to_instance_idx;
+ _shuffle_idx_to_instance_idx = shuffle_idx_to_instance_idx;
+ if (state->query_options().__isset.enable_new_shuffle_hash_method &&
Review Comment:
已抽成公共的 `_create_partitioner(state, bucket_count)`,现在 BE-native 路径的 `init()`(`local_exchange_sink_operator.cpp:77`) 和 FE-planned 路径的 `init_partitioner()`(:84) 都复用它,去掉了三段重复的 hash-partitioner 创建代码。
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -290,6 +290,32 @@ Status PipelineFragmentContext::_build_and_prepare_full_pipeline(ThreadPool* thr
RETURN_IF_ERROR(_build_pipelines(_runtime_state->obj_pool(), *_query_ctx->desc_tbl,
&_root_op, root_pipeline));
+ // Propagate _num_instances from LOCAL_EXCHANGE pipelines to ancestor pipelines
+ // that inherited reduced num_tasks from a serial operator.
+ _propagate_local_exchange_num_tasks();
+
+ // Create deferred local exchangers now that all pipelines have final num_tasks.
+ RETURN_IF_ERROR(_create_deferred_local_exchangers());
Review Comment:
是的,方向一致。`enable_local_shuffle_planner=true` 时 BE 的 `_plan_local_exchange` 被 `RuntimeState::plan_local_shuffle()` 整个跳过,BE 完全按 FE 规划好的 `LOCAL_EXCHANGE_NODE` 来建 pipeline,不再自己算 local exchange。
目前仍保留 BE 的 `required_data_distribution()` / 并行度计算,只是给 `enable_local_shuffle_planner=false` 的 BE-native 回退路径用(保证和老行为逐字节一致,方便灰度 / 回滚)。FE-planned 路径不读它。
长期我也认同应该让 BE 这边退化成「只校验不计算」——FE 既然已经插好了正确的 LE,BE 的 propagate 理想情况下应该是一个 assert("FE 给的分布已满足要求"),而不是再算一遍。这个 PR 先保持两条路径并存、默认走 FE。
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AddLocalExchange.java:
##########
@@ -0,0 +1,182 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.planner;
+
+import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.glue.translator.PlanTranslatorContext;
+import org.apache.doris.nereids.trees.plans.distribute.DistributedPlan;
+import org.apache.doris.nereids.trees.plans.distribute.FragmentIdMapping;
+import org.apache.doris.nereids.trees.plans.distribute.PipelineDistributedPlan;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeType;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeTypeRequire;
+import org.apache.doris.planner.LocalExchangeNode.RequireHash;
+
+/**
+ * FE-side local exchange planner — inserts {@link LocalExchangeNode} into each fragment's
+ * plan tree so that within-fragment data redistribution is decided at planning time
+ * instead of at BE pipeline-build time.
+ *
+ * <h3>When this runs</h3>
+ * Invoked from {@code NereidsPlanner.addLocalExchangeAfterDistribute()} right after
+ * {@code DistributePlanner} has assigned instances to fragments and before the plan is
+ * serialized to BE. Gated by session variable {@code enable_local_shuffle_planner}
+ * (default true) and {@code enable_local_shuffle}; when either is off this pass is
+ * skipped entirely and BE falls back to its own {@code _plan_local_exchange}. The two
+ * paths are mutually exclusive: BE consults {@code runtime_state.h::plan_local_shuffle()}
+ * to know whether it should plan LE itself.
+ *
+ * <h3>What it changes</h3>
+ * <ul>
+ * <li>For each fragment with {@code maxPerBeInstances > 1}, walks the plan tree
+ * bottom-up via {@link PlanNode#enforceAndDeriveLocalExchange} and inserts
+ * LocalExchangeNodes where children's output distribution doesn't satisfy the
+ * parent's requirement.</li>
+ * <li>May wrap the fragment root with an extra PASSTHROUGH LE so the data sink
+ * (DataStreamSink / OlapTableSink) runs with the full instance count even when
+ * the root operator is serial — see {@link #addLocalExchangeForFragment}.</li>
+ * <li>Does NOT modify the fragment sink itself, fragment boundaries, or instance
+ * assignment.</li>
+ * </ul>
+ *
+ * <h3>Per-BE instance semantics</h3>
+ * Skips fragments where every BE has at most 1 instance. Using a global instance count
+ * would insert LE for "2 BEs × 1 instance" cases, which BE's own
+ * {@code _plan_local_exchange} would not — leading to pipeline task-count mismatch and
+ * deadlock. See {@link #addLocalExchange}.
+ *
+ * <h3>Reading order</h3>
+ * Start with {@link PlanNode#enforceRequire} (the recursion engine), then individual
+ * {@code enforceAndDeriveLocalExchange} overrides on PlanNode subclasses.
+ */
+public class AddLocalExchange {
+ /** addLocalExchange with distributed plans, skipping single-instance fragments.
+ * BE's _plan_local_exchange checks _num_instances which is the per-BE instance count.
+ * With _num_instances<=1 all pipelines on that BE have 1 task so local exchange is a no-op.
+ * We must use the same per-BE semantics: skip when every BE has at most 1 instance.
+ * Using global instanceCount would insert LE for fragments where 2 BEs each have 1 instance
+ * (global=2, per-BE=1), causing pipeline task mismatch and deadlock. */
+ public void addLocalExchange(FragmentIdMapping<DistributedPlan> distributedPlans,
+ PlanTranslatorContext context) {
+ for (DistributedPlan plan : distributedPlans.values()) {
+ PipelineDistributedPlan pipePlan = (PipelineDistributedPlan) plan;
+ long maxPerBeInstances = pipePlan.getInstanceJobs().stream()
+ .collect(java.util.stream.Collectors.groupingBy(
+ j -> j.getAssignedWorker().id(), java.util.stream.Collectors.counting()))
+ .values().stream().mapToLong(Long::longValue).max().orElse(0);
+ if (maxPerBeInstances <= 1) {
+ continue;
+ }
+ PlanFragment fragment = pipePlan.getFragmentJob().getFragment();
+ addLocalExchangeForFragment(fragment, context);
+ }
+ }
+
+ private void addLocalExchangeForFragment(PlanFragment fragment, PlanTranslatorContext context) {
+ DataSink sink = fragment.getSink();
+ LocalExchangeTypeRequire require = sink == null
+ ? LocalExchangeTypeRequire.noRequire() : sink.getLocalExchangeTypeRequire();
+ PlanNode root = fragment.getPlanRoot();
+ context.setHasSerialAncestorInPipeline(root, false);
+ Pair<PlanNode, LocalExchangeType> output = root
+ .enforceAndDeriveLocalExchange(context, null, require);
+ PlanNode newRoot = output.first;
+ // The fragment data sink (DataStreamSink, OlapTableSink) runs in the same pipeline
+ // as the root. If the root will be serial on BE, the sink pipeline has 1 task —
+ // only instance 0 sends data, others hang or miss writes.
+ // Insert PASSTHROUGH fan-out so sink runs with _num_instances tasks.
+ // This matches BE-native's default required_data_distribution():
+ // _child->is_serial_operator() ? PASSTHROUGH : NOOP
+ if (newRoot.isSerialOperatorOnBe(context.getConnectContext())) {
+ newRoot = new LocalExchangeNode(context.nextPlanNodeId(), newRoot,
+ LocalExchangeType.PASSTHROUGH, null);
+ }
+ if (newRoot != root) {
+ fragment.setPlanRoot(newRoot);
+ }
+ validateNoSerialWithoutLocalExchange(fragment.getPlanRoot(), context.getConnectContext());
+ }
+
+ /**
+ * In a local-shuffle fragment, the root check above guarantees the root pipeline
+ * has N tasks. Any serial operator reduces its pipeline to 1 task. If this serial
+ * operator feeds into a non-serial parent without LocalExchangeNode in between,
+ * some pipelines have 1 task while others have N → shared_state mismatch, data loss.
+ *
+ * Serial→serial chains are fine (all at 1 task, consistent). Only the transition
+ * from serial to non-serial needs LE to restore parallelism.
+ */
+ private void validateNoSerialWithoutLocalExchange(PlanNode node,
+ org.apache.doris.qe.ConnectContext context) {
+ for (PlanNode child : node.getChildren()) {
+ validateNoSerialWithoutLocalExchange(child, context);
+ if (child.isSerialOperatorOnBe(context)
+ && !(child instanceof LocalExchangeNode)
+ && !(node instanceof LocalExchangeNode)
+ && !(node instanceof ExchangeNode)
+ && !node.isSerialOperatorOnBe(context)) {
+ org.apache.logging.log4j.LogManager.getLogger(AddLocalExchange.class).warn(
Review Comment:
Removed this validation entirely (commit `3630071a131`) — after the CI evidence, neither `throw` nor `warn` is worth keeping:
1. **It reads the same `isSerialOperatorOnBe` flag it would validate**, so it is blind to the one bug class that actually matters — a *wrong* serial flag. The framework and the check would agree on the same wrong answer; a check can't validate an input it shares with the thing it's checking.
2. **It fires on legitimate plans.** Pushing it as a `throw` surfaced 9 distinct legitimate P0/cloud_p0 patterns where it fires but the plan is correct: serial `UnionNode → TableFunctionNode/AggregationNode`, serial `RecursiveCteScanNode → NLJ/Agg`, serial `OlapScanNode → HashJoinNode`. In all of these the parent runs effectively serial at runtime and BE reconciles `num_tasks` across the coupled pipelines, so no LE is required and results are correct.
3. **The one class it *could* catch** — a `require`→LE logic regression — is already caught end-to-end by the regression suite / RQG.
Correctness still comes from `enforceRequire` inserting the LEs, validated by `test_local_shuffle_rqg_bugs` + the RQG differential against the BE-native baseline. The principled direction is a single source of truth for the serial/distribution decision (BE reading the FE-written flag), so there is no FE-prediction-vs-BE-reality gap to police in the first place.
##########
regression-test/suites/nereids_p0/local_shuffle/test_local_shuffle_rqg_bugs.groovy:
##########
@@ -0,0 +1,1557 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * Regression tests for bugs discovered by RQG testing on the local-exchange2 branch.
+ *
+ * These queries triggered "must set shared state" errors or incorrect results
+ * in RQG build 183992. Common conditions:
+ * - use_serial_exchange=true (makes ALL Exchanges serial, not just UNPARTITIONED)
+ * - enable_local_shuffle_planner=true (FE-planned local exchange)
+ * - parallel_pipeline_task_num > 1
+ *
+ * Error types reproduced:
+ * 1. must set shared state, in AGGREGATION_OPERATOR
+ * 2. must set shared state, in SORT_OPERATOR
+ * 3. incorrect results with GROUPING SETS + scalar subquery + window function
+ */
+suite("test_local_shuffle_rqg_bugs") {
+
+ // ============================================================
+ // Table setup — mirrors RQG table structure
+ // 10 buckets to match RQG (replication_num=1 for single-BE testing)
+ // ============================================================
+ sql "DROP TABLE IF EXISTS rqg_t1"
+ sql "DROP TABLE IF EXISTS rqg_t2"
+ sql "DROP TABLE IF EXISTS rqg_t3"
+ sql "DROP TABLE IF EXISTS rqg_t4"
+
+ sql """
+ CREATE TABLE rqg_t1 (
+ pk INT NOT NULL,
+ col_int_undef_signed INT,
+ col_int_undef_signed2 INT,
+ col_int_undef_signed_not_null INT NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ CREATE TABLE rqg_t2 (
+ pk INT NOT NULL,
+ col_int_undef_signed INT,
+ col_int_undef_signed2 INT,
+ col_bigint_undef_signed_not_null BIGINT NOT NULL,
+ col_decimal_38_10__undef_signed_not_null DECIMAL(38,10) NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ // Table for build 184181 GLOBAL_HASH_SHUFFLE bugs — needs varchar + bigint columns
+ sql """
+ CREATE TABLE rqg_t3 (
+ pk INT NOT NULL,
+ col_bigint_undef_signed BIGINT,
+ col_varchar_10__undef_signed VARCHAR(10),
+ col_varchar_64__undef_signed VARCHAR(64)
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ // Second table for FULL OUTER JOIN case (col_bigint_undef_signed_not_null)
+ sql """
+ CREATE TABLE rqg_t4 (
+ pk INT NOT NULL,
+ col_bigint_undef_signed_not_null BIGINT NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ INSERT INTO rqg_t3 VALUES
+ (0, -94, 'Abc', 'hello world'),
+ (1, 672609, 'Xyz', null),
+ (2, -3766684, 'Pqr', 'test string'),
+ (3, 5070261, 'abc', 'another row'),
+ (4, null, 'def', 'value four'),
+ (5, -86, 'XgpxlHBLEM', null),
+ (6, 21910, 'abc', 'they'),
+ (7, -63, 'zzzz', 'some text'),
+ (8, -8276281, 'AHlvNtoGLO', 'longer string here'),
+ (9, -101, 'mid', 'final row')
+ """
+
+ sql """
+ INSERT INTO rqg_t4 VALUES
+ (0, 0), (1, 1), (2, 2), (3, 3), (4, 4),
+ (5, 5), (6, 6), (7, 7), (8, 8), (9, 9),
+ (10, 2), (11, 2), (12, 2), (13, 3), (14, 4),
+ (15, 5), (16, 2), (17, 2), (18, 2), (19, 9)
+ """
+
+ // Insert enough rows to exercise multiple pipeline tasks
+ sql """
+ INSERT INTO rqg_t1 VALUES
+ (0, 0, 10, 0), (1, 1, 11, 1), (2, 2, 12, 2), (3, 3, 13, 3),
+ (4, 4, 14, 4), (5, 5, 15, 5), (6, 6, 16, 6), (7, 7, 17, 7),
+ (8, 8, 18, 8), (9, 9, 19, 9), (10, 0, 20, 10), (11, 1, 21, 11),
+ (12, 2, 22, 12), (13, 3, 23, 13), (14, 4, 24, 14), (15, 5, 25, 15),
+ (16, 6, 26, 16), (17, 7, 27, 17), (18, 8, 28, 18), (19, 9, 29, 19)
+ """
+
+ sql """
+ INSERT INTO rqg_t2 VALUES
+ (0, 0, 10, 100, 1.5), (1, 1, 11, 101, 2.5), (2, 2, 12, 102, 3.5),
+ (3, 3, 13, 103, 4.5), (4, 4, 14, 104, 5.5), (5, 5, 15, 105, 6.5),
+ (6, 6, 16, 106, 7.5), (7, 7, 17, 107, 8.5), (8, 8, 18, 108, 9.5),
+ (9, 9, 19, 109, 10.5), (10, 0, 20, 110, 11.5), (11, 1, 21, 111, 12.5),
+ (12, 2, 22, 112, 13.5), (13, 3, 23, 113, 14.5), (14, 4, 24, 114, 15.5),
+ (15, 5, 25, 115, 16.5), (16, 6, 26, 116, 17.5), (17, 7, 27, 117, 18.5),
+ (18, 8, 28, 118, 19.5), (19, 9, 29, 119, 20.5)
+ """
+
+ // Wait for data to be visible
+ Thread.sleep(5000)
+
+ // ============================================================
+ // Common settings
+ // ============================================================
+ sql "SET enable_nereids_planner=true"
+ sql "SET enable_fallback_to_original_planner=false"
+ sql "SET runtime_filter_mode=off"
+ sql "SET enable_profile=true"
+ sql "SET enable_sql_cache=false"
+ sql "SET enable_local_shuffle=true"
+
+ // ============================================================
+ // Bug 1: must set shared state, in AGGREGATION_OPERATOR
+ // RQG case: eliminate_group_by_uniform.case_id_11007680713
+ // Key conditions: use_serial_exchange=true, parallel_pipeline_task_num=3
+ // SQL: EXCEPT with count(*) GROUP BY on both sides
+ // ============================================================
+
+ // Test with FE planner (the buggy path)
+ logger.info("=== Bug 1a: AGG shared state - EXCEPT with serial exchange (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=3,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_common_expr_pushdown=false,
+ disable_streaming_preaggregations=true)*/
+ col_int_undef_signed_not_null as col1,
+ col_int_undef_signed_not_null as col2,
+ 0 as col3, count(1)
+ FROM rqg_t1
+ GROUP BY col1, col2, col3
+ EXCEPT
+ SELECT col_bigint_undef_signed_not_null as col1,
+ col_decimal_38_10__undef_signed_not_null as col2,
+ 5 as col3, count(1)
+ FROM rqg_t2
+ GROUP BY col1, col2, col3
+ """
+ logger.info("Bug 1a: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 1a FAILED: ${t.message}")
+ assertTrue(false, "Bug 1a: must set shared state in AGGREGATION_OPERATOR: ${t.message}")
+ }
+
+ // Compare with BE native planner
+ logger.info("=== Bug 1b: AGG shared state - EXCEPT with serial exchange (BE native) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=3,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_common_expr_pushdown=false,
+ disable_streaming_preaggregations=true)*/
+ col_int_undef_signed_not_null as col1,
+ col_int_undef_signed_not_null as col2,
+ 0 as col3, count(1)
+ FROM rqg_t1
+ GROUP BY col1, col2, col3
+ EXCEPT
+ SELECT col_bigint_undef_signed_not_null as col1,
+ col_decimal_38_10__undef_signed_not_null as col2,
+ 5 as col3, count(1)
+ FROM rqg_t2
+ GROUP BY col1, col2, col3
+ """
+ logger.info("Bug 1b: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 1b FAILED: ${t.message}")
+ assertTrue(false, "Bug 1b: BE native also fails: ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 2: must set shared state, in SORT_OPERATOR
+ // RQG case: grouping_set.case_id_5308471751
+ // Key conditions: use_serial_exchange=true, parallel_pipeline_task_num=5
+ // SQL: GROUPING SETS + window function (PERCENT_RANK)
+ // ============================================================
+
+ logger.info("=== Bug 2a: SORT shared state - GROUPING SETS + window (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=5,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true)*/
+ SUM(PERCENT_RANK() OVER (PARTITION BY col_int_undef_signed2 ORDER BY col_int_undef_signed2))
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2),(pk, pk),(col_int_undef_signed))
+ """
+ logger.info("Bug 2a: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 2a FAILED: ${t.message}")
+ assertTrue(false, "Bug 2a: must set shared state in SORT_OPERATOR: ${t.message}")
+ }
+
+ logger.info("=== Bug 2b: SORT shared state - GROUPING SETS + window (BE native) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=5,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true)*/
+ SUM(PERCENT_RANK() OVER (PARTITION BY col_int_undef_signed2 ORDER BY col_int_undef_signed2))
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2),(pk, pk),(col_int_undef_signed))
+ """
+ logger.info("Bug 2b: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 2b FAILED: ${t.message}")
+ assertTrue(false, "Bug 2b: BE native also fails: ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 3: incorrect results with GROUPING SETS + scalar subquery + window
+ // RQG case: grouping_set.case_id_5694495756
+ // Key conditions: parallel_pipeline_task_num=2, disable_streaming_preaggregations=true
+ // Expected: all rows same value; Actual: values split proportionally (1/3, 2/3)
+ // ============================================================
+
+ logger.info("=== Bug 3: incorrect results - GROUPING SETS + subquery + window ===")
+ // FE planner
+ def result_fe = sql """
+ SELECT /*+SET_VAR(parallel_pipeline_task_num=2,
+ enable_local_shuffle_planner=true,
+ disable_streaming_preaggregations=true,
+ enable_share_hash_table_for_broadcast_join=true)*/
+ SUM((SELECT MAX(col_int_undef_signed2) FROM rqg_t1))
+ OVER (PARTITION BY pk ORDER BY pk)
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2, pk),(pk), (pk))
+ """
+ // BE native
+ def result_be = sql """
+ SELECT /*+SET_VAR(parallel_pipeline_task_num=2,
+ enable_local_shuffle_planner=false,
+ disable_streaming_preaggregations=true,
+ enable_share_hash_table_for_broadcast_join=true)*/
+ SUM((SELECT MAX(col_int_undef_signed2) FROM rqg_t1))
+ OVER (PARTITION BY pk ORDER BY pk)
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2, pk),(pk), (pk))
+ """
+ logger.info("Bug 3 FE result rows: ${result_fe.size()}, first few: ${result_fe.take(5)}")
+ logger.info("Bug 3 BE result rows: ${result_be.size()}, first few: ${result_be.take(5)}")
+
+ // All values in both should be the same
+ if (result_fe.size() != result_be.size()) {
+ logger.warn("Bug 3: row count mismatch FE=${result_fe.size()} vs BE=${result_be.size()}")
Review Comment:
Done. Bug 3 now asserts FE==BE (row count + order-insensitive content); Bug 4/5 raise `assertTrue(false)` on an unexpected exception instead of only logging (Bug 1/2/6/7/8 already asserted in their `catch`). The Bug 21 `catch` that swallowed `query timeout` (flagged separately by the bot) was also removed, so a hang fails the suite too.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/HashJoinNode.java:
##########
@@ -285,4 +302,71 @@ public List<Expr> getOtherJoinConjuncts() {
public List<Expr> getMarkJoinConjuncts() {
return markJoinConjuncts;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+
+ LocalExchangeTypeRequire probeSideRequire;
+ LocalExchangeTypeRequire buildSideRequire;
+ LocalExchangeType outputType = null;
+
+ if (joinOp == JoinOperator.NULL_AWARE_LEFT_ANTI_JOIN) {
+ buildSideRequire = probeSideRequire = LocalExchangeTypeRequire.noRequire();
+ outputType = LocalExchangeType.NOOP;
+ } else if (distrMode == DistributionMode.BROADCAST) {
+ // BE: _child->is_serial_operator() ? PASSTHROUGH/PASS_TO_ONE : NOOP
+ boolean probeChildSerial = children.get(0).isSerialOperatorOnBe(
+ translatorContext.getConnectContext());
+ boolean buildChildSerial = children.get(1).isSerialOperatorOnBe(
+ translatorContext.getConnectContext());
+ probeSideRequire = probeChildSerial
Review Comment:
Mirrored — `probeSideRequire` now folds in the session flag: `probePassthrough = forcePassthrough || probeChildSerial`, where `forcePassthrough` reads `enableBroadcastJoinForcePassthrough` (`HashJoinNode` ~329-337, null-safe for the unit-test mocks).
One caveat from investigating it on a 4-BE cluster (forced non-serial probe via `ignore_storage_data_distribution=false`): it is effectively a no-op there. `enforceRequire` only inserts a PASSTHROUGH local exchange to fan a *serial* (1-task) source out to N tasks; an already-N-task source already satisfies passthrough, so no exchange is added (identical plan/results vs BE-native, no crash). The branch is kept to match BE's intent; a true rebalance of a non-serial probe (actually forcing the exchange) is a perf-only follow-up.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3411863216
##########
regression-test/data/query_p0/join/test_multilevel_join_agg_local_shuffle.out:
##########
@@ -0,0 +1,815 @@
+-- This file is automatically generated. You should know what you did if you want to edit this
+-- !bucket_shuffle_broadcast_agg_stage_1_result_on --
+1 52 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_shuffle_broadcast_agg_stage_1_result_off --
+1 52 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_shuffle_broadcast_agg_stage_2_result_on --
+1 52 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_shuffle_broadcast_agg_stage_2_result_off --
+1 52 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_shuffle_broadcast_agg_stage_3_result_on --
+1 52 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_shuffle_broadcast_agg_stage_3_result_off --
+1 52 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !shuffle_broadcast_broadcast_agg_stage_1_result_on --
+1 10 62 10
+2 5 54 20
+3 5 38 30
+4 6 49 40
+
+-- !shuffle_broadcast_broadcast_agg_stage_1_result_off --
+1 10 62 10
+2 5 54 20
+3 5 38 30
+4 6 49 40
+
+-- !shuffle_broadcast_broadcast_agg_stage_2_result_on --
+1 10 62 10
+2 5 54 20
+3 5 38 30
+4 6 49 40
+
+-- !shuffle_broadcast_broadcast_agg_stage_2_result_off --
+1 10 62 10
+2 5 54 20
+3 5 38 30
+4 6 49 40
+
+-- !shuffle_broadcast_broadcast_agg_stage_3_result_on --
+1 10 62 10
+2 5 54 20
+3 5 38 30
+4 6 49 40
+
+-- !shuffle_broadcast_broadcast_agg_stage_3_result_off --
+1 10 62 10
+2 5 54 20
+3 5 38 30
+4 6 49 40
+
+-- !bucket_broadcast_shuffle_agg_stage_1_result_on --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_broadcast_shuffle_agg_stage_1_result_off --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_broadcast_shuffle_agg_stage_2_result_on --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_broadcast_shuffle_agg_stage_2_result_off --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_broadcast_shuffle_agg_stage_3_result_on --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_broadcast_shuffle_agg_stage_3_result_off --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !alternating_bucket_bucket_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_bucket_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_bucket_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_bucket_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_bucket_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_bucket_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_shuffle_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_shuffle_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_shuffle_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_shuffle_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_shuffle_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_shuffle_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_broadcast_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_broadcast_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_broadcast_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_broadcast_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_broadcast_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_bucket_broadcast_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_bucket_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_bucket_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_bucket_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_bucket_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_bucket_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_bucket_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_shuffle_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_shuffle_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_shuffle_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_shuffle_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_shuffle_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_shuffle_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_broadcast_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_broadcast_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_broadcast_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_broadcast_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_broadcast_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_shuffle_broadcast_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_bucket_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_bucket_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_bucket_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_bucket_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_bucket_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_bucket_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_shuffle_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_shuffle_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_shuffle_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_shuffle_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_shuffle_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_shuffle_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_broadcast_bucket_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_broadcast_bucket_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_broadcast_shuffle_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_broadcast_shuffle_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_broadcast_broadcast_result_on --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !alternating_broadcast_broadcast_broadcast_result_off --
+1 63 16 11
+2 16 24 22
+3 16 35 33
+4 19 46 44
+
+-- !window_union_all_bucket_row_number_result_on --
+1 52 4 40
+2 21 3 60
+3 16 2 60
+4 20 2 80
+
+-- !window_union_all_bucket_row_number_result_off --
+1 52 4 40
+2 21 3 60
+3 16 2 60
+4 20 2 80
+
+-- !window_union_all_bucket_window_sum_result_on --
+1 10 13 40
+2 6 7 60
+3 3 8 60
+4 3 10 80
+
+-- !window_union_all_bucket_window_sum_result_off --
+1 10 13 40
+2 6 7 60
+3 3 8 60
+4 3 10 80
+
+-- !window_union_all_shuffle_row_number_result_on --
+1 208 8 44
+2 21 3 21
+3 16 2 16
+4 20 2 18
+
+-- !window_union_all_shuffle_row_number_result_off --
+1 208 8 44
+2 21 3 21
+3 16 2 16
+4 20 2 18
+
+-- !window_union_all_shuffle_window_sum_result_on --
+1 36 26 44
+2 6 7 21
+3 3 8 16
+4 3 10 18
+
+-- !window_union_all_shuffle_window_sum_result_off --
+1 36 26 44
+2 6 7 21
+3 3 8 16
+4 3 10 18
+
+-- !window_union_all_broadcast_row_number_result_on --
+1 52 4 40
+2 21 3 60
+3 16 2 60
+4 20 2 80
+
+-- !window_union_all_broadcast_row_number_result_off --
+1 52 4 40
+2 21 3 60
+3 16 2 60
+4 20 2 80
+
+-- !window_union_all_broadcast_window_sum_result_on --
+1 10 13 40
+2 6 7 60
+3 3 8 60
+4 3 10 80
+
+-- !window_union_all_broadcast_window_sum_result_off --
+1 10 13 40
+2 6 7 60
+3 3 8 60
+4 3 10 80
+
+-- !window_except_bucket_row_number_result_on --
+1 10 2 20
+2 10 2 40
+3 5 1 30
+4 6 1 40
+
+-- !window_except_bucket_row_number_result_off --
+1 10 2 20
+2 10 2 40
+3 5 1 30
+4 6 1 40
+
+-- !window_except_bucket_window_sum_result_on --
+1 3 5 20
+2 3 5 40
+3 1 5 30
+4 1 6 40
+
+-- !window_except_bucket_window_sum_result_off --
+1 3 5 20
+2 3 5 40
+3 1 5 30
+4 1 6 40
+
+-- !window_except_shuffle_row_number_result_on --
+1 40 4 22
+2 10 2 14
+3 5 1 8
+4 6 1 9
+
+-- !window_except_shuffle_row_number_result_off --
+1 40 4 22
+2 10 2 14
+3 5 1 8
+4 6 1 9
+
+-- !window_except_shuffle_window_sum_result_on --
+1 10 10 22
+2 3 5 14
+3 1 5 8
+4 1 6 9
+
+-- !window_except_shuffle_window_sum_result_off --
+1 10 10 22
+2 3 5 14
+3 1 5 8
+4 1 6 9
+
+-- !window_except_broadcast_row_number_result_on --
+1 10 2 20
+2 10 2 40
+3 5 1 30
+4 6 1 40
+
+-- !window_except_broadcast_row_number_result_off --
+1 10 2 20
+2 10 2 40
+3 5 1 30
+4 6 1 40
+
+-- !window_except_broadcast_window_sum_result_on --
+1 3 5 20
+2 3 5 40
+3 1 5 30
+4 1 6 40
+
+-- !window_except_broadcast_window_sum_result_off --
+1 3 5 20
+2 3 5 40
+3 1 5 30
+4 1 6 40
+
+-- !window_intersect_bucket_row_number_result_on --
+
+-- !window_intersect_bucket_row_number_result_off --
+
+-- !window_intersect_bucket_window_sum_result_on --
+
+-- !window_intersect_bucket_window_sum_result_off --
+
+-- !window_intersect_shuffle_row_number_result_on --
+
+-- !window_intersect_shuffle_row_number_result_off --
+
+-- !window_intersect_shuffle_window_sum_result_on --
+
+-- !window_intersect_shuffle_window_sum_result_off --
+
+-- !window_intersect_broadcast_row_number_result_on --
+
+-- !window_intersect_broadcast_row_number_result_off --
+
+-- !window_intersect_broadcast_window_sum_result_on --
+
+-- !window_intersect_broadcast_window_sum_result_off --
+
+-- !bucket_broadcast_agg_result_on --
+1 26 10
+2 9 20
+3 8 30
+4 10 40
+
+-- !bucket_broadcast_agg_result_off --
+1 26 10
+2 9 20
+3 8 30
+4 10 40
+
+-- !partitioned_broadcast_agg_result_on --
+1 32 10
+2 19 20
+3 13 30
+4 15 40
+
+-- !partitioned_broadcast_agg_result_off --
+1 32 10
+2 19 20
+3 13 30
+4 15 40
+
+-- !bucket_partitioned_agg_result_on --
+1 52 44
+2 9 14
+3 8 8
+4 10 9
+
+-- !bucket_partitioned_agg_result_off --
+1 52 44
+2 9 14
+3 8 8
+4 10 9
+
+-- !all_three_multilevel_agg_result_on --
+1 96 10
+2 23 20
+3 16 30
+4 19 40
+
+-- !all_three_multilevel_agg_result_off --
+1 96 10
+2 23 20
+3 16 30
+4 19 40
+
+-- !agg_join_agg_mix_result_on --
+1 10 11 10
+2 5 7 20
+3 5 8 30
+4 6 9 40
+
+-- !agg_join_agg_mix_result_off --
+1 10 11 10
+2 5 7 20
+3 5 8 30
+4 6 9 40
+
+-- !double_broadcast_after_bucket_result_on --
+1 26 20
+2 9 40
+3 8 60
+4 10 80
+
+-- !double_broadcast_after_bucket_result_off --
+1 26 20
+2 9 40
+3 8 60
+4 10 80
+
+-- !partitioned_join_between_two_aggs_then_broadcast_result_on --
+1 16 10
+2 12 20
+3 13 30
+4 15 40
+
+-- !partitioned_join_between_two_aggs_then_broadcast_result_off --
+1 16 10
+2 12 20
+3 13 30
+4 15 40
+
+-- !bucket_shuffle_broadcast_two_stage_agg_result_on --
+1 52 44 10
+2 9 14 20
+3 8 8 30
+4 10 9 40
+
+-- !bucket_shuffle_broadcast_two_stage_agg_result_off --
+1 52 44 10
+2 9 14 20
+3 8 8 30
+4 10 9 40
+
+-- !left_join_null_preserving_with_multilevel_agg_result_on --
+1 5 44
+2 5 14
+3 5 8
+4 6 9
+
+-- !left_join_null_preserving_with_multilevel_agg_result_off --
+1 5 44
+2 5 14
+3 5 8
+4 6 9
+
+-- !seven_layer_bucket_shuffle_broadcast_result_on --
+1 37 10
+2 16 20
+3 16 30
+4 19 40
+
+-- !seven_layer_bucket_shuffle_broadcast_result_off --
+1 37 10
+2 16 20
+3 16 30
+4 19 40
+
+-- !eight_layer_mixed_join_agg_chain_result_on --
+1 52 44 20
+2 9 14 40
+3 8 8 60
+4 10 9 80
+
+-- !eight_layer_mixed_join_agg_chain_result_off --
+1 52 44 20
+2 9 14 40
+3 8 8 60
+4 10 9 80
+
+-- !seven_layer_left_join_mix_result_on --
+1 10 11 10
+2 5 7 20
+3 5 8 30
+4 6 9 40
+
+-- !seven_layer_left_join_mix_result_off --
+1 10 11 10
+2 5 7 20
+3 5 8 30
+4 6 9 40
+
+-- !broadcast_shuffle_broadcast_nested_agg_result_on --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !broadcast_shuffle_broadcast_nested_agg_result_off --
+1 26 11 10
+2 9 7 20
+3 8 8 30
+4 10 9 40
+
+-- !window_union_join_agg_result_on --
+1 13 14
+2 7 23
+3 8 32
+4 10 42
+
+-- !window_union_join_agg_result_off --
+1 13 14
+2 7 23
+3 8 32
+4 10 42
+
+-- !window_except_join_agg_result_on --
+1 1 10
+2 1 20
+3 1 30
+
+-- !window_except_join_agg_result_off --
+1 1 10
+2 1 20
+3 1 30
+
+-- !window_intersect_shuffle_agg_result_on --
+1 22 2
+2 7 1
+3 8 1
+4 9 1
+
+-- !window_intersect_shuffle_agg_result_off --
+1 22 2
+2 7 1
+3 8 1
+4 9 1
+
+-- !window_union_except_broadcast_agg_result_on --
+1 13 4 10
+2 7 3 20
+3 8 2 30
+4 10 2 40
+
+-- !window_union_except_broadcast_agg_result_off --
+1 13 4 10
+2 7 3 20
+3 8 2 30
+4 10 2 40
+
+-- !window_setop_join_agg_chain_result_on --
+1 20 2 22
+2 10 2 14
+3 5 1 8
+4 6 1 9
+
+-- !window_setop_join_agg_chain_result_off --
+1 20 2 22
+2 10 2 14
+3 5 1 8
+4 6 1 9
+
Review Comment:
Fixed in `73cc6458fd1` — trimmed the trailing blank line at EOF so the expected output ends at the last result row. `git diff --check` is clean.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3413491554
##########
be/src/exec/operator/partition_sort_sink_operator.h:
##########
@@ -95,9 +95,10 @@ class PartitionSortSinkOperatorX final : public DataSinkOperatorX<PartitionSortS
Status sink_impl(RuntimeState* state, Block* in_block, bool eos) override;
DataDistribution required_data_distribution(RuntimeState* /*state*/) const override {
if (_topn_phase == TPartTopNPhase::TWO_PHASE_GLOBAL) {
- return DataDistribution(ExchangeType::HASH_SHUFFLE, _distribute_exprs);
+ return DataDistribution(TLocalPartitionType::GLOBAL_EXECUTION_HASH_SHUFFLE,
Review Comment:
`HASH_SHUFFLE` was renamed to `GLOBAL_EXECUTION_HASH_SHUFFLE` in this PR — same semantics (the old single hash-shuffle type was the global, cross-instance one), so every old `HASH_SHUFFLE` became `GLOBAL_EXECUTION_HASH_SHUFFLE`. `LOCAL_EXECUTION_HASH_SHUFFLE` is the new, finer-grained type.
An operator declares the general `GLOBAL_EXECUTION_HASH_SHUFFLE` as its requirement; BE then downgrades it to `LOCAL_EXECUTION_HASH_SHUFFLE` at pipeline build (`pipeline_fragment_context.cpp:1026-1028`) when `use_global_hash_shuffle` is false — i.e. when a within-backend rebalance suffices and a cross-backend global hash isn't needed. (On the FE-planned path, `AddLocalExchange.resolveExchangeType` does the same GLOBAL→LOCAL specialization.)
##########
be/src/exec/pipeline/dependency.h:
##########
@@ -832,50 +832,44 @@ struct SetSharedState : public BasicSharedState {
Status hash_table_init();
};
-enum class ExchangeType : uint8_t {
- NOOP = 0,
- // Shuffle data by Crc32CHashPartitioner
- HASH_SHUFFLE = 1,
- // Round-robin passthrough data blocks.
- PASSTHROUGH = 2,
- // Shuffle data by Crc32HashPartitioner<ShuffleChannelIds> (e.g. same as storage engine).
- BUCKET_HASH_SHUFFLE = 3,
- // Passthrough data blocks to all channels.
- BROADCAST = 4,
- // Passthrough data to channels evenly in an adaptive way.
- ADAPTIVE_PASSTHROUGH = 5,
- // Send all data to the first channel.
- PASS_TO_ONE = 6,
-};
+inline bool is_shuffled_exchange(TLocalPartitionType::type idx) {
+ return idx == TLocalPartitionType::GLOBAL_EXECUTION_HASH_SHUFFLE ||
+ idx == TLocalPartitionType::LOCAL_EXECUTION_HASH_SHUFFLE ||
+ idx == TLocalPartitionType::BUCKET_HASH_SHUFFLE;
+}
Review Comment:
`is_shuffled_exchange` is defined once (inline here in `dependency.h`) and reused at `pipeline.h:72/79`, `local_exchange_sink_operator.cpp:120`, and `local_exchange_source_operator.cpp:34` — there is no duplicate copy in `operator.cpp`. If you spotted a separate function in `operator.cpp` doing the same hash-type check, point me at it and I'll unify them.
##########
be/src/exec/pipeline/pipeline.h:
##########
@@ -68,16 +68,15 @@ class Pipeline : public std::enable_shared_from_this<Pipeline> {
[[nodiscard]] PipelineId id() const { return _pipeline_id; }
- static bool is_hash_exchange(ExchangeType idx) {
- return idx == ExchangeType::HASH_SHUFFLE || idx == ExchangeType::BUCKET_HASH_SHUFFLE;
+ static bool is_hash_exchange(TLocalPartitionType::type idx) {
+ return is_shuffled_exchange(idx);
}
// For HASH_SHUFFLE, BUCKET_HASH_SHUFFLE, and ADAPTIVE_PASSTHROUGH,
Review Comment:
Updated in `6a48e0864df` — the comment now lists the hash-shuffle types (GLOBAL_EXECUTION_HASH_SHUFFLE / LOCAL_EXECUTION_HASH_SHUFFLE / BUCKET_HASH_SHUFFLE) plus ADAPTIVE_PASSTHROUGH, instead of the old `HASH_SHUFFLE` name.
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -723,7 +931,7 @@ Status PipelineFragmentContext::_create_tree_helper(
*root = op;
}
/**
- * `ExchangeType::HASH_SHUFFLE` should be used if an operator is followed by a shuffled operator (shuffled hash join, union operator followed by co-located operators).
+ * `TLocalPartitionType::HASH_SHUFFLE` should be used if an operator is followed by a shuffled operator (shuffled hash join, union operator followed by co-located operators).
Review Comment:
Updated in `6a48e0864df` — `HASH_SHUFFLE` → `GLOBAL_EXECUTION_HASH_SHUFFLE` in the comment.
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -810,52 +1018,59 @@ Status PipelineFragmentContext::_add_local_exchange_impl(
sink_id, local_exchange_id, use_global_hash_shuffle ? _total_instances : _num_instances,
data_distribution.partition_exprs, bucket_seq_to_instance_idx);
if (bucket_seq_to_instance_idx.empty() &&
- data_distribution.distribution_type == ExchangeType::BUCKET_HASH_SHUFFLE) {
- data_distribution.distribution_type = ExchangeType::HASH_SHUFFLE;
+ data_distribution.distribution_type == TLocalPartitionType::BUCKET_HASH_SHUFFLE) {
+ data_distribution.distribution_type =
+ use_global_hash_shuffle ? TLocalPartitionType::GLOBAL_EXECUTION_HASH_SHUFFLE
+ : TLocalPartitionType::LOCAL_EXECUTION_HASH_SHUFFLE;
+ }
+ if (!use_global_hash_shuffle &&
+ data_distribution.distribution_type == TLocalPartitionType::GLOBAL_EXECUTION_HASH_SHUFFLE) {
+ data_distribution.distribution_type = TLocalPartitionType::LOCAL_EXECUTION_HASH_SHUFFLE;
Review Comment:
Yes, exactly. The operator declares `GLOBAL_EXECUTION_HASH_SHUFFLE` as its required distribution (the general/safe hash), and here BE downgrades it to `LOCAL_EXECUTION_HASH_SHUFFLE` when `use_global_hash_shuffle` is false. That flag is `use_global_hash_shuffle = bucket_seq_to_instance_idx.empty() && !shuffle_idx_to_instance_idx.contains(-1) && followed_by_shuffled_operator && !_use_serial_source` (line 1014) — so GLOBAL is kept only when the data must align with a cross-backend hash (followed by a shuffled operator, non-bucket, non-serial); otherwise a within-backend LOCAL rebalance is enough.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4708136408
@morrySnow re: "why don't you impl it before translation and after post-process in Nereids?"
Local-exchange planning needs two inputs that only exist **after translation + distribute**, not on the Nereids physical plan:
1. **PlanNode-level info** — `AddLocalExchange` walks the translated `PlanNode` / `PlanFragment` tree via `PlanNode.enforceAndDeriveLocalExchange`, using per-node things like `isSerialNode()` / `isSerialOperatorOnBe()`, the distribute-expr lists serialized to BE thrift, the fragment sink type, etc. Those are the legacy `PlanNode` structures produced by the translator — they don't exist on the Nereids physical operators.
2. **Instance counts** — whether to insert an LE (and the fan-out width) depends on the per-BE instance count: `addLocalExchange` skips fragments where `maxPerBeInstances <= 1` (mirroring BE's `_num_instances <= 1` no-op), and the PASSTHROUGH fan-out targets `_num_instances`. Instance assignment is done by `DistributePlanner`, which runs **after** translation.
So it runs in `addLocalExchangeAfterDistribute()` — right after `splitFragments()` + `doDistribute()` — because that's the first point where both the BE-facing PlanNode tree and the instance assignment exist. Doing it pre-translation in Nereids would require re-deriving the fragment structure and instance counts that haven't been computed yet.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4741384477
<details>
<summary>TPC-H: <b>Total hot run time: 29107 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 0436cc4cb573b104a59408a6169bd145bcfeff6c, data reload: false
------ Round 1 ----------------------------------
============================================
q1 17853 4015 3993 3993
q2 2014 322 202 202
q3 10306 1462 818 818
q4 4716 473 362 362
q5 7777 860 576 576
q6 229 181 141 141
q7 799 867 629 629
q8 10254 1483 1653 1483
q9 6015 4578 4551 4551
q10 6781 1822 1511 1511
q11 442 282 247 247
q12 662 447 299 299
q13 18169 3504 2810 2810
q14 268 263 248 248
q15 q16 789 772 716 716
q17 1041 891 924 891
q18 7153 5863 5599 5599
q19 1258 1289 1100 1100
q20 494 403 259 259
q21 5699 2667 2363 2363
q22 432 365 309 309
Total cold run time: 103151 ms
Total hot run time: 29107 ms
----- Round 2, with runtime_filter_mode=off -----
============================================
q1 4384 4296 4308 4296
q2 311 350 226 226
q3 4622 4994 4467 4467
q4 2071 2173 1361 1361
q5 4462 4325 4337 4325
q6 228 180 130 130
q7 1966 2034 1669 1669
q8 2600 2219 2215 2215
q9 8546 8266 8187 8187
q10 4834 4777 4294 4294
q11 606 428 396 396
q12 770 747 539 539
q13 3240 3596 2970 2970
q14 299 298 279 279
q15 q16 703 723 652 652
q17 1357 1474 1324 1324
q18 8063 7235 7194 7194
q19 1146 1108 1131 1108
q20 2249 2222 1936 1936
q21 5271 4609 4533 4533
q22 512 466 401 401
Total cold run time: 58240 ms
Total hot run time: 52502 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4732291285
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/c2c51332d7f90c80ccabf77b27c23317d636a128_c2c51332d7f90c80ccabf77b27c23317d636a128_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/c2c51332d7f90c80ccabf77b27c23317d636a128_c2c51332d7f90c80ccabf77b27c23317d636a128_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 77.32% (1889/2443) |
| Line Coverage | 64.41% (33962/52725) |
| Region Coverage | 64.80% (17463/26948) |
| Branch Coverage | 53.98% (9348/17316) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4681086984
<details>
<summary>TPC-DS: <b>Total hot run time: 168474 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 1f614d8cadd1619a8c17dd6f7ec7b12ba6983286, data reload: false
query5 4358 611 456 456
query6 441 187 168 168
query7 4814 555 287 287
query8 359 216 198 198
query9 8753 3999 4011 3999
query10 445 309 278 278
query11 5898 2360 2172 2172
query12 153 101 121 101
query13 1291 596 419 419
query14 6723 5339 5002 5002
query14_1 4368 4360 4326 4326
query15 204 200 172 172
query16 1020 461 416 416
query17 1101 689 563 563
query18 2729 461 332 332
query19 224 186 136 136
query20 109 105 100 100
query21 221 139 117 117
query22 13658 13549 13457 13457
query23 17447 16409 16263 16263
query23_1 16279 16347 16340 16340
query24 7587 1793 1313 1313
query24_1 1321 1318 1285 1285
query25 557 452 390 390
query26 1298 321 176 176
query27 2546 574 354 354
query28 4367 2036 2012 2012
query29 1089 624 509 509
query30 315 247 209 209
query31 1161 1099 961 961
query32 109 69 61 61
query33 528 322 256 256
query34 1174 1126 674 674
query35 762 784 695 695
query36 1415 1434 1260 1260
query37 161 112 91 91
query38 3201 3144 3058 3058
query39 943 929 896 896
query39_1 891 898 881 881
query40 223 126 105 105
query41 68 66 71 66
query42 98 95 95 95
query43 319 366 280 280
query44
query45 188 180 190 180
query46 1076 1195 715 715
query47 2435 2369 2218 2218
query48 397 403 277 277
query49 614 481 337 337
query50 974 361 255 255
query51 4470 4446 4323 4323
query52 88 86 75 75
query53 239 250 191 191
query54 253 211 194 194
query55 77 74 68 68
query56 221 213 199 199
query57 1451 1413 1338 1338
query58 239 202 211 202
query59 1542 1626 1436 1436
query60 288 240 223 223
query61 142 145 151 145
query62 716 666 588 588
query63 237 179 181 179
query64 2444 760 607 607
query65
query66 1705 451 342 342
query67 29005 29015 29575 29015
query68
query69 422 298 264 264
query70 971 951 939 939
query71 303 219 209 209
query72 2887 2675 2390 2390
query73 871 758 440 440
query74 5105 4926 4750 4750
query75 2660 2563 2232 2232
query76 2335 1144 748 748
query77 347 362 277 277
query78 12448 12378 11937 11937
query79 1478 986 721 721
query80 578 454 380 380
query81 451 279 243 243
query82 559 158 117 117
query83 345 273 244 244
query84
query85 852 505 410 410
query86 361 298 296 296
query87 3382 3296 3223 3223
query88 3596 2711 2675 2675
query89 412 383 331 331
query90 1951 181 181 181
query91 169 156 132 132
query92 65 64 56 56
query93 1570 1490 944 944
query94 558 359 318 318
query95 697 460 346 346
query96 978 804 344 344
query97 2720 2735 2600 2600
query98 216 207 201 201
query99 1151 1182 1028 1028
Total cold run time: 250302 ms
Total hot run time: 168474 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3397293756
##########
regression-test/suites/nereids_p0/local_shuffle/test_local_shuffle_rqg_bugs.groovy:
##########
@@ -0,0 +1,1561 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/**
+ * Regression tests for bugs discovered by RQG testing on the local-exchange2 branch.
+ *
+ * These queries triggered "must set shared state" errors or incorrect results
+ * in RQG build 183992. Common conditions:
+ * - use_serial_exchange=true (makes ALL Exchanges serial, not just UNPARTITIONED)
+ * - enable_local_shuffle_planner=true (FE-planned local exchange)
+ * - parallel_pipeline_task_num > 1
+ *
+ * Error types reproduced:
+ * 1. must set shared state, in AGGREGATION_OPERATOR
+ * 2. must set shared state, in SORT_OPERATOR
+ * 3. incorrect results with GROUPING SETS + scalar subquery + window function
+ */
+suite("test_local_shuffle_rqg_bugs") {
+
+ // ============================================================
+ // Table setup — mirrors RQG table structure
+ // 10 buckets to match RQG (replication_num=1 for single-BE testing)
+ // ============================================================
+ sql "DROP TABLE IF EXISTS rqg_t1"
+ sql "DROP TABLE IF EXISTS rqg_t2"
+ sql "DROP TABLE IF EXISTS rqg_t3"
+ sql "DROP TABLE IF EXISTS rqg_t4"
+
+ sql """
+ CREATE TABLE rqg_t1 (
+ pk INT NOT NULL,
+ col_int_undef_signed INT,
+ col_int_undef_signed2 INT,
+ col_int_undef_signed_not_null INT NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ CREATE TABLE rqg_t2 (
+ pk INT NOT NULL,
+ col_int_undef_signed INT,
+ col_int_undef_signed2 INT,
+ col_bigint_undef_signed_not_null BIGINT NOT NULL,
+ col_decimal_38_10__undef_signed_not_null DECIMAL(38,10) NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ // Table for build 184181 GLOBAL_HASH_SHUFFLE bugs — needs varchar + bigint columns
+ sql """
+ CREATE TABLE rqg_t3 (
+ pk INT NOT NULL,
+ col_bigint_undef_signed BIGINT,
+ col_varchar_10__undef_signed VARCHAR(10),
+ col_varchar_64__undef_signed VARCHAR(64)
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ // Second table for FULL OUTER JOIN case (col_bigint_undef_signed_not_null)
+ sql """
+ CREATE TABLE rqg_t4 (
+ pk INT NOT NULL,
+ col_bigint_undef_signed_not_null BIGINT NOT NULL
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ INSERT INTO rqg_t3 VALUES
+ (0, -94, 'Abc', 'hello world'),
+ (1, 672609, 'Xyz', null),
+ (2, -3766684, 'Pqr', 'test string'),
+ (3, 5070261, 'abc', 'another row'),
+ (4, null, 'def', 'value four'),
+ (5, -86, 'XgpxlHBLEM', null),
+ (6, 21910, 'abc', 'they'),
+ (7, -63, 'zzzz', 'some text'),
+ (8, -8276281, 'AHlvNtoGLO', 'longer string here'),
+ (9, -101, 'mid', 'final row')
+ """
+
+ sql """
+ INSERT INTO rqg_t4 VALUES
+ (0, 0), (1, 1), (2, 2), (3, 3), (4, 4),
+ (5, 5), (6, 6), (7, 7), (8, 8), (9, 9),
+ (10, 2), (11, 2), (12, 2), (13, 3), (14, 4),
+ (15, 5), (16, 2), (17, 2), (18, 2), (19, 9)
+ """
+
+ // Insert enough rows to exercise multiple pipeline tasks
+ sql """
+ INSERT INTO rqg_t1 VALUES
+ (0, 0, 10, 0), (1, 1, 11, 1), (2, 2, 12, 2), (3, 3, 13, 3),
+ (4, 4, 14, 4), (5, 5, 15, 5), (6, 6, 16, 6), (7, 7, 17, 7),
+ (8, 8, 18, 8), (9, 9, 19, 9), (10, 0, 20, 10), (11, 1, 21, 11),
+ (12, 2, 22, 12), (13, 3, 23, 13), (14, 4, 24, 14), (15, 5, 25, 15),
+ (16, 6, 26, 16), (17, 7, 27, 17), (18, 8, 28, 18), (19, 9, 29, 19)
+ """
+
+ sql """
+ INSERT INTO rqg_t2 VALUES
+ (0, 0, 10, 100, 1.5), (1, 1, 11, 101, 2.5), (2, 2, 12, 102, 3.5),
+ (3, 3, 13, 103, 4.5), (4, 4, 14, 104, 5.5), (5, 5, 15, 105, 6.5),
+ (6, 6, 16, 106, 7.5), (7, 7, 17, 107, 8.5), (8, 8, 18, 108, 9.5),
+ (9, 9, 19, 109, 10.5), (10, 0, 20, 110, 11.5), (11, 1, 21, 111, 12.5),
+ (12, 2, 22, 112, 13.5), (13, 3, 23, 113, 14.5), (14, 4, 24, 114, 15.5),
+ (15, 5, 25, 115, 16.5), (16, 6, 26, 116, 17.5), (17, 7, 27, 117, 18.5),
+ (18, 8, 28, 118, 19.5), (19, 9, 29, 119, 20.5)
+ """
+
+ // Wait for data to be visible
+ Thread.sleep(5000)
+
+ // ============================================================
+ // Common settings
+ // ============================================================
+ sql "SET enable_nereids_planner=true"
+ sql "SET enable_fallback_to_original_planner=false"
+ sql "SET runtime_filter_mode=off"
+ sql "SET enable_profile=true"
+ sql "SET enable_sql_cache=false"
+ sql "SET enable_local_shuffle=true"
+
+ // ============================================================
+ // Bug 1: must set shared state, in AGGREGATION_OPERATOR
+ // RQG case: eliminate_group_by_uniform.case_id_11007680713
+ // Key conditions: use_serial_exchange=true, parallel_pipeline_task_num=3
+ // SQL: EXCEPT with count(*) GROUP BY on both sides
+ // ============================================================
+
+ // Test with FE planner (the buggy path)
+ logger.info("=== Bug 1a: AGG shared state - EXCEPT with serial exchange (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=3,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_common_expr_pushdown=false,
+ disable_streaming_preaggregations=true)*/
+ col_int_undef_signed_not_null as col1,
+ col_int_undef_signed_not_null as col2,
+ 0 as col3, count(1)
+ FROM rqg_t1
+ GROUP BY col1, col2, col3
+ EXCEPT
+ SELECT col_bigint_undef_signed_not_null as col1,
+ col_decimal_38_10__undef_signed_not_null as col2,
+ 5 as col3, count(1)
+ FROM rqg_t2
+ GROUP BY col1, col2, col3
+ """
+ logger.info("Bug 1a: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 1a FAILED: ${t.message}")
+ assertTrue(false, "Bug 1a: must set shared state in AGGREGATION_OPERATOR: ${t.message}")
+ }
+
+ // Compare with BE native planner
+ logger.info("=== Bug 1b: AGG shared state - EXCEPT with serial exchange (BE native) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=3,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_common_expr_pushdown=false,
+ disable_streaming_preaggregations=true)*/
+ col_int_undef_signed_not_null as col1,
+ col_int_undef_signed_not_null as col2,
+ 0 as col3, count(1)
+ FROM rqg_t1
+ GROUP BY col1, col2, col3
+ EXCEPT
+ SELECT col_bigint_undef_signed_not_null as col1,
+ col_decimal_38_10__undef_signed_not_null as col2,
+ 5 as col3, count(1)
+ FROM rqg_t2
+ GROUP BY col1, col2, col3
+ """
+ logger.info("Bug 1b: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 1b FAILED: ${t.message}")
+ assertTrue(false, "Bug 1b: BE native also fails: ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 2: must set shared state, in SORT_OPERATOR
+ // RQG case: grouping_set.case_id_5308471751
+ // Key conditions: use_serial_exchange=true, parallel_pipeline_task_num=5
+ // SQL: GROUPING SETS + window function (PERCENT_RANK)
+ // ============================================================
+
+ logger.info("=== Bug 2a: SORT shared state - GROUPING SETS + window (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=5,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true)*/
+ SUM(PERCENT_RANK() OVER (PARTITION BY col_int_undef_signed2 ORDER BY col_int_undef_signed2))
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2),(pk, pk),(col_int_undef_signed))
+ """
+ logger.info("Bug 2a: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 2a FAILED: ${t.message}")
+ assertTrue(false, "Bug 2a: must set shared state in SORT_OPERATOR: ${t.message}")
+ }
+
+ logger.info("=== Bug 2b: SORT shared state - GROUPING SETS + window (BE native) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=5,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true)*/
+ SUM(PERCENT_RANK() OVER (PARTITION BY col_int_undef_signed2 ORDER BY col_int_undef_signed2))
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2),(pk, pk),(col_int_undef_signed))
+ """
+ logger.info("Bug 2b: PASSED (no crash)")
+ } catch (Throwable t) {
+ logger.error("Bug 2b FAILED: ${t.message}")
+ assertTrue(false, "Bug 2b: BE native also fails: ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 3: incorrect results with GROUPING SETS + scalar subquery + window
+ // RQG case: grouping_set.case_id_5694495756
+ // Key conditions: parallel_pipeline_task_num=2, disable_streaming_preaggregations=true
+ // Expected: all rows same value; Actual: values split proportionally (1/3, 2/3)
+ // ============================================================
+
+ logger.info("=== Bug 3: incorrect results - GROUPING SETS + subquery + window ===")
+ // FE planner
+ def result_fe = sql """
+ SELECT /*+SET_VAR(parallel_pipeline_task_num=2,
+ enable_local_shuffle_planner=true,
+ disable_streaming_preaggregations=true,
+ enable_share_hash_table_for_broadcast_join=true)*/
+ SUM((SELECT MAX(col_int_undef_signed2) FROM rqg_t1))
+ OVER (PARTITION BY pk ORDER BY pk)
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2, pk),(pk), (pk))
+ """
+ // BE native
+ def result_be = sql """
+ SELECT /*+SET_VAR(parallel_pipeline_task_num=2,
+ enable_local_shuffle_planner=false,
+ disable_streaming_preaggregations=true,
+ enable_share_hash_table_for_broadcast_join=true)*/
+ SUM((SELECT MAX(col_int_undef_signed2) FROM rqg_t1))
+ OVER (PARTITION BY pk ORDER BY pk)
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed2, pk),(pk), (pk))
+ """
+ logger.info("Bug 3 FE result rows: ${result_fe.size()}, first few: ${result_fe.take(5)}")
+ logger.info("Bug 3 BE result rows: ${result_be.size()}, first few: ${result_be.take(5)}")
+
+ // FE planner and BE native must produce identical results (the bug was values split
+ // proportionally instead of equal). Assert row count and order-insensitive content so a
+ // recurrence fails the suite.
+ assertEquals(result_be.size(), result_fe.size(), "Bug 3: FE/BE row count mismatch")
+ assertEquals(result_be.collect { it.toString() }.sort(), result_fe.collect { it.toString() }.sort(),
+ "Bug 3: FE/BE result mismatch")
+
+ // ============================================================
+ // Bug 4: Simplified AGG shared state — single table GROUP BY with serial exchange
+ // Minimal reproduction attempt
+ // ============================================================
+
+ logger.info("=== Bug 4: Simplified AGG shared state ===")
+ for (int ppt : [2, 3, 4, 5]) {
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true)*/
+ col_int_undef_signed, count(*)
+ FROM rqg_t1
+ GROUP BY col_int_undef_signed
+ UNION ALL
+ SELECT col_int_undef_signed, count(*)
+ FROM rqg_t2
+ GROUP BY col_int_undef_signed
+ """
+ logger.info("Bug 4 ppt=${ppt}: PASSED")
+ } catch (Throwable t) {
+ logger.error("Bug 4 ppt=${ppt} FAILED: ${t.message}")
+ assertTrue(false, "Bug 4 ppt=${ppt}: AGG shared state crash with serial exchange: ${t.message}")
+ }
+ }
+
+ // ============================================================
+ // Bug 5: GROUPING SETS + window variations with serial exchange
+ // More variations to find minimal repro
+ // ============================================================
+
+ logger.info("=== Bug 5: GROUPING SETS + window variations ===")
+ for (int ppt : [2, 3, 4, 5]) {
+ try {
+ sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true)*/
+ pk, col_int_undef_signed,
+ ROW_NUMBER() OVER (ORDER BY pk)
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((pk, col_int_undef_signed), (pk), ())
+ ORDER BY pk
+ """
+ logger.info("Bug 5 ppt=${ppt}: PASSED")
+ } catch (Throwable t) {
+ logger.error("Bug 5 ppt=${ppt} FAILED: ${t.message}")
+ assertTrue(false, "Bug 5 ppt=${ppt}: GROUPING SETS + window crash with serial exchange: ${t.message}")
+ }
+ }
+
+ // ============================================================
+ // Bug 6: must set shared state, in CROSS_JOIN_OPERATOR
+ // Root cause: nested NLJ + pooling scan — FE planner skipped BROADCAST
+ // local exchange on outer NLJ's build side because child was NLJ (not ScanNode).
+ // Fixed in NestedLoopJoinNode.enforceAndDeriveLocalExchange by using
+ // fragment.useSerialSource() instead of instanceof ScanNode check.
+ // This was the root cause of 989 RQG test failures (build 183677).
+ // ============================================================
+
+ logger.info("=== Bug 6: CROSS_JOIN shared state - nested NLJ + pooling scan (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(ignore_storage_data_distribution=true,
+ parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ disable_join_reorder=true,
+ disable_colocate_plan=true,
+ auto_broadcast_join_threshold=-1,
+ broadcast_row_count_limit=0,
+ query_timeout=60)*/
+ count(a.pk) AS cnt, a.col_int_undef_signed
+ FROM rqg_t1 a
+ LEFT JOIN rqg_t1 b ON b.col_int_undef_signed >= b.col_int_undef_signed
+ LEFT JOIN rqg_t1 c ON b.pk >= b.pk
+ WHERE a.pk IS NOT NULL
+ GROUP BY a.col_int_undef_signed
+ ORDER BY cnt, a.col_int_undef_signed
+ """
+ logger.info("Bug 6: PASSED (no CROSS_JOIN_OPERATOR shared state error)")
+ } catch (Throwable t) {
+ logger.error("Bug 6 FAILED: ${t.message}")
+ assertTrue(false, "Bug 6: must set shared state in CROSS_JOIN_OPERATOR: ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 7: DataStreamSink hang — sender fragment with pooling scan
+ // Root cause: FE planner did not insert PASSTHROUGH at the root of pooling scan
+ // sender fragments. With pooling scan, only instance 0 creates pipeline tasks,
+ // so only 1 EOS is sent. The downstream ExchangeNode expects _num_instances EOSes
+ // and hangs indefinitely.
+ // Fixed in AddLocalExchange.addLocalExchangeForFragment: insert PASSTHROUGH
+ // when isLocalShuffle && newRoot.isSerialOperator().
+ // Any NLJ + pooling scan query triggers this via the UNPARTITIONED sender fragments.
+ // ============================================================
+
+ logger.info("=== Bug 7: DataStreamSink hang - NLJ + pooling scan sender (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(ignore_storage_data_distribution=true,
+ parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ disable_join_reorder=true,
+ disable_colocate_plan=true,
+ auto_broadcast_join_threshold=-1,
+ broadcast_row_count_limit=0,
+ query_timeout=60)*/
+ a.col_int_undef_signed, MAX(a.pk) AS mx
+ FROM rqg_t1 a
+ LEFT JOIN rqg_t1 b ON b.col_int_undef_signed < b.col_int_undef_signed
+ WHERE a.pk IS NOT NULL
+ GROUP BY a.col_int_undef_signed
+ ORDER BY a.col_int_undef_signed, mx
+ """
+ logger.info("Bug 7: PASSED (no hang)")
+ } catch (Throwable t) {
+ logger.error("Bug 7 FAILED: ${t.message}")
+ assertTrue(false, "Bug 7: DataStreamSink hang (query timed out or crashed): ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 8: must set shared state, in SORT_OPERATOR / UNION_OPERATOR
+ // Root cause: FE planner + pooling scan + GROUPING SETS. Serial UNPARTITIONED
+ // Exchange reduces downstream pipeline num_tasks to 1. SORT and UNION operators
+ // need _num_instances tasks to inject shared state for all instances.
+ // Fixed by: (1) restoring num_tasks raise for non-scan serial operators in BE
+ // deferred exchanger creation (commit 920d43d), and (2) FE inserting PASSTHROUGH
+ // after serial ExchangeNode in pooling scan fragments (commit d2e7fa2).
+ // ============================================================
+
+ logger.info("=== Bug 8a: SORT/UNION shared state - GROUPING SETS + pooling scan (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(ignore_storage_data_distribution=true,
+ parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ disable_streaming_preaggregations=true,
+ query_timeout=60)*/
+ pk, col_int_undef_signed, SUM(col_int_undef_signed_not_null) AS sv
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((pk, col_int_undef_signed), (pk), ())
+ ORDER BY pk, col_int_undef_signed, sv
+ """
+ logger.info("Bug 8a: PASSED (no SORT/UNION_OPERATOR shared state error)")
+ } catch (Throwable t) {
+ logger.error("Bug 8a FAILED: ${t.message}")
+ assertTrue(false, "Bug 8a: must set shared state in SORT/UNION_OPERATOR: ${t.message}")
+ }
+
+ logger.info("=== Bug 8b: SORT shared state - window + GROUPING SETS + pooling scan (FE planner) ===")
+ try {
+ sql """
+ SELECT /*+SET_VAR(ignore_storage_data_distribution=true,
+ parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ disable_streaming_preaggregations=true,
+ query_timeout=60)*/
+ pk, SUM(col_int_undef_signed_not_null) AS sv,
+ ROW_NUMBER() OVER (ORDER BY pk) AS rn
+ FROM rqg_t1
+ GROUP BY GROUPING SETS ((pk), ())
+ ORDER BY pk, sv, rn
+ """
+ logger.info("Bug 8b: PASSED (no SORT_OPERATOR shared state error)")
+ } catch (Throwable t) {
+ logger.error("Bug 8b FAILED: ${t.message}")
+ assertTrue(false, "Bug 8b: must set shared state in SORT_OPERATOR (window+grouping_sets): ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 9: FE/BE result inconsistency — agg after NLJ + pooling scan
+ // Root cause: StreamingAgg used fragment.useSerialSource()=true to require
+ // PASSTHROUGH from child, but when child is NLJ (not directly a serial scan),
+ // NLJ outputs ADAPTIVE_PASSTHROUGH. FE wrongly inserted an extra PASSTHROUGH
+ // exchange between StreamingAgg and NLJ (5 extra LOCAL_EXCHANGE_SINK_OPERATOR
+ // entries vs BE native).
+ // Fixed in AggregationNode: only requirePassthrough when
+ // children.get(0).isSerialOperator()=true, mirroring BE _child->is_serial_operator().
+ // ============================================================
+
+ logger.info("=== Bug 9: FE/BE result consistency - agg after NLJ + pooling scan ===")
+ def bug9_fe = sql """
+ SELECT /*+SET_VAR(ignore_storage_data_distribution=true,
+ parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ disable_join_reorder=true,
+ disable_colocate_plan=true,
+ auto_broadcast_join_threshold=-1,
+ broadcast_row_count_limit=0)*/
+ a.col_int_undef_signed, MAX(a.pk) AS mx
+ FROM rqg_t1 a LEFT JOIN rqg_t1 b ON b.col_int_undef_signed < b.col_int_undef_signed
+ WHERE a.pk IS NOT NULL
+ GROUP BY a.col_int_undef_signed
+ ORDER BY a.col_int_undef_signed, mx
+ """
+ def bug9_be = sql """
+ SELECT /*+SET_VAR(ignore_storage_data_distribution=true,
+ parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=false,
+ disable_join_reorder=true,
+ disable_colocate_plan=true,
+ auto_broadcast_join_threshold=-1,
+ broadcast_row_count_limit=0)*/
+ a.col_int_undef_signed, MAX(a.pk) AS mx
+ FROM rqg_t1 a LEFT JOIN rqg_t1 b ON b.col_int_undef_signed < b.col_int_undef_signed
+ WHERE a.pk IS NOT NULL
+ GROUP BY a.col_int_undef_signed
+ ORDER BY a.col_int_undef_signed, mx
+ """
+ logger.info("Bug 9 FE rows: ${bug9_fe.size()}, BE rows: ${bug9_be.size()}")
+ assertEquals(bug9_be.size(), bug9_fe.size(), "Bug 9: FE/BE row count mismatch")
+ assertEquals(bug9_be, bug9_fe, "Bug 9: FE/BE result mismatch for agg after NLJ + pooling scan")
+ logger.info("Bug 9: PASSED (FE/BE results match)")
+
+ // ============================================================
+ // Bug 10: GLOBAL_HASH_SHUFFLE Rows mismatched — self-join + NLJ
+ // RQG case: 906784672 (build 184181)
+ // Root cause: HashJoinNode used requireGlobalExecutionHash() → GLOBAL local exchange
+ // inserted when use_serial_exchange=true; shuffle_idx_to_instance_idx map has only
+ // 4 entries (1/BE) but GLOBAL hash needs N*dop entries → most rows unrouted (0 actual rows).
+ // Fixed: changed to requireHash() so resolveExchangeType() downgrades to LOCAL hash.
+ // SQL: self-join (table1 LEFT JOIN table1 table2 ON pk=col_bigint_undef_signed)
+ // then NLJ (LEFT JOIN table1 table3 ON pk > col_bigint_undef_signed)
+ // ============================================================
+
+ logger.info("=== Bug 10: GLOBAL_HASH_SHUFFLE Rows mismatched - self-join + NLJ (build 184181 case 906784672) ===")
+ def bug10_fe = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ disable_join_reorder=true, disable_colocate_plan=true)*/
+ table1.pk AS field1, table1.col_bigint_undef_signed AS field2
+ FROM rqg_t3 AS table1
+ LEFT JOIN rqg_t3 AS table2 ON table1.pk = table2.col_bigint_undef_signed
+ LEFT JOIN rqg_t3 AS table3 ON table1.pk > table2.col_bigint_undef_signed
+ WHERE (table1.col_varchar_10__undef_signed > 'AHlvNtoGLO'
+ AND table1.col_varchar_10__undef_signed < 'zzzz')
+ OR (table1.col_bigint_undef_signed = table1.pk AND table1.col_varchar_64__undef_signed IS NULL)
+ OR (table1.pk != table1.pk AND table1.pk <> 2)
+ GROUP BY field1, field2
+ ORDER BY field1, field2
+ """
+ def bug10_be = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ disable_join_reorder=true, disable_colocate_plan=true)*/
+ table1.pk AS field1, table1.col_bigint_undef_signed AS field2
+ FROM rqg_t3 AS table1
+ LEFT JOIN rqg_t3 AS table2 ON table1.pk = table2.col_bigint_undef_signed
+ LEFT JOIN rqg_t3 AS table3 ON table1.pk > table2.col_bigint_undef_signed
+ WHERE (table1.col_varchar_10__undef_signed > 'AHlvNtoGLO'
+ AND table1.col_varchar_10__undef_signed < 'zzzz')
+ OR (table1.col_bigint_undef_signed = table1.pk AND table1.col_varchar_64__undef_signed IS NULL)
+ OR (table1.pk != table1.pk AND table1.pk <> 2)
+ GROUP BY field1, field2
+ ORDER BY field1, field2
+ """
+ logger.info("Bug 10 FE rows: ${bug10_fe.size()}, BE rows: ${bug10_be.size()}")
+ assertEquals(bug10_be.size(), bug10_fe.size(), "Bug 10: FE/BE row count mismatch (GLOBAL_HASH_SHUFFLE Rows mismatched)")
+ assertEquals(bug10_be, bug10_fe, "Bug 10: FE/BE result mismatch for self-join + NLJ")
+ logger.info("Bug 10: PASSED")
+
+ // ============================================================
+ // Bug 11: GLOBAL_HASH_SHUFFLE Rows mismatched — FULL OUTER JOIN + GROUP BY
+ // RQG case: 11007681241 (build 184181)
+ // Same root cause as Bug 10.
+ // SQL: FULL OUTER JOIN on col_bigint_undef_signed_not_null with WHERE + GROUP BY
+ // ============================================================
+
+ logger.info("=== Bug 11: GLOBAL_HASH_SHUFFLE Rows mismatched - FULL OUTER JOIN + GROUP BY (build 184181 case 11007681241) ===")
+ def bug11_fe = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false)*/
+ t1.col_bigint_undef_signed_not_null, t2.col_bigint_undef_signed_not_null, count(1)
+ FROM rqg_t4 t1
+ FULL OUTER JOIN rqg_t2 t2
+ ON t1.col_bigint_undef_signed_not_null = t2.col_bigint_undef_signed_not_null
+ WHERE t2.col_bigint_undef_signed_not_null = 2
+ GROUP BY t1.col_bigint_undef_signed_not_null, t2.col_bigint_undef_signed_not_null
+ ORDER BY 1, 2, 3
+ """
+ def bug11_be = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false)*/
+ t1.col_bigint_undef_signed_not_null, t2.col_bigint_undef_signed_not_null, count(1)
+ FROM rqg_t4 t1
+ FULL OUTER JOIN rqg_t2 t2
+ ON t1.col_bigint_undef_signed_not_null = t2.col_bigint_undef_signed_not_null
+ WHERE t2.col_bigint_undef_signed_not_null = 2
+ GROUP BY t1.col_bigint_undef_signed_not_null, t2.col_bigint_undef_signed_not_null
+ ORDER BY 1, 2, 3
+ """
+ logger.info("Bug 11 FE rows: ${bug11_fe.size()}, BE rows: ${bug11_be.size()}")
+ assertEquals(bug11_be.size(), bug11_fe.size(), "Bug 11: FE/BE row count mismatch (GLOBAL_HASH_SHUFFLE Rows mismatched)")
+ assertEquals(bug11_be, bug11_fe, "Bug 11: FE/BE result mismatch for FULL OUTER JOIN + GROUP BY")
+ logger.info("Bug 11: PASSED")
+
+ // ============================================================
+ // Bug 12: GLOBAL_HASH_SHUFFLE Rows mismatched — LEFT JOIN + VARCHAR predicates + MIN()
+ // RQG case: 906784662 (build 184181)
+ // Same root cause as Bug 10/11.
+ // SQL: LEFT JOIN on pk with VARCHAR NOT IN / BETWEEN / IN predicates, MIN() aggregate
+ // ============================================================
+
+ logger.info("=== Bug 12: GLOBAL_HASH_SHUFFLE Rows mismatched - LEFT JOIN + VARCHAR predicates (build 184181 case 906784662) ===")
+ def bug12_fe = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ disable_join_reorder=true, disable_colocate_plan=true)*/
+ table1.pk AS field1, MIN(table1.pk) AS field2
+ FROM rqg_t3 AS table1
+ LEFT JOIN rqg_t1 AS table2 ON table2.pk = table1.pk
+ WHERE table1.col_varchar_64__undef_signed NOT IN ('they')
+ AND table1.col_varchar_10__undef_signed BETWEEN 'AHlvNtoGLO' AND 'z'
+ AND table1.pk IN (3, 6, 8, 9, 2)
+ GROUP BY field1
+ ORDER BY field1, field2 ASC
+ """
+ def bug12_be = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=4,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ disable_join_reorder=true, disable_colocate_plan=true)*/
+ table1.pk AS field1, MIN(table1.pk) AS field2
+ FROM rqg_t3 AS table1
+ LEFT JOIN rqg_t1 AS table2 ON table2.pk = table1.pk
+ WHERE table1.col_varchar_64__undef_signed NOT IN ('they')
+ AND table1.col_varchar_10__undef_signed BETWEEN 'AHlvNtoGLO' AND 'z'
+ AND table1.pk IN (3, 6, 8, 9, 2)
+ GROUP BY field1
+ ORDER BY field1, field2 ASC
+ """
+ logger.info("Bug 12 FE rows: ${bug12_fe.size()}, BE rows: ${bug12_be.size()}")
+ assertEquals(bug12_be.size(), bug12_fe.size(), "Bug 12: FE/BE row count mismatch (GLOBAL_HASH_SHUFFLE Rows mismatched)")
+ assertEquals(bug12_be, bug12_fe, "Bug 12: FE/BE result mismatch for LEFT JOIN + VARCHAR predicates")
+ logger.info("Bug 12: PASSED")
+
+ // ============================================================
+ // Bug 13: NLJ COREDUMP — serial NLJ + pooling scan + BROADCAST build side
+ // RQG build 184430, query c0dafc1bed0f4910
+ // Root cause: serial NLJ (RIGHT_OUTER) with pooling scan inserted BROADCAST
+ // local exchange on build side, inflating build pipeline num_tasks to _num_instances
+ // while probe pipeline stayed at 1 task. Instance 1+ created build tasks without
+ // corresponding probe tasks → source_deps empty → set_ready_to_read() crash.
+ // Fixed: serial NLJ sets buildSideRequire=noRequire() to match BE-native
+ // num_tasks_of_parent()<=1 skip logic.
+ // ============================================================
+
+ logger.info("=== Bug 13: NLJ COREDUMP - serial NLJ + pooling scan (FE planner) ===")
+ try {
+ def bug13_fe = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=0,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true,
+ disable_join_reorder=true)*/
+ t2.col_bigint_undef_signed_not_null AS field1
+ FROM rqg_t4 AS t1
+ RIGHT OUTER JOIN rqg_t2 AS t2 ON t1.col_bigint_undef_signed_not_null > t2.col_bigint_undef_signed_not_null
+ GROUP BY field1
+ ORDER BY field1 ASC
+ """
+ def bug13_be = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true, parallel_pipeline_task_num=0,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true,
+ disable_join_reorder=true)*/
+ t2.col_bigint_undef_signed_not_null AS field1
+ FROM rqg_t4 AS t1
+ RIGHT OUTER JOIN rqg_t2 AS t2 ON t1.col_bigint_undef_signed_not_null > t2.col_bigint_undef_signed_not_null
+ GROUP BY field1
+ ORDER BY field1 ASC
+ """
+ logger.info("Bug 13 FE rows: ${bug13_fe.size()}, BE rows: ${bug13_be.size()}")
+ assertEquals(bug13_be.size(), bug13_fe.size(), "Bug 13: FE/BE row count mismatch (NLJ COREDUMP)")
+ assertEquals(bug13_be, bug13_fe, "Bug 13: FE/BE result mismatch for serial NLJ + pooling scan")
+ logger.info("Bug 13: PASSED (no crash, results match)")
+ } catch (Throwable t) {
+ logger.error("Bug 13 FAILED: ${t.message}")
+ assertTrue(false, "Bug 13: NLJ COREDUMP (serial NLJ + pooling scan): ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 14: BUCKET_SHUFFLE join + serial build Exchange — must set shared state
+ // RQG build 184563, cases 906784706/906784783/906784987/906785006
+ // Root cause: BUCKET_SHUFFLE join build side ExchangeNode marked serial in
+ // pooling scan fragment → build pipeline num_tasks reduced to 1 →
+ // instance 1+ have probe tasks without build tasks → shared state injection
+ // fails. Fixed: isBucketShuffle() branch checks buildChildSerial and uses
+ // requirePassToOne() to restore num_tasks, matching BE-native behavior.
+ // Requires replication_num=3 + [shuffle] hint to force BUCKET_SHUFFLE plan.
+ // ============================================================
+
+ logger.info("=== Bug 14: BUCKET_SHUFFLE join + serial build Exchange (FE planner) ===")
+ // Need replication_num=3 for BUCKET_SHUFFLE. Check if allow_replica_on_same_host is enabled.
+ def allowSameHost = sql "ADMIN SHOW FRONTEND CONFIG LIKE 'allow_replica_on_same_host'"
+ if (allowSameHost[0][1].toString() == "true") {
+ sql "DROP TABLE IF EXISTS rqg_t5_rep3"
+ sql "DROP TABLE IF EXISTS rqg_t6_rep3"
+ try {
+ sql """
+ CREATE TABLE rqg_t5_rep3 (
+ pk INT NULL,
+ col_varchar_10__undef_signed VARCHAR(10) NULL,
+ col_bigint_undef_signed BIGINT NULL,
+ col_varchar_64__undef_signed VARCHAR(64) NULL
+ ) DUPLICATE KEY(pk, col_varchar_10__undef_signed)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "3")
+ """
+ sql """
+ CREATE TABLE rqg_t6_rep3 (
+ pk INT NULL,
+ col_varchar_10__undef_signed VARCHAR(10) NULL,
+ col_bigint_undef_signed BIGINT NULL,
+ col_varchar_64__undef_signed VARCHAR(64) NULL
+ ) DUPLICATE KEY(pk, col_varchar_10__undef_signed)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "3")
+ """
+ sql """
+ INSERT INTO rqg_t5_rep3 VALUES
+ (0,'abc',-94,'hello'),(1,'xyz',672609,null),(2,'pqr',-3766684,'test'),
+ (3,'abc',5070261,'another'),(4,'def',null,'value'),(5,'so',-86,null),
+ (6,'abc',21910,'they'),(7,'zzzz',-63,'some'),(8,'xPLflvBEcW',-8276281,'longer'),
+ (9,'mid',-101,'final')
+ """
+ sql """
+ INSERT INTO rqg_t6_rep3 VALUES
+ (0,'aaa',100,'world'),(1,'bbb',200,null),(2,'ccc',300,'foo'),
+ (3,'ddd',400,'bar'),(4,'eee',500,'baz'),(5,'fff',600,null),
+ (6,'ggg',700,'qux'),(7,'hhh',800,'quux'),(8,'iii',900,'corge'),
+ (9,'jjj',1000,'grault')
+ """
+ Thread.sleep(3000)
+
+ def bug14_fe = sql """
+ SELECT /*+SET_VAR(enable_local_shuffle_planner=true,
+ parallel_pipeline_task_num=3,
+ disable_streaming_preaggregations=true,
+ enable_sql_cache=false,
+ disable_join_reorder=true)*/
+ table1.pk AS field1
+ FROM rqg_t5_rep3 AS table1
+ LEFT OUTER JOIN [shuffle] rqg_t6_rep3 AS table2 ON table1.pk = table2.pk
+ WHERE table1.col_varchar_10__undef_signed >= 'so'
+ GROUP BY field1
+ ORDER BY field1
+ """
+ def bug14_be = sql """
+ SELECT /*+SET_VAR(enable_local_shuffle_planner=false,
+ parallel_pipeline_task_num=3,
+ disable_streaming_preaggregations=true,
+ enable_sql_cache=false,
+ disable_join_reorder=true)*/
+ table1.pk AS field1
+ FROM rqg_t5_rep3 AS table1
+ LEFT OUTER JOIN [shuffle] rqg_t6_rep3 AS table2 ON table1.pk = table2.pk
+ WHERE table1.col_varchar_10__undef_signed >= 'so'
+ GROUP BY field1
+ ORDER BY field1
+ """
+ logger.info("Bug 14 FE rows: ${bug14_fe.size()}, BE rows: ${bug14_be.size()}")
+ assertEquals(bug14_be.size(), bug14_fe.size(), "Bug 14: FE/BE row count mismatch (BUCKET_SHUFFLE must set shared state)")
+ assertEquals(bug14_be, bug14_fe, "Bug 14: FE/BE result mismatch for BUCKET_SHUFFLE + serial build Exchange")
+ logger.info("Bug 14: PASSED (no crash, results match)")
+ } catch (Throwable t) {
+ logger.error("Bug 14 FAILED: ${t.message}")
+ assertTrue(false, "Bug 14: BUCKET_SHUFFLE must set shared state: ${t.message}")
+ }
+ } else {
+ logger.info("Bug 14: SKIPPED (allow_replica_on_same_host not enabled, cannot create replication_num=3 tables)")
+ }
+
+ // ============================================================
+ // Bug 15: BUCKET_SHUFFLE join wrong results with serial exchange — PASS_TO_ONE data loss
+ // Root cause: When serial exchange feeds BUCKET_SHUFFLE join build side,
+ // PASS_TO_ONE routes ALL build data to task 0. Unlike BROADCAST joins,
+ // BUCKET_SHUFFLE has no shared hash table mechanism — tasks 1..N-1 build
+ // empty hash tables and lose rows during probe. Fixed by using
+ // BUCKET_HASH_SHUFFLE instead of PASS_TO_ONE for BUCKET_SHUFFLE build side.
+ // Tables use 3 buckets so pptn=4 triggers serial scan on single BE (3 < 4*1).
+ // ============================================================
+
+ logger.info("=== Bug 15: BUCKET_SHUFFLE join wrong results with serial PASS_TO_ONE ===")
+
+ sql "DROP TABLE IF EXISTS rqg_t7_3bucket"
+ sql "DROP TABLE IF EXISTS rqg_t8_3bucket"
+
+ sql """
+ CREATE TABLE rqg_t7_3bucket (
+ pk INT NOT NULL,
+ col_int INT NULL,
+ col_varchar VARCHAR(64) NULL
+ ) DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 3
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ CREATE TABLE rqg_t8_3bucket (
+ pk INT NOT NULL,
+ col_int INT NULL,
+ col_varchar VARCHAR(64) NULL
+ ) DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 3
+ PROPERTIES ("replication_num" = "1")
+ """
+
+ sql """
+ INSERT INTO rqg_t7_3bucket VALUES
+ (0, 10, 'aaa'), (1, 20, 'bbb'), (2, 30, 'ccc'),
+ (3, 40, 'ddd'), (4, 50, 'eee'), (5, 60, 'fff'),
+ (6, 70, 'ggg'), (7, 80, 'hhh'), (8, 90, 'iii'), (9, 100, 'jjj')
+ """
+
+ sql """
+ INSERT INTO rqg_t8_3bucket VALUES
+ (0, 10, 'aaa'), (1, 20, 'bbb'), (2, 30, 'ccc'),
+ (3, 40, 'ddd'), (4, 50, 'eee'), (5, 60, 'fff'),
+ (6, 70, 'ggg'), (7, 80, 'hhh'), (8, 90, 'iii'), (9, 100, 'jjj')
+ """
+
+ Thread.sleep(3000)
+
+ try {
+ // pptn=4 with 3 buckets on 1 BE: 3 < 4*1 → serial scan → serial exchange
+ // This triggers the PASS_TO_ONE bug for BUCKET_SHUFFLE build side.
+ // Also test with higher pptn values to cover more cases.
+ for (int ppt : [4, 6, 8]) {
+ def bug15_fe = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,
+ parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ enable_sql_cache=false,
+ disable_join_reorder=true)*/
+ t1.pk, t1.col_int, t2.col_varchar
+ FROM rqg_t7_3bucket t1
+ INNER JOIN [shuffle] rqg_t8_3bucket t2 ON t1.pk = t2.pk
+ ORDER BY t1.pk
+ """
+ def bug15_be = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,
+ parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ enable_sql_cache=false,
+ disable_join_reorder=true)*/
+ t1.pk, t1.col_int, t2.col_varchar
+ FROM rqg_t7_3bucket t1
+ INNER JOIN [shuffle] rqg_t8_3bucket t2 ON t1.pk = t2.pk
+ ORDER BY t1.pk
+ """
+ logger.info("Bug 15 ppt=${ppt}: FE rows=${bug15_fe.size()}, BE rows=${bug15_be.size()}")
+ assertEquals(10, bug15_fe.size(), "Bug 15 ppt=${ppt}: expected 10 rows from FE planner, got ${bug15_fe.size()}")
+ assertEquals(bug15_be, bug15_fe, "Bug 15 ppt=${ppt}: FE/BE result mismatch for BUCKET_SHUFFLE + serial exchange")
+ }
+
+ // Also test LEFT OUTER JOIN to verify no rows lost on probe side
+ def bug15_left_fe = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,
+ parallel_pipeline_task_num=6,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ enable_sql_cache=false,
+ disable_join_reorder=true)*/
+ t1.pk, t2.col_int
+ FROM rqg_t7_3bucket t1
+ LEFT OUTER JOIN [shuffle] rqg_t8_3bucket t2 ON t1.pk = t2.pk
+ ORDER BY t1.pk
+ """
+ def bug15_left_be = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,
+ parallel_pipeline_task_num=6,
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_share_hash_table_for_broadcast_join=false,
+ enable_sql_cache=false,
+ disable_join_reorder=true)*/
+ t1.pk, t2.col_int
+ FROM rqg_t7_3bucket t1
+ LEFT OUTER JOIN [shuffle] rqg_t8_3bucket t2 ON t1.pk = t2.pk
+ ORDER BY t1.pk
+ """
+ assertEquals(10, bug15_left_fe.size(), "Bug 15 LEFT JOIN: expected 10 rows from FE planner")
+ assertEquals(bug15_left_be, bug15_left_fe, "Bug 15 LEFT JOIN: FE/BE result mismatch")
+
+ logger.info("Bug 15: PASSED (no wrong results, all pptn values correct)")
+ } catch (Throwable t) {
+ logger.error("Bug 15 FAILED: ${t.message}")
+ assertTrue(false, "Bug 15: BUCKET_SHUFFLE wrong results with serial PASS_TO_ONE: ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 16 & 17: Serial AnalyticEval crash and DataStreamSink hang
+ // with LocalShuffleAssignedJob (multiple instances on one BE)
+ //
+ // Bug 16 (crash): Exchange wraps itself with PASSTHROUGH LocalExchange.
+ // This restores AnalyticSink pipeline to _num_instances tasks while
+ // serial AnalyticSource stays at 1 task. For instance_idx > 0,
+ // source_deps is empty → DCHECK crash.
+ //
+ // Bug 17 (hang): After fixing the crash, serial AnalyticSource reduces
+ // all downstream pipeline tasks to 1 via add_pipeline() inheritance.
+ // Only instance 0 runs DataStreamSink → receiver expects _num_instances
+ // EOSes → hang.
+ //
+ // Both triggered by: OVER() with no PARTITION BY + GROUPING SETS +
+ // pptn=0 (auto-parallel) + disable_streaming_preaggregations=true
+ // RQG build 186195, query IDs: 7f3178a77c2c4b6b, 71887f7bf804c0c, 5dd9fcad234c4484
+ // ============================================================
+ sql "DROP TABLE IF EXISTS rqg_analytic_t1"
+ sql """
+ CREATE TABLE rqg_analytic_t1 (
+ pk INT NOT NULL,
+ col_int_undef_signed INT
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 10
+ PROPERTIES ("replication_num" = "1")
+ """
+ sql """
+ INSERT INTO rqg_analytic_t1 VALUES
+ (1, 10), (2, 20), (3, 30), (4, 40), (5, 50),
+ (6, 60), (7, 70), (8, 80), (9, 90), (10, 100),
+ (11, 10), (12, 20), (13, 30), (14, 40), (15, 50),
+ (16, 60), (17, 70), (18, 80), (19, 90), (20, 100)
+ """
+
+ try {
+ logger.info("Bug 16+17: Testing serial AnalyticEval with GROUPING SETS")
+
+ // Baseline: pptn=1 (no multi-instance, no local shuffle)
+ def bug16_baseline = sql """
+ SELECT /*+SET_VAR(parallel_pipeline_task_num=1,
+ enable_sql_cache=false,
+ disable_streaming_preaggregations=true)*/
+ COUNT(MIN(col_int_undef_signed) OVER())
+ FROM rqg_analytic_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed, pk), (), (pk))
+ ORDER BY 1
+ """
+ assertEquals(41, bug16_baseline.size(), "Bug 16 baseline: expected 41 rows")
+
+ // Test with pptn=0 (auto-parallel, triggers LocalShuffleAssignedJob)
+ for (int ppt : [0, 2, 4, 8]) {
+ def bug16_result = sql """
+ SELECT /*+SET_VAR(parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ disable_streaming_preaggregations=true)*/
+ COUNT(MIN(col_int_undef_signed) OVER())
+ FROM rqg_analytic_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed, pk), (), (pk))
+ ORDER BY 1
+ """
+ assertEquals(bug16_baseline, bug16_result,
+ "Bug 16+17 pptn=${ppt}: result mismatch with serial AnalyticEval")
+ }
+
+ // Also test with use_serial_exchange=true (makes ALL exchanges serial)
+ def bug16_serial = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,
+ parallel_pipeline_task_num=0,
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ disable_streaming_preaggregations=true)*/
+ COUNT(MIN(col_int_undef_signed) OVER())
+ FROM rqg_analytic_t1
+ GROUP BY GROUPING SETS ((col_int_undef_signed, pk), (), (pk))
+ ORDER BY 1
+ """
+ assertEquals(bug16_baseline, bug16_serial,
+ "Bug 16+17 serial_exchange: result mismatch")
+
+ logger.info("Bug 16+17: PASSED (no crash, no hang, correct results)")
+ } catch (Throwable t) {
+ logger.error("Bug 16+17 FAILED: ${t.message}")
+ assertTrue(false, "Bug 16+17: Serial AnalyticEval crash/hang: ${t.message}")
+ }
+
+ // Bug 18: DCHECK crash in Pipeline::set_num_tasks when PASSTHROUGH LE is inserted
+ // between serial NLJ and its child Exchange.
+ // Root cause: ExchangeNode.enforceAndDeriveLocalExchange wraps UNPARTITIONED serial
+ // Exchange with PASSTHROUGH LE. On BE, NLJ_PROBE (serial) sets pipeline num_tasks=1,
+ // then the LE handler's set_num_tasks(_num_instances) overrides it to N, triggering
+ // DCHECK (serial operator in pipeline with num_tasks > 1).
+ // Fix: skip PASSTHROUGH wrapping when hasSerialAncestorInPipeline is true.
+ // Query: LEFT JOIN with always-true self-ref condition (table.pk = table.pk) creates
+ // RIGHT_OUTER NLJ (serial). With pptn>1 and ignore_data_distribution, the fragment
+ // gets N instances but NLJ forces 1 task.
+ try {
+ logger.info("Bug 18: Testing serial NLJ with PASSTHROUGH LE crash")
+ // Use existing rqg_t1 table (10 rows, 10 buckets)
+ def bug18_baseline = sql """
+ SELECT /*+SET_VAR(enable_local_shuffle_planner=false,
+ enable_sql_cache=false)*/
+ table1.col_int_undef_signed AS field1
+ FROM rqg_t1 AS table1
+ LEFT JOIN rqg_t1 AS table2
+ ON table2.pk = table2.pk
+ WHERE table1.pk BETWEEN 2 AND 11
+ GROUP BY field1
+ ORDER BY 1
+ """
+
+ // Test with various pptn values — crash requires pptn > 1
+ for (int ppt : [4, 7]) {
+ def bug18_result = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=false,
+ parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ enable_share_hash_table_for_broadcast_join=false,
+ enable_broadcast_join_force_passthrough=true,
+ enable_parallel_scan=false)*/
+ table1.col_int_undef_signed AS field1
+ FROM rqg_t1 AS table1
+ LEFT JOIN rqg_t1 AS table2
+ ON table2.pk = table2.pk
+ WHERE table1.pk BETWEEN 2 AND 11
+ GROUP BY field1
+ ORDER BY 1
+ """
+ assertEquals(bug18_baseline, bug18_result,
+ "Bug 18 pptn=${ppt}: result mismatch with serial NLJ + local exchange")
+ }
+ logger.info("Bug 18: PASSED (no crash, correct results)")
+ } catch (Throwable t) {
+ logger.error("Bug 18 FAILED: ${t.message}")
+ assertTrue(false, "Bug 18: Serial NLJ PASSTHROUGH LE crash: ${t.message}")
+ }
+
+ // Bug 19: source_deps.size()=0 crash in NLJ build sink.
+ // Root cause: serial NLJ (RIGHT_OUTER) resets serial ancestor flag for build side.
+ // Exchange(UNPARTITIONED) on build side sees hasSerialAncestorInPipeline=false and
+ // inserts PASSTHROUGH LE. This restores build pipeline num_tasks to _num_instances
+ // while probe pipeline stays at 1. The extra build tasks have NLJ shared state with
+ // empty source_deps → crash in set_ready_to_read().
+ // Fix: shouldResetSerialFlagForChild(1) returns false when NLJ is serial.
+ // Differs from Bug 18 in fuzzy vars: enable_share_hash_table=true, broadcast_passthrough=false.
+ try {
+ logger.info("Bug 19: Testing serial NLJ build-side source_deps crash")
+ def bug19_baseline = sql """
+ SELECT /*+SET_VAR(enable_local_shuffle_planner=false,
+ enable_sql_cache=false)*/
+ table1.col_int_undef_signed AS field1
+ FROM rqg_t1 AS table1
+ LEFT JOIN rqg_t1 AS table2
+ ON table2.pk = table2.pk
+ WHERE table1.pk BETWEEN 2 AND 11
+ GROUP BY field1
+ ORDER BY 1
+ """
+
+ for (int ppt : [2, 4]) {
+ def bug19_result = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=false,
+ parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ enable_share_hash_table_for_broadcast_join=true,
+ enable_broadcast_join_force_passthrough=false,
+ enable_parallel_scan=true,
+ disable_streaming_preaggregations=true)*/
+ table1.col_int_undef_signed AS field1
+ FROM rqg_t1 AS table1
+ LEFT JOIN rqg_t1 AS table2
+ ON table2.pk = table2.pk
+ WHERE table1.pk BETWEEN 2 AND 11
+ GROUP BY field1
+ ORDER BY 1
+ """
+ assertEquals(bug19_baseline, bug19_result,
+ "Bug 19 pptn=${ppt}: result mismatch with serial NLJ build side crash")
+ }
+ logger.info("Bug 19: PASSED (no crash, correct results)")
+ } catch (Throwable t) {
+ logger.error("Bug 19 FAILED: ${t.message}")
+ assertTrue(false, "Bug 19: Serial NLJ build-side source_deps crash: ${t.message}")
+ }
+
+ // Bug 20: Hang (ASAN: COREDUMP source_deps.size()=0 in AggSinkOperatorX) when
+ // use_serial_exchange=true + RIGHT JOIN + GROUP BY in non-pooling fragment.
+ // Root cause: serial HASH Exchange in non-pooling fragment returned NOOP, causing FE
+ // to insert LOCAL_EXECUTION_HASH_SHUFFLE LE. On BE, serial Exchange pipeline has 1 task
+ // but LE downstream has _num_instances tasks. AggSink on instances 1+ has empty source_deps.
+ // Fix: ExchangeNode.enforceAndDeriveLocalExchange() returns actual distribution type
+ // (GLOBAL_EXECUTION_HASH_SHUFFLE/BUCKET_HASH_SHUFFLE) for serial Exchange in non-pooling
+ // fragments, preventing LE insertion.
+ // Requires 3+ BEs to reproduce (single BE has _num_instances=1, no hang).
+ try {
+ logger.info("Bug 20: Testing serial exchange + agg hang in non-pooling fragment")
+ // Baseline uses same fuzzy vars but with planner=false (BE-native).
+ // This way we compare FE-planned vs BE-native under identical conditions,
+ // not against "correct" results — use_serial_exchange=true itself may have
+ // pre-existing BE bugs with certain pptn values.
+ def bug20_baseline_sql = { int ppt -> """
+ SELECT /*+SET_VAR(use_serial_exchange=true,
+ parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=false,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true)*/
+ table1.pk AS field1
+ FROM rqg_t1 AS table1
+ RIGHT OUTER JOIN rqg_t1 AS table2 ON table1.pk = table2.pk
+ LEFT JOIN rqg_t1 AS table3 ON table3.pk = table1.pk
+ WHERE table1.col_int_undef_signed IS NOT NULL OR table1.pk <> 10
+ GROUP BY field1
+ ORDER BY 1
+ """ }
+ for (int ppt : [3, 4, 7]) {
+ def bug20_baseline = sql bug20_baseline_sql(ppt)
+ def bug20_result = sql """
+ SELECT /*+SET_VAR(use_serial_exchange=true,
+ parallel_pipeline_task_num=${ppt},
+ enable_local_shuffle_planner=true,
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ enable_share_hash_table_for_broadcast_join=false,
+ disable_streaming_preaggregations=true)*/
+ table1.pk AS field1
+ FROM rqg_t1 AS table1
+ RIGHT OUTER JOIN rqg_t1 AS table2 ON table1.pk = table2.pk
+ LEFT JOIN rqg_t1 AS table3 ON table3.pk = table1.pk
+ WHERE table1.col_int_undef_signed IS NOT NULL OR table1.pk <> 10
+ GROUP BY field1
+ ORDER BY 1
+ """
+ assertEquals(bug20_baseline, bug20_result,
+ "Bug 20 pptn=${ppt}: result mismatch with serial exchange + agg hang")
+ }
+ logger.info("Bug 20: PASSED (no hang, correct results)")
+ } catch (Throwable t) {
+ logger.error("Bug 20 FAILED: ${t.message}")
+ assertTrue(false, "Bug 20: Serial exchange + agg hang: ${t.message}")
+ }
+
+ // ============================================================
+ // Bug 21: Multi-distinct COUNT on many-bucket table → COREDUMP
+ // RQG build 186737/186929/186952: AggSinkOperatorX::sink → set_ready_to_read
+ // with empty source_deps.
+ //
+ // Root cause: AGG operators (streaming, distinct-streaming, serialize) requested
+ // PASSTHROUGH from non-ScanNode serial children (Exchange, AGG), inserting a
+ // PASSTHROUGH LE that created a pipeline split disconnecting AggSink↔AggSource
+ // shared state.
+ //
+ // Fix: restrict AGG PASSTHROUGH requests to ScanNode children only.
+ // Triggered by: multi-distinct COUNT/MIN with MultiCastDataSinks feeding
+ // serial UNPARTITIONED Exchanges into streaming AGG fragments.
+ // ============================================================
+ sql "DROP TABLE IF EXISTS rqg_t5_many_buckets"
+ sql """
+ CREATE TABLE rqg_t5_many_buckets (
+ pk INT NOT NULL,
+ col_int_undef_signed INT,
+ col_date_undef_signed DATE,
+ col_date_undef_signed2 DATE,
+ col_varchar_1024__undef_signed VARCHAR(1024)
+ ) ENGINE=OLAP
+ DUPLICATE KEY(pk)
+ DISTRIBUTED BY HASH(pk) BUCKETS 56
+ PROPERTIES ("replication_num" = "1")
+ """
+ sql """INSERT INTO rqg_t5_many_buckets VALUES
+ (1,1,'2023-12-09','2024-06-01','s1'),(2,2,'2023-03-15','2024-01-20','s2'),
+ (3,3,'2023-07-22','2024-03-10',NULL),(4,4,'2023-12-09','2024-06-01','s4'),
+ (5,5,'2023-01-05','2024-09-15','s5'),(6,6,'2023-08-11','2024-02-28','s6'),
+ (7,7,'2023-04-18','2024-07-04',NULL),(8,8,'2023-11-25','2024-05-12','s8'),
+ (9,9,'2023-06-30','2024-11-19','s9'),(10,10,'2023-02-14','2024-08-07','s10')
+ """
+
+ try {
+ logger.info("Bug 21: Testing multi-distinct COUNT on many-bucket table (COREDUMP fix)")
+ for (int ppt : [4, 6]) {
+ // Test without use_serial_exchange
+ def bug21_baseline = sql """
+ SELECT /*+SET_VAR(enable_local_shuffle_planner=false,
+ parallel_pipeline_task_num=${ppt},
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ query_timeout=60)*/
+ MIN(distinct col_date_undef_signed),
+ COUNT(distinct col_date_undef_signed2),
+ COUNT(distinct col_int_undef_signed)
+ FROM rqg_t5_many_buckets
+ WHERE col_int_undef_signed = col_int_undef_signed
+ LIMIT 1000
+ """
+ def bug21_result = sql """
+ SELECT /*+SET_VAR(enable_local_shuffle_planner=true,
+ parallel_pipeline_task_num=${ppt},
+ ignore_storage_data_distribution=true,
+ enable_sql_cache=false,
+ query_timeout=60)*/
+ MIN(distinct col_date_undef_signed),
+ COUNT(distinct col_date_undef_signed2),
+ COUNT(distinct col_int_undef_signed)
+ FROM rqg_t5_many_buckets
+ WHERE col_int_undef_signed = col_int_undef_signed
+ LIMIT 1000
+ """
+ assertEquals(bug21_baseline, bug21_result,
+ "Bug 21 pptn=${ppt}: multi-distinct COUNT result mismatch (was COREDUMP)")
+ }
+ logger.info("Bug 21: PASSED (no crash, correct results)")
+ } catch (Throwable t) {
+ if (t.message != null && t.message.contains("query timeout")) {
+ logger.warn("Bug 21: SKIPPED (query timeout - likely BE crash from previous test, not our bug)")
+ } else {
Review Comment:
This catch makes the regression pass when the query times out. Timeout/hang is one of the failure modes this case is meant to catch (the comment says this reproduced a COREDUMP/shared-state bug); if FE-planned local shuffle hangs here, CI logs `SKIPPED` and the suite succeeds. Other cases in this file fail from their `catch` blocks. Please let timeouts fail too, or skip before executing under an explicit environment predicate, so Bug 21 remains a real regression test.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4680726307
# FE UT Coverage Report
Increment line coverage ` 74.48% (429/576)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_9f33ce47e1bdfae26100cf19b96a7eab06238d13/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_9f33ce47e1bdfae26100cf19b96a7eab06238d13/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4682234726
# BE Regression && UT Coverage Report
Increment line coverage `76.83% (315/410)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_784476ead971eeebeb0b637f6da4a434e380ce36_merge/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_784476ead971eeebeb0b637f6da4a434e380ce36_merge/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 63.86% (24432/38257) |
| Line Coverage | 47.66% (253665/532266) |
| Region Coverage | 44.41% (209410/471561) |
| Branch Coverage | 45.55% (90922/199624) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3449986962
##########
fe/fe-core/src/main/java/org/apache/doris/common/profile/SummaryProfile.java:
##########
@@ -563,6 +567,10 @@ public void update(Map<String, String> summaryInfo) {
// This method is used to display the final data status when the overall query ends.
// This can avoid recalculating some strings and so on every time during the update process.
public void queryFinished() {
+ // Mark profile collection as complete. This is written last, after all BE fragment
+ // profiles have been merged (called post-waitForFragmentsDone). Pollers can wait for
+ // this field to avoid reading a partial profile.
Review Comment:
Update: after rebasing onto latest master I noticed this marker fully duplicated master's #64392 (`Profile Completion State`: RUNNING/COLLECTING/COMPLETE, wired in `Profile.java`, tested in `ProfileManagerTest`), and ours had no reader. So instead of moving it, I **removed `IS_PROFILE_COLLECTION_COMPLETED` entirely** (commit `7fdb989a3a7`) and made the profile-plan-tree test plugin poll master's `Profile Completion State: COMPLETE` instead of a fixed sleep. The ordering concern is moot now.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4765707891
Codex automated review failed and did not complete.
Error: You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at Jun 24th, 2026 10:56 PM.
Workflow run: https://github.com/apache/doris/actions/runs/27934785852
Please inspect the workflow logs and rerun the review after the underlying issue is resolved.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4670550333
<details>
<summary>TPC-DS: <b>Total hot run time: 168769 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 7c898c900506116cfb44b47189025b6fbfa7e44c, data reload: false
query5 4341 645 491 491
query6 437 199 184 184
query7 4825 533 305 305
query8 363 230 203 203
query9 8754 4043 4032 4032
query10 477 327 295 295
query11 5779 2316 2150 2150
query12 163 103 102 102
query13 1297 618 437 437
query14 6409 5407 5085 5085
query14_1 4396 4384 4424 4384
query15 209 198 190 190
query16 1016 459 446 446
query17 1150 730 608 608
query18 2489 507 378 378
query19 208 195 157 157
query20 116 108 110 108
query21 217 150 123 123
query22 13577 13564 13325 13325
query23 17449 16533 16087 16087
query23_1 16278 16241 16335 16241
query24 7489 1755 1329 1329
query24_1 1311 1324 1340 1324
query25 571 466 394 394
query26 1308 333 177 177
query27 2646 592 327 327
query28 4418 2070 2063 2063
query29 1069 609 478 478
query30 308 237 200 200
query31 1142 1071 961 961
query32 116 63 60 60
query33 527 318 251 251
query34 1185 1195 656 656
query35 766 772 686 686
query36 1393 1433 1318 1318
query37 153 102 93 93
query38 3211 3168 3031 3031
query39 917 913 902 902
query39_1 867 866 874 866
query40 229 124 104 104
query41 72 63 62 62
query42 94 103 93 93
query43 321 323 279 279
query44
query45 198 188 176 176
query46 1100 1169 718 718
query47 2384 2425 2274 2274
query48 415 424 297 297
query49 625 470 361 361
query50 1042 358 245 245
query51 4403 4426 4341 4341
query52 89 90 79 79
query53 243 270 187 187
query54 265 228 199 199
query55 79 76 82 76
query56 248 257 227 227
query57 1428 1389 1345 1345
query58 237 217 207 207
query59 1565 1656 1445 1445
query60 284 236 235 235
query61 167 159 158 158
query62 713 651 594 594
query63 232 190 185 185
query64 2570 823 629 629
query65
query66 1826 460 344 344
query67 29633 29725 28968 28968
query68
query69 415 301 271 271
query70 919 988 900 900
query71 303 227 205 205
query72 2983 2750 2406 2406
query73 840 778 439 439
query74 5125 4933 4760 4760
query75 2665 2551 2268 2268
query76 2325 1129 786 786
query77 354 392 287 287
query78 12507 12409 11876 11876
query79 1293 1008 779 779
query80 601 474 397 397
query81 454 284 240 240
query82 569 155 122 122
query83 356 273 250 250
query84
query85 911 532 445 445
query86 377 290 270 270
query87 3386 3381 3218 3218
query88 3645 2779 2754 2754
query89 420 382 332 332
query90 1999 177 182 177
query91 179 161 137 137
query92 66 62 59 59
query93 1505 1591 907 907
query94 536 358 306 306
query95 702 389 356 356
query96 1066 787 329 329
query97 2695 2704 2551 2551
query98 208 206 209 206
query99 1166 1170 1038 1038
Total cold run time: 250844 ms
Total hot run time: 168769 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4686120768
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4686649179
<details>
<summary>TPC-DS: <b>Total hot run time: 167953 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 9ccb7f30b5fb41183c99a77cc3de3311715aa9b6, data reload: false
query5 4348 619 489 489
query6 440 181 168 168
query7 4962 586 302 302
query8 359 218 202 202
query9 8772 4006 4004 4004
query10 439 298 252 252
query11 5862 2352 2163 2163
query12 157 103 96 96
query13 1273 623 459 459
query14 6392 5410 5067 5067
query14_1 4383 4392 4398 4392
query15 206 202 172 172
query16 959 470 438 438
query17 1115 705 595 595
query18 2447 477 348 348
query19 205 183 150 150
query20 111 107 105 105
query21 216 143 117 117
query22 13618 13576 13402 13402
query23 17516 16556 16200 16200
query23_1 16334 16275 16192 16192
query24 7576 1790 1294 1294
query24_1 1334 1317 1330 1317
query25 558 452 389 389
query26 1337 347 167 167
query27 2625 520 351 351
query28 4449 2030 2028 2028
query29 1140 629 485 485
query30 307 225 202 202
query31 1135 1091 953 953
query32 112 63 59 59
query33 535 325 253 253
query34 1182 1142 641 641
query35 746 776 711 711
query36 1365 1421 1206 1206
query37 155 103 89 89
query38 3214 3159 3055 3055
query39 912 916 931 916
query39_1 890 899 864 864
query40 217 119 98 98
query41 64 61 59 59
query42 92 97 93 93
query43 318 317 272 272
query44
query45 192 185 178 178
query46 1125 1232 723 723
query47 2380 2393 2267 2267
query48 415 426 304 304
query49 632 450 338 338
query50 930 347 263 263
query51 4397 4396 4315 4315
query52 89 102 77 77
query53 242 270 191 191
query54 256 213 187 187
query55 79 75 69 69
query56 227 224 202 202
query57 1437 1412 1337 1337
query58 223 214 211 211
query59 1581 1636 1414 1414
query60 291 245 222 222
query61 158 149 143 143
query62 691 643 579 579
query63 244 182 203 182
query64 2527 744 589 589
query65
query66 1793 459 338 338
query67 29713 29751 28895 28895
query68
query69 400 330 253 253
query70 930 963 942 942
query71 292 213 204 204
query72 2937 2573 2354 2354
query73 892 754 453 453
query74 5135 4988 4788 4788
query75 2638 2569 2218 2218
query76 2357 1123 752 752
query77 350 375 285 285
query78 12512 12528 11917 11917
query79 1441 1086 734 734
query80 1240 471 395 395
query81 524 282 244 244
query82 588 152 119 119
query83 323 269 246 246
query84
query85 892 502 415 415
query86 425 306 297 297
query87 3395 3382 3208 3208
query88 3583 2717 2694 2694
query89 415 383 335 335
query90 1906 175 173 173
query91 173 157 133 133
query92 64 58 50 50
query93 1489 1584 943 943
query94 710 341 289 289
query95 674 377 338 338
query96 1074 782 364 364
query97 2694 2716 2568 2568
query98 211 214 203 203
query99 1136 1163 1035 1035
Total cold run time: 251624 ms
Total hot run time: 167953 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4765420988
Codex automated review failed and did not complete.
Error: You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at Jun 24th, 2026 10:56 PM.
Workflow run: https://github.com/apache/doris/actions/runs/27933095561
Please inspect the workflow logs and rerun the review after the underlying issue is resolved.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4767805679
<details>
<summary>TPC-DS: <b>Total hot run time: 173280 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b, data reload: false
query5 4347 625 485 485
query6 436 185 171 171
query7 4838 594 316 316
query8 358 222 216 216
query9 8753 4149 4143 4143
query10 436 307 251 251
query11 5923 2352 2114 2114
query12 152 103 108 103
query13 1246 641 473 473
query14 6397 5437 5107 5107
query14_1 4441 4438 4471 4438
query15 207 199 177 177
query16 1033 468 439 439
query17 1145 719 583 583
query18 2721 489 364 364
query19 199 175 136 136
query20 107 105 102 102
query21 218 139 118 118
query22 13683 13645 13416 13416
query23 17344 16486 16229 16229
query23_1 16282 16203 16402 16203
query24 7546 1779 1315 1315
query24_1 1341 1315 1314 1314
query25 529 427 362 362
query26 1302 300 176 176
query27 2634 551 341 341
query28 4417 2050 2047 2047
query29 1047 603 473 473
query30 309 238 201 201
query31 1105 1063 969 969
query32 100 57 59 57
query33 533 314 253 253
query34 1164 1138 641 641
query35 758 792 666 666
query36 1370 1395 1241 1241
query37 154 102 89 89
query38 1892 1734 1657 1657
query39 924 905 903 903
query39_1 879 887 868 868
query40 216 129 103 103
query41 64 63 63 63
query42 87 88 85 85
query43 334 341 287 287
query44 1437 784 792 784
query45 194 183 179 179
query46 1114 1226 785 785
query47 2397 2340 2266 2266
query48 406 418 295 295
query49 617 463 375 375
query50 1023 352 258 258
query51 4406 4408 4289 4289
query52 81 85 72 72
query53 249 272 183 183
query54 270 237 217 217
query55 75 73 67 67
query56 249 232 222 222
query57 1425 1413 1305 1305
query58 245 220 218 218
query59 1636 1662 1458 1458
query60 298 264 250 250
query61 170 176 176 176
query62 696 652 591 591
query63 239 201 202 201
query64 2540 811 652 652
query65 4891 4763 4782 4763
query66 1735 501 355 355
query67 29839 29722 29680 29680
query68 3051 1538 978 978
query69 406 301 260 260
query70 1070 981 973 973
query71 293 237 213 213
query72 2878 2676 2362 2362
query73 889 756 418 418
query74 5107 4957 4759 4759
query75 2609 2592 2213 2213
query76 2313 1220 800 800
query77 358 381 282 282
query78 12497 12516 11872 11872
query79 1420 1237 724 724
query80 1276 465 383 383
query81 540 279 241 241
query82 630 155 119 119
query83 327 276 248 248
query84 259 146 117 117
query85 889 494 417 417
query86 442 290 277 277
query87 1833 1837 1799 1799
query88 3717 2794 2785 2785
query89 424 391 337 337
query90 1914 182 184 182
query91 169 161 132 132
query92 61 64 58 58
query93 1639 1554 930 930
query94 726 334 323 323
query95 688 381 440 381
query96 1045 783 361 361
query97 2714 2680 2575 2575
query98 224 208 205 205
query99 1176 1153 1023 1023
Total cold run time: 259154 ms
Total hot run time: 173280 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 merged PR #63366:
URL: https://github.com/apache/doris/pull/63366
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4552040940
# FE Regression Coverage Report
Increment line coverage ` 66.67% (476/714)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_ccfac05f6b543ebe78f973987084a8de173759c9_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_ccfac05f6b543ebe78f973987084a8de173759c9_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4490040910
<details>
<summary>TPC-H: <b>Total hot run time: 31839 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 4e05e7bfbb8aace37eb65198a1530e9325047d34, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17008 4093 4055 4055
q2 q3 10494 1424 891 891
q4 4685 481 365 365
q5 7816 2357 2142 2142
q6 267 191 146 146
q7 1026 794 642 642
q8 9380 1750 1655 1655
q9 5537 5094 5054 5054
q10 6382 2144 1768 1768
q11 429 268 244 244
q12 638 431 315 315
q13 18180 3449 2784 2784
q14 268 257 237 237
q15 q16 825 813 716 716
q17 1009 982 1006 982
q18 7125 5794 5761 5761
q19 1179 1232 1085 1085
q20 519 422 274 274
q21 5609 2650 2411 2411
q22 446 375 312 312
Total cold run time: 98822 ms
Total hot run time: 31839 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4361 4345 4290 4290
q2 q3 4528 4949 4331 4331
q4 2184 2261 1419 1419
q5 4568 4406 4368 4368
q6 311 331 177 177
q7 2301 1961 1738 1738
q8 2704 2361 2325 2325
q9 8320 7948 8070 7948
q10 4572 4454 4060 4060
q11 610 441 418 418
q12 751 745 553 553
q13 3315 3705 2988 2988
q14 292 313 297 297
q15 q16 753 755 686 686
q17 1422 1440 1554 1440
q18 8205 7423 7590 7423
q19 1195 1153 1091 1091
q20 2232 2272 1928 1928
q21 5465 4899 4652 4652
q22 549 483 401 401
Total cold run time: 58638 ms
Total hot run time: 52533 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4508592992
<details>
<summary>TPC-H: <b>Total hot run time: 31343 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 846d365a88901fa32fdf44b42b6cd04b900da259, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17644 3831 3835 3831
q2 q3 10789 1395 822 822
q4 4742 483 353 353
q5 9240 2255 2088 2088
q6 379 175 137 137
q7 975 758 633 633
q8 9721 1667 1761 1667
q9 7096 4986 5003 4986
q10 6503 2120 1824 1824
q11 431 279 239 239
q12 689 429 296 296
q13 18201 3411 2824 2824
q14 265 259 243 243
q15 q16 814 770 704 704
q17 1034 894 886 886
q18 6924 5808 5608 5608
q19 1235 1263 1135 1135
q20 536 405 262 262
q21 5735 2582 2510 2510
q22 425 354 295 295
Total cold run time: 103378 ms
Total hot run time: 31343 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4190 4102 4346 4102
q2 q3 4570 4881 4317 4317
q4 2120 2220 1410 1410
q5 4399 4304 5149 4304
q6 260 199 141 141
q7 2066 1856 1614 1614
q8 2489 2206 2175 2175
q9 8079 7922 7854 7854
q10 4549 4534 4097 4097
q11 578 411 377 377
q12 740 740 520 520
q13 3291 3544 2951 2951
q14 303 296 261 261
q15 q16 710 725 628 628
q17 1385 1484 1318 1318
q18 7821 7326 7378 7326
q19 1152 1127 1056 1056
q20 2235 2202 1932 1932
q21 5354 4664 4478 4478
q22 521 486 414 414
Total cold run time: 56812 ms
Total hot run time: 51275 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4488243884
<details>
<summary>TPC-H: <b>Total hot run time: 31328 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 9016e5a105f84231f1a535be233aaf3e595c711d, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17620 4009 3866 3866
q2 q3 10989 1467 845 845
q4 4810 476 354 354
q5 10550 2299 2104 2104
q6 382 181 139 139
q7 1020 761 642 642
q8 9603 1863 1716 1716
q9 6948 5174 4937 4937
q10 6492 2085 1794 1794
q11 428 269 245 245
q12 647 422 295 295
q13 18188 3370 2776 2776
q14 268 259 238 238
q15 q16 823 778 708 708
q17 931 931 900 900
q18 6829 5674 5604 5604
q19 1154 1225 1129 1129
q20 504 412 262 262
q21 5488 2532 2467 2467
q22 434 374 307 307
Total cold run time: 104108 ms
Total hot run time: 31328 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4246 4221 4168 4168
q2 q3 4537 4942 4338 4338
q4 2110 2242 1392 1392
q5 4393 4312 5249 4312
q6 256 194 144 144
q7 1913 1816 1568 1568
q8 2442 2154 2075 2075
q9 7958 7888 7894 7888
q10 4565 4579 4065 4065
q11 565 428 374 374
q12 738 735 518 518
q13 3221 3668 2992 2992
q14 290 298 281 281
q15 q16 722 771 666 666
q17 1355 1339 1339 1339
q18 7802 7376 6840 6840
q19 1155 1076 1078 1076
q20 2217 2227 1956 1956
q21 5329 4679 4490 4490
q22 533 449 416 416
Total cold run time: 56347 ms
Total hot run time: 50898 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3414840309
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -939,4 +1031,263 @@ private String mergeIcebergAccessPathsWithId(
}
return StringUtils.join(mergeDisplayAccessPaths, ", ");
}
+
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ ArrayList<PlanNode> newChildren = Lists.newArrayList();
+ for (int i = 0; i < children.size(); i++) {
+ Pair<PlanNode, LocalExchangeType> childOutput
+ = enforceRequire(translatorContext, children.get(i), i, LocalExchangeTypeRequire.noRequire());
+ newChildren.add(childOutput.first);
+ }
+ this.children = newChildren;
+ return Pair.of(this, LocalExchangeType.NOOP);
+ }
+
+ /**
+ * Unified framework method: propagate serial flag → recurse child → satisfy check → Layer 1 skip → insert LE.
+ * Replaces the old enforceChild/enforceChildExchange/forceEnforceChildExchange trio.
+ *
+ * <h3>Data flow</h3>
+ * <ul>
+ * <li><b>serial-ancestor flag</b> ({@link PlanTranslatorContext#hasSerialAncestorInPipeline})
+ * — flows root → leaf during traversal. Mirrors BE's
+ * {@code any_of(operators[idx..end], is_serial_operator)} check used by
+ * {@code _add_local_exchange} to skip LE insertion when an ancestor in the same
+ * pipeline is already serial. Reset at pipeline boundaries via
+ * {@link #shouldResetSerialFlagForChild}.</li>
+ * <li><b>shuffle-for-correctness flag</b>
+ * ({@link PlanTranslatorContext#hasShuffleForCorrectnessAncestor}) — also flows
+ * root → leaf. Mirrors BE's {@code _followed_by_shuffled_operator}: tells a
+ * child whether some downstream operator depends on hash distribution for
+ * correctness, so {@code SetOperationNode} can pre-shuffle union branches.</li>
+ * <li><b>return value</b> {@code Pair<PlanNode, LocalExchangeType>} — first is the
+ * (possibly LE-wrapped) child; second is the actual output distribution as
+ * observed by the parent. Caller's {@code require.satisfy(output)} decides
+ * whether more LE is needed.</li>
+ * <li><b>parent.require</b> describes the constraint on the child output —
+ * computed inside the parent's {@code enforceAndDeriveLocalExchange} per child.</li>
+ * </ul>
+ *
+ * <h3>Invariants</h3>
+ * <ul>
+ * <li>Where a serial → non-serial transition needs redistribution, framework step 3 inserts
+ * the LE (e.g. a serial source fanned out via PASSTHROUGH). This is not a hard invariant:
+ * a serial child feeding a parent that requires PASSTHROUGH / noRequire (TableFunction,
+ * NLJ, Agg) is already correct and needs no LE, so it is intentionally not validated by a
+ * post-pass.</li>
+ * <li>{@code LocalExchangeNode} itself is always non-serial — setting it serial
+ * would defeat its purpose of fanning a 1-task pipeline back to N tasks.</li>
+ * <li>For pipeline-breaking parents ({@code shouldResetSerialFlagForChild=true}),
+ * the child starts a fresh pipeline so {@code hasSerialAncestor} is reset; the
+ * node's own {@code isSerialNode()} still composes in for the child's view.</li>
+ * <li>{@code RequireHash} accepts any hash flavour; {@code RequireSpecific} demands
+ * an exact match (with the one PASSTHROUGH/ADAPTIVE_PASSTHROUGH compatibility).
+ * Pick the looser one whenever correctness allows — see
+ * {@link LocalExchangeNode.LocalExchangeTypeRequire}.</li>
+ * </ul>
+ *
+ * <h3>Layers</h3>
+ * Layer 1 (shouldSkipLE): mirrors BE's need_to_local_exchange — skip when this node or
+ * an ancestor in the same pipeline is serial (operators[idx..end] has serial → skip).
+ * Layer 2 (require/output): each Node declares require and output in enforceAndDeriveLocalExchange.
+ */
+ protected Pair<PlanNode, LocalExchangeType> enforceRequire(
+ PlanTranslatorContext translatorContext, PlanNode child, int childIndex,
+ LocalExchangeTypeRequire require) {
+ // 1. Propagate serial-ancestor flag to child.
+ // For pipeline-splitting operators (shouldReset=true, e.g. non-streaming AGG):
+ // Drop inherited serial flag from parent (parent is in a different pipeline),
+ // but keep this node's own serial status (child is in the same pipeline as this
+ // node's sink, e.g. Exchange is in AGG_Sink pipeline).
+ // For non-splitting operators (shouldReset=false, e.g. streaming AGG):
+ // Inherit parent's serial flag + this node's own.
+ boolean inheritedSerial = shouldResetSerialFlagForChild(childIndex)
+ ? false : translatorContext.hasSerialAncestorInPipeline(this);
+ // Use isSerialOperatorOnBe (= isSerialNode && fragment.useSerialSource) instead of the
+ // raw isSerialNode(). BE's OperatorBase reads the Thrift `is_serial_operator` flag —
+ // which is what FE writes via isSerialOperatorOnBe — so when the fragment is not in
+ // serial-source mode, BE treats this operator as non-serial regardless of isSerialNode.
+ // Using isSerialNode here would set the child's serial-ancestor flag wider than BE's
+ // view and over-skip required LocalExchanges downstream.
+ boolean childHasSerialAncestor = inheritedSerial
+ || isSerialOperatorOnBe(translatorContext.getConnectContext());
+ translatorContext.setHasSerialAncestorInPipeline(child, childHasSerialAncestor);
+
+ // 1b. Propagate shuffle-for-correctness-ancestor flag to child.
+ // Mirrors BE's _followed_by_shuffled_operator: a downstream operator needs hash
+ // distribution for correctness, and the chain to here goes through HASH or NOOP
+ // requirements (so the dependency is preserved).
+ // propagate = ((inheritedShuffled || self.requiresShuffleForCorrectness)
+ // && require is hash)
+ // || (inheritedShuffled && require is noop/passthrough)
+ boolean inheritedShuffled = translatorContext.hasShuffleForCorrectnessAncestor(this);
+ boolean selfOrInheritedShuffled = inheritedShuffled || requiresShuffleForCorrectness();
+ boolean requireIsHash = require.preferType().isHashShuffle();
+ boolean requireIsNoop = require.preferType() == LocalExchangeNode.LocalExchangeType.NOOP;
+ boolean childShuffledAncestor = (selfOrInheritedShuffled && requireIsHash)
+ || (inheritedShuffled && requireIsNoop);
+ translatorContext.setHasShuffleForCorrectnessAncestor(child, childShuffledAncestor);
+
+ // 2. Recurse child (Layer 2: child declares its own require/output)
+ Pair<PlanNode, LocalExchangeType> childOutput =
+ child.enforceAndDeriveLocalExchange(translatorContext, this, require);
+
+ // Steps 2.5 and 3 both react to a serial child but address different concerns:
+ // - Step 2.5 rewrites the OUTPUT-side view (what we tell satisfy/parent about
+ // the child's actual distribution). A serial pipeline runs with 1 task so
+ // its distribution claim is meaningless — flatten to NOOP so the satisfy
+ // check below doesn't get fooled by a stale "I output BUCKET_HASH" claim.
+ // - Step 3 rewrites the REQUIRE-side decision (what we want from the child).
+ // If we previously asked for nothing (noRequire) but the child turns out
+ // to be serial and we're not, upgrade to requirePassthrough so an LE is
+ // inserted to restore parallelism.
+
+ // 2.5. Serial child override (output side): if child is serial on BE, force its
+ // reported output to NOOP. Distribution is irrelevant when the child runs
+ // with 1 task; downstream parallelism is restored either by step 3 (LE
+ // insertion) or skipped entirely by step 4b (we're also serial).
+ if (childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ childOutput = Pair.of(childOutput.first, LocalExchangeType.NOOP);
+ }
+
+ // 3. Framework-level serial child check (require side, mirrors BE base class
+ // required_data_distribution): if child will be serial on BE but this node is
+ // not serial, the pipeline has a 1-task serial child feeding an N-task non-serial
+ // parent. Without LE, pipeline splits (AGG/JOIN) create paired pipelines with
+ // mismatched num_tasks → crash. Upgrade noRequire to requirePassthrough so an
+ // LE is inserted below to restore parallelism.
+ if (require instanceof LocalExchangeNode.NoRequire
+ && childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())
+ && !isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ require = LocalExchangeTypeRequire.requirePassthrough();
+ }
+
+ // 4. Satisfy check: child output meets requirement → done
+ if (require.satisfy(childOutput.second)) {
+ return childOutput;
+ }
+
+ // 4. Layer 1: skip LE when serial operator or ancestor in same pipeline
+ // Equivalent to BE's need_to_local_exchange: any_of(operators[idx..end], is_serial) → skip.
+ // Use isSerialOperatorOnBe (not isSerialNode) because BE's Pipeline::need_to_local_exchange
+ // checks op->is_serial_operator() which reads the Thrift flag set from isSerialOperatorOnBe;
+ // when fragment.useSerialSource is false, BE treats this node as non-serial.
+ if (translatorContext.hasSerialAncestorInPipeline(this)
Review Comment:
why can add no local exchange node when `hasSerialAncestorInPipeline(this)` equals true?
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4701708703
<details>
<summary>TPC-H: <b>Total hot run time: 28718 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 8db568b5100ed397fc6347033402642e90e5509e, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17706 3971 4026 3971
q2 q3 10887 1446 802 802
q4 4822 490 340 340
q5 8546 870 581 581
q6 349 170 134 134
q7 947 845 637 637
q8 10865 1468 1535 1468
q9 7051 4572 4507 4507
q10 6848 1815 1539 1539
q11 434 273 248 248
q12 644 431 292 292
q13 18112 3441 2741 2741
q14 267 258 234 234
q15 q16 823 767 707 707
q17 893 830 957 830
q18 7289 5868 5602 5602
q19 1169 1370 1081 1081
q20 511 403 261 261
q21 5603 2699 2440 2440
q22 431 363 303 303
Total cold run time: 104197 ms
Total hot run time: 28718 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4327 4269 4263 4263
q2 q3 4591 4987 4353 4353
q4 2074 2199 1380 1380
q5 4479 4321 4363 4321
q6 228 171 126 126
q7 2340 1904 1636 1636
q8 2462 2203 2148 2148
q9 8165 8102 8210 8102
q10 4792 4759 4563 4563
q11 582 424 385 385
q12 763 750 530 530
q13 3236 3729 2948 2948
q14 318 323 283 283
q15 q16 719 734 644 644
q17 1353 1353 1332 1332
q18 7985 7200 6958 6958
q19 1130 1086 1131 1086
q20 2252 2234 2011 2011
q21 5338 4588 4455 4455
q22 529 471 415 415
Total cold run time: 57663 ms
Total hot run time: 51939 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3413561854
##########
be/src/exec/pipeline/dependency.h:
##########
@@ -832,50 +832,44 @@ struct SetSharedState : public BasicSharedState {
Status hash_table_init();
};
-enum class ExchangeType : uint8_t {
- NOOP = 0,
- // Shuffle data by Crc32CHashPartitioner
- HASH_SHUFFLE = 1,
- // Round-robin passthrough data blocks.
- PASSTHROUGH = 2,
- // Shuffle data by Crc32HashPartitioner<ShuffleChannelIds> (e.g. same as storage engine).
- BUCKET_HASH_SHUFFLE = 3,
- // Passthrough data blocks to all channels.
- BROADCAST = 4,
- // Passthrough data to channels evenly in an adaptive way.
- ADAPTIVE_PASSTHROUGH = 5,
- // Send all data to the first channel.
- PASS_TO_ONE = 6,
-};
+inline bool is_shuffled_exchange(TLocalPartitionType::type idx) {
+ return idx == TLocalPartitionType::GLOBAL_EXECUTION_HASH_SHUFFLE ||
+ idx == TLocalPartitionType::LOCAL_EXECUTION_HASH_SHUFFLE ||
+ idx == TLocalPartitionType::BUCKET_HASH_SHUFFLE;
+}
Review Comment:
You're right, and my earlier reply was wrong — `OperatorBase::is_hash_shuffle` (`operator.cpp:150`) is the exact same predicate. I only grepped the name `is_shuffled_exchange` and missed it; both were added in this PR. Deduped in `832ba5f0bf8`: removed `is_hash_shuffle` and routed its single caller (`child_breaks_local_key_distribution`) to the free `is_shuffled_exchange` (operator.cpp already includes dependency.h). Thanks for catching it.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4708905260
<details>
<summary>TPC-DS: <b>Total hot run time: 167932 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 832ba5f0bf8ab5846bb316e94f1099ae58fe466f, data reload: false
query5 4317 637 480 480
query6 465 189 166 166
query7 4835 535 300 300
query8 386 225 199 199
query9 8757 4047 4051 4047
query10 460 312 259 259
query11 5882 2365 2139 2139
query12 153 100 124 100
query13 1235 587 390 390
query14 6412 5348 4992 4992
query14_1 4358 4357 4307 4307
query15 207 199 174 174
query16 986 446 443 443
query17 956 681 556 556
query18 2426 474 333 333
query19 199 178 136 136
query20 112 107 104 104
query21 220 144 117 117
query22 13585 13585 13318 13318
query23 17361 16549 16126 16126
query23_1 16346 16289 16267 16267
query24 7555 1834 1319 1319
query24_1 1336 1342 1333 1333
query25 572 459 411 411
query26 1303 327 171 171
query27 2756 572 328 328
query28 4517 2032 2065 2032
query29 1152 661 500 500
query30 327 235 202 202
query31 1118 1077 970 970
query32 113 62 62 62
query33 546 327 285 285
query34 1205 1170 638 638
query35 757 780 696 696
query36 1394 1400 1207 1207
query37 160 110 91 91
query38 3193 3141 3090 3090
query39 941 922 910 910
query39_1 878 892 876 876
query40 222 130 104 104
query41 68 67 67 67
query42 100 98 94 94
query43 321 325 285 285
query44
query45 203 191 181 181
query46 1089 1231 776 776
query47 2409 2407 2320 2320
query48 415 413 308 308
query49 652 486 363 363
query50 1018 369 262 262
query51 4655 4360 4344 4344
query52 95 92 78 78
query53 248 282 191 191
query54 286 232 225 225
query55 80 79 77 77
query56 245 243 245 243
query57 1424 1413 1301 1301
query58 266 229 212 212
query59 1598 1645 1442 1442
query60 351 242 232 232
query61 153 142 160 142
query62 710 658 598 598
query63 233 189 190 189
query64 2606 763 599 599
query65
query66 1811 450 347 347
query67 29701 29638 28939 28939
query68
query69 433 312 268 268
query70 1039 962 900 900
query71 296 230 209 209
query72 2911 2613 2339 2339
query73 852 746 442 442
query74 5123 4929 4784 4784
query75 2661 2565 2213 2213
query76 2312 1162 792 792
query77 368 367 290 290
query78 12372 12600 11743 11743
query79 1516 1068 810 810
query80 616 471 391 391
query81 467 284 249 249
query82 635 160 120 120
query83 342 291 253 253
query84
query85 924 545 442 442
query86 383 292 289 289
query87 3423 3361 3187 3187
query88 3729 2773 2733 2733
query89 438 388 329 329
query90 1965 183 179 179
query91 170 158 133 133
query92 63 62 57 57
query93 1495 1437 892 892
query94 617 338 293 293
query95 687 473 334 334
query96 1072 774 329 329
query97 2696 2719 2574 2574
query98 219 215 205 205
query99 1136 1166 1007 1007
Total cold run time: 251786 ms
Total hot run time: 167932 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4701410489
/review
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4708537624
# FE UT Coverage Report
Increment line coverage ` 74.96% (428/571)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_fe9a3316be056faed18351cf64096e36790c6c89/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_fe9a3316be056faed18351cf64096e36790c6c89/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4706824885
# FE UT Coverage Report
Increment line coverage ` 74.65% (430/576)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_1d7cf6dbd6095ee1fb5860efe258b0ce99242ae6/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_1d7cf6dbd6095ee1fb5860efe258b0ce99242ae6/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4738331130
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/0436cc4cb573b104a59408a6169bd145bcfeff6c_0436cc4cb573b104a59408a6169bd145bcfeff6c_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/0436cc4cb573b104a59408a6169bd145bcfeff6c_0436cc4cb573b104a59408a6169bd145bcfeff6c_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 77.32% (1889/2443) |
| Line Coverage | 64.43% (33970/52725) |
| Region Coverage | 64.80% (17461/26948) |
| Branch Coverage | 53.98% (9347/17316) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4739601944
# BE Regression && UT Coverage Report
Increment line coverage `76.60% (311/406)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_0436cc4cb573b104a59408a6169bd145bcfeff6c_merge/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_0436cc4cb573b104a59408a6169bd145bcfeff6c_merge/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 64.09% (24560/38322) |
| Line Coverage | 47.88% (255578/533815) |
| Region Coverage | 44.59% (210879/472979) |
| Branch Coverage | 45.63% (91504/200514) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4737259524
/review
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4731455457
/review
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3372721760
##########
fe/fe-core/src/main/java/org/apache/doris/planner/SetOperationNode.java:
##########
@@ -193,4 +198,52 @@ public int getNumInstances() {
public boolean isBucketShuffle() {
return distributionMode.equals(DistributionMode.BUCKET_SHUFFLE);
}
+
+ public boolean isColocate() {
+ return isColocate;
+ }
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(PlanTranslatorContext translatorContext,
+ PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ LocalExchangeTypeRequire requireChild;
+ LocalExchangeType outputType;
+ PlanNode firstChild = children.isEmpty() ? null : children.get(0);
+ if (this instanceof UnionNode) {
+ // Propagate parent's hash requirement to children ONLY when a downstream operator
+ // requires shuffle for correctness (not just performance optimization). Matches BE's
Review Comment:
**🟡 Minor**: When `children.isEmpty()` (constant-output union), `firstChild` is `null`. It's then passed to `resolveExchangeType()` — safe today because `child` is unused, but fragile if that method ever starts using the parameter. Consider adding a guard or a comment noting that `resolveExchangeType` does not dereference `child`.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AddLocalExchange.java:
##########
@@ -0,0 +1,182 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.planner;
+
+import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.glue.translator.PlanTranslatorContext;
+import org.apache.doris.nereids.trees.plans.distribute.DistributedPlan;
+import org.apache.doris.nereids.trees.plans.distribute.FragmentIdMapping;
+import org.apache.doris.nereids.trees.plans.distribute.PipelineDistributedPlan;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeType;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeTypeRequire;
+import org.apache.doris.planner.LocalExchangeNode.RequireHash;
+
+/**
+ * FE-side local exchange planner — inserts {@link LocalExchangeNode} into each fragment's
+ * plan tree so that within-fragment data redistribution is decided at planning time
+ * instead of at BE pipeline-build time.
+ *
+ * <h3>When this runs</h3>
+ * Invoked from {@code NereidsPlanner.addLocalExchangeAfterDistribute()} right after
+ * {@code DistributePlanner} has assigned instances to fragments and before the plan is
+ * serialized to BE. Gated by session variable {@code enable_local_shuffle_planner}
+ * (default true) and {@code enable_local_shuffle}; when either is off this pass is
+ * skipped entirely and BE falls back to its own {@code _plan_local_exchange}. The two
+ * paths are mutually exclusive: BE consults {@code runtime_state.h::plan_local_shuffle()}
+ * to know whether it should plan LE itself.
+ *
+ * <h3>What it changes</h3>
+ * <ul>
+ * <li>For each fragment with {@code maxPerBeInstances > 1}, walks the plan tree
+ * bottom-up via {@link PlanNode#enforceAndDeriveLocalExchange} and inserts
+ * LocalExchangeNodes where children's output distribution doesn't satisfy the
+ * parent's requirement.</li>
+ * <li>May wrap the fragment root with an extra PASSTHROUGH LE so the data sink
+ * (DataStreamSink / OlapTableSink) runs with the full instance count even when
+ * the root operator is serial — see {@link #addLocalExchangeForFragment}.</li>
+ * <li>Does NOT modify the fragment sink itself, fragment boundaries, or instance
+ * assignment.</li>
+ * </ul>
+ *
+ * <h3>Per-BE instance semantics</h3>
+ * Skips fragments where every BE has at most 1 instance. Using a global instance count
+ * would insert LE for "2 BEs × 1 instance" cases, which BE's own
+ * {@code _plan_local_exchange} would not — leading to pipeline task-count mismatch and
+ * deadlock. See {@link #addLocalExchange}.
+ *
+ * <h3>Reading order</h3>
+ * Start with {@link PlanNode#enforceRequire} (the recursion engine), then individual
+ * {@code enforceAndDeriveLocalExchange} overrides on PlanNode subclasses.
+ */
+public class AddLocalExchange {
+ /** addLocalExchange with distributed plans, skipping single-instance fragments.
+ * BE's _plan_local_exchange checks _num_instances which is the per-BE instance count.
+ * With _num_instances<=1 all pipelines on that BE have 1 task so local exchange is a no-op.
+ * We must use the same per-BE semantics: skip when every BE has at most 1 instance.
+ * Using global instanceCount would insert LE for fragments where 2 BEs each have 1 instance
+ * (global=2, per-BE=1), causing pipeline task mismatch and deadlock. */
+ public void addLocalExchange(FragmentIdMapping<DistributedPlan> distributedPlans,
+ PlanTranslatorContext context) {
+ for (DistributedPlan plan : distributedPlans.values()) {
+ PipelineDistributedPlan pipePlan = (PipelineDistributedPlan) plan;
+ long maxPerBeInstances = pipePlan.getInstanceJobs().stream()
+ .collect(java.util.stream.Collectors.groupingBy(
+ j -> j.getAssignedWorker().id(), java.util.stream.Collectors.counting()))
+ .values().stream().mapToLong(Long::longValue).max().orElse(0);
+ if (maxPerBeInstances <= 1) {
+ continue;
+ }
+ PlanFragment fragment = pipePlan.getFragmentJob().getFragment();
+ addLocalExchangeForFragment(fragment, context);
+ }
+ }
+
+ private void addLocalExchangeForFragment(PlanFragment fragment, PlanTranslatorContext context) {
+ DataSink sink = fragment.getSink();
+ LocalExchangeTypeRequire require = sink == null
+ ? LocalExchangeTypeRequire.noRequire() : sink.getLocalExchangeTypeRequire();
+ PlanNode root = fragment.getPlanRoot();
+ context.setHasSerialAncestorInPipeline(root, false);
+ Pair<PlanNode, LocalExchangeType> output = root
+ .enforceAndDeriveLocalExchange(context, null, require);
+ PlanNode newRoot = output.first;
+ // The fragment data sink (DataStreamSink, OlapTableSink) runs in the same pipeline
+ // as the root. If the root will be serial on BE, the sink pipeline has 1 task —
+ // only instance 0 sends data, others hang or miss writes.
+ // Insert PASSTHROUGH fan-out so sink runs with _num_instances tasks.
+ // This matches BE-native's default required_data_distribution():
+ // _child->is_serial_operator() ? PASSTHROUGH : NOOP
+ if (newRoot.isSerialOperatorOnBe(context.getConnectContext())) {
+ newRoot = new LocalExchangeNode(context.nextPlanNodeId(), newRoot,
+ LocalExchangeType.PASSTHROUGH, null);
+ }
+ if (newRoot != root) {
+ fragment.setPlanRoot(newRoot);
+ }
+ validateNoSerialWithoutLocalExchange(fragment.getPlanRoot(), context.getConnectContext());
+ }
+
+ /**
+ * In a local-shuffle fragment, the root check above guarantees the root pipeline
+ * has N tasks. Any serial operator reduces its pipeline to 1 task. If this serial
+ * operator feeds into a non-serial parent without LocalExchangeNode in between,
+ * some pipelines have 1 task while others have N → shared_state mismatch, data loss.
+ *
+ * Serial→serial chains are fine (all at 1 task, consistent). Only the transition
+ * from serial to non-serial needs LE to restore parallelism.
+ */
+ private void validateNoSerialWithoutLocalExchange(PlanNode node,
+ org.apache.doris.qe.ConnectContext context) {
+ for (PlanNode child : node.getChildren()) {
+ validateNoSerialWithoutLocalExchange(child, context);
+ if (child.isSerialOperatorOnBe(context)
+ && !(child instanceof LocalExchangeNode)
+ && !(node instanceof LocalExchangeNode)
+ && !(node instanceof ExchangeNode)
+ && !node.isSerialOperatorOnBe(context)) {
+ org.apache.logging.log4j.LogManager.getLogger(AddLocalExchange.class).warn(
+ "Serial " + child.getClass().getSimpleName() + "(id=" + child.getId()
+ + ") feeds into non-serial " + node.getClass().getSimpleName()
+ + "(id=" + node.getId() + ") without LocalExchangeNode"
+ + " in fragment " + node.getFragment().getFragmentId()
+ + ". FE should insert LocalExchangeNode to restore parallelism.");
+ }
+ }
+ }
+
+ public static boolean isColocated(PlanNode plan) {
+ if (plan instanceof AggregationNode) {
+ return ((AggregationNode) plan).isColocate() && isColocated(plan.getChild(0));
+ } else if (plan instanceof OlapScanNode) {
+ return true;
+ } else if (plan instanceof SelectNode) {
+ return isColocated(plan.getChild(0));
+ } else if (plan instanceof HashJoinNode) {
+ return ((HashJoinNode) plan).isColocate()
+ && (isColocated(plan.getChild(0)) || isColocated(plan.getChild(1)));
+ } else if (plan instanceof SetOperationNode) {
+ if (!((SetOperationNode) plan).isColocate()) {
+ return false;
+ }
+ for (PlanNode child : plan.getChildren()) {
+ if (isColocated(child)) {
+ return true;
+ }
Review Comment:
**🟡 Minor**: `resolveExchangeType()` accepts a `child` parameter that is never used. This is confusing at call sites — e.g., `SetOperationNode` passes a potentially-null `firstChild` (when `children.isEmpty()`, line 214). It's safe today because `child` is unused, but if someone later adds code that accesses `child`, it could NPE on empty-children Union nodes. Consider removing the unused parameter.
##########
be/src/exec/pipeline/pipeline_fragment_context.h:
##########
@@ -320,8 +322,11 @@ class PipelineFragmentContext : public TaskExecutionContext {
std::mutex _state_map_lock;
- int _operator_id = 0;
- int _sink_operator_id = 0;
+ // Start from -1 so all operator IDs are negative. This avoids collision with
Review Comment:
**🟢 Note**: `_operator_id` and `_sink_operator_id` now start from `-1` instead of `0` to avoid collision with unpaired sinks (e.g., `OlapTableSink`) that hardcode `dest_id=0`. This is a clean fix. Verify that no code path in the BE enumerates or displays operator IDs expecting them to be non-negative — negative IDs in logs or profiles could confuse debugging.
##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -690,6 +716,199 @@ Status PipelineFragmentContext::_build_pipelines(ObjectPool* pool, const Descrip
return Status::OK();
}
+Status PipelineFragmentContext::_create_deferred_local_exchangers() {
+ for (auto& info : _deferred_exchangers) {
+ // DANGER ZONE — do not "fix" this line without reading the history.
+ //
+ // sender_count seeds Exchanger::_running_sink_operators, which the source side
+ // waits to reach 0 via sub_running_sink_operators on each sink LocalState close.
+ // The correct value is THIS pipeline-instance's sink task count, which is exactly
+ // info.upstream_pipe->num_tasks() — one PipelineTask per task, one close per task.
+ //
+ // Tempting wrong fix #1: `std::max(num_tasks, _num_instances)` to mirror the
+ // BE-planned path in _add_local_exchange_impl (~line 1023). THIS BREAKS the
+ // common FE-planned shape of `serial scan → LE(PT) → ...`: upstream_pipe
+ // genuinely has num_tasks=1, only 1 close arrives, but seed becomes
+ // _num_instances so _running_sink_operators never reaches 0 — downstream
+ // sources hang on SHUFFLE_DATA_DEPENDENCY (e.g. MTMV refresh from
+ // mtmv_up_down_job_p0/load.groovy stays at Status=RUNNING; build 949402
+ // regressed exactly this way). BE-planned mode uses max() because its
+ // `cur_pipe` is the source-side pipeline (always raised to _num_instances by
+ // add_pipeline) — not analogous to our `upstream_pipe` here, which is the
+ // sink-side pipeline that may legitimately stay at 1 for serial sources.
+ //
+ // Tempting wrong fix #2: multiply by _num_instances on the theory shared_state
+ // is shared across all instances. Same hang — each fragment-instance
+ // PipelineFragmentContext has its OWN _op_id_to_shared_state map, so the
+ // exchanger is per-instance, not per-BE. num_tasks() is already the right
+ // close-count for one instance.
+ //
+ // If a hang shows up with `_running_sink_operators < 0`, the bug is upstream:
+ // _propagate_local_exchange_num_tasks left num_tasks too low (or too high) for
+ // this fragment shape. Fix THAT pass, not this seed value.
+ const int sender_count = info.upstream_pipe->num_tasks();
Review Comment:
**🟠 Medium**: The two-pass `while (changed)` loops in `_propagate_local_exchange_num_tasks()` rely on monotonic convergence (num_tasks only increases in Pass 1, only decreases in Pass 2). If a future code change introduces a non-monotonic update, this could loop infinitely. Consider adding a max-iteration guard (`while (changed && ++iter_count < MAX_ITERATIONS)`) with a `DCHECK` or `LOG(WARNING)` on exceeded iterations, so a future regression fails loudly rather than hanging.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AddLocalExchange.java:
##########
@@ -0,0 +1,182 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.planner;
+
+import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.glue.translator.PlanTranslatorContext;
+import org.apache.doris.nereids.trees.plans.distribute.DistributedPlan;
+import org.apache.doris.nereids.trees.plans.distribute.FragmentIdMapping;
+import org.apache.doris.nereids.trees.plans.distribute.PipelineDistributedPlan;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeType;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeTypeRequire;
+import org.apache.doris.planner.LocalExchangeNode.RequireHash;
+
+/**
+ * FE-side local exchange planner — inserts {@link LocalExchangeNode} into each fragment's
+ * plan tree so that within-fragment data redistribution is decided at planning time
+ * instead of at BE pipeline-build time.
+ *
+ * <h3>When this runs</h3>
+ * Invoked from {@code NereidsPlanner.addLocalExchangeAfterDistribute()} right after
+ * {@code DistributePlanner} has assigned instances to fragments and before the plan is
+ * serialized to BE. Gated by session variable {@code enable_local_shuffle_planner}
+ * (default true) and {@code enable_local_shuffle}; when either is off this pass is
+ * skipped entirely and BE falls back to its own {@code _plan_local_exchange}. The two
+ * paths are mutually exclusive: BE consults {@code runtime_state.h::plan_local_shuffle()}
+ * to know whether it should plan LE itself.
+ *
+ * <h3>What it changes</h3>
+ * <ul>
+ * <li>For each fragment with {@code maxPerBeInstances > 1}, walks the plan tree
+ * bottom-up via {@link PlanNode#enforceAndDeriveLocalExchange} and inserts
+ * LocalExchangeNodes where children's output distribution doesn't satisfy the
+ * parent's requirement.</li>
+ * <li>May wrap the fragment root with an extra PASSTHROUGH LE so the data sink
+ * (DataStreamSink / OlapTableSink) runs with the full instance count even when
+ * the root operator is serial — see {@link #addLocalExchangeForFragment}.</li>
+ * <li>Does NOT modify the fragment sink itself, fragment boundaries, or instance
+ * assignment.</li>
+ * </ul>
+ *
+ * <h3>Per-BE instance semantics</h3>
+ * Skips fragments where every BE has at most 1 instance. Using a global instance count
+ * would insert LE for "2 BEs × 1 instance" cases, which BE's own
+ * {@code _plan_local_exchange} would not — leading to pipeline task-count mismatch and
+ * deadlock. See {@link #addLocalExchange}.
+ *
+ * <h3>Reading order</h3>
+ * Start with {@link PlanNode#enforceRequire} (the recursion engine), then individual
+ * {@code enforceAndDeriveLocalExchange} overrides on PlanNode subclasses.
+ */
+public class AddLocalExchange {
+ /** addLocalExchange with distributed plans, skipping single-instance fragments.
+ * BE's _plan_local_exchange checks _num_instances which is the per-BE instance count.
+ * With _num_instances<=1 all pipelines on that BE have 1 task so local exchange is a no-op.
+ * We must use the same per-BE semantics: skip when every BE has at most 1 instance.
+ * Using global instanceCount would insert LE for fragments where 2 BEs each have 1 instance
+ * (global=2, per-BE=1), causing pipeline task mismatch and deadlock. */
+ public void addLocalExchange(FragmentIdMapping<DistributedPlan> distributedPlans,
+ PlanTranslatorContext context) {
+ for (DistributedPlan plan : distributedPlans.values()) {
+ PipelineDistributedPlan pipePlan = (PipelineDistributedPlan) plan;
+ long maxPerBeInstances = pipePlan.getInstanceJobs().stream()
+ .collect(java.util.stream.Collectors.groupingBy(
+ j -> j.getAssignedWorker().id(), java.util.stream.Collectors.counting()))
+ .values().stream().mapToLong(Long::longValue).max().orElse(0);
+ if (maxPerBeInstances <= 1) {
+ continue;
+ }
+ PlanFragment fragment = pipePlan.getFragmentJob().getFragment();
+ addLocalExchangeForFragment(fragment, context);
+ }
+ }
+
+ private void addLocalExchangeForFragment(PlanFragment fragment, PlanTranslatorContext context) {
+ DataSink sink = fragment.getSink();
+ LocalExchangeTypeRequire require = sink == null
+ ? LocalExchangeTypeRequire.noRequire() : sink.getLocalExchangeTypeRequire();
+ PlanNode root = fragment.getPlanRoot();
+ context.setHasSerialAncestorInPipeline(root, false);
+ Pair<PlanNode, LocalExchangeType> output = root
+ .enforceAndDeriveLocalExchange(context, null, require);
+ PlanNode newRoot = output.first;
+ // The fragment data sink (DataStreamSink, OlapTableSink) runs in the same pipeline
+ // as the root. If the root will be serial on BE, the sink pipeline has 1 task —
+ // only instance 0 sends data, others hang or miss writes.
+ // Insert PASSTHROUGH fan-out so sink runs with _num_instances tasks.
+ // This matches BE-native's default required_data_distribution():
+ // _child->is_serial_operator() ? PASSTHROUGH : NOOP
+ if (newRoot.isSerialOperatorOnBe(context.getConnectContext())) {
+ newRoot = new LocalExchangeNode(context.nextPlanNodeId(), newRoot,
+ LocalExchangeType.PASSTHROUGH, null);
+ }
+ if (newRoot != root) {
+ fragment.setPlanRoot(newRoot);
+ }
+ validateNoSerialWithoutLocalExchange(fragment.getPlanRoot(), context.getConnectContext());
+ }
+
+ /**
+ * In a local-shuffle fragment, the root check above guarantees the root pipeline
+ * has N tasks. Any serial operator reduces its pipeline to 1 task. If this serial
+ * operator feeds into a non-serial parent without LocalExchangeNode in between,
+ * some pipelines have 1 task while others have N → shared_state mismatch, data loss.
+ *
+ * Serial→serial chains are fine (all at 1 task, consistent). Only the transition
+ * from serial to non-serial needs LE to restore parallelism.
+ */
+ private void validateNoSerialWithoutLocalExchange(PlanNode node,
+ org.apache.doris.qe.ConnectContext context) {
+ for (PlanNode child : node.getChildren()) {
+ validateNoSerialWithoutLocalExchange(child, context);
+ if (child.isSerialOperatorOnBe(context)
+ && !(child instanceof LocalExchangeNode)
Review Comment:
**🟡 Observation**: `validateNoSerialWithoutLocalExchange()` only logs a warning when the invariant is violated — it does not throw. If a serial→non-serial transition occurs without an LE in between, queries could produce wrong results silently. Consider: (1) in debug/test builds, throw or DCHECK; (2) add a metric counter so operators can monitor this in production before it causes user-visible issues.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AggregationNode.java:
##########
@@ -258,4 +268,155 @@ public boolean isQueryCacheCandidate() {
public void setQueryCacheCandidate(boolean queryCacheCandidate) {
this.queryCacheCandidate = queryCacheCandidate;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+
+ ConnectContext connectContext = translatorContext.getConnectContext();
+ SessionVariable sessionVariable = connectContext.getSessionVariable();
+ // PR #62438: when false, non-finalize agg falls back to BE base class.
+ boolean enableLeBeforeAgg = sessionVariable.enableLocalExchangeBeforeAgg;
+ boolean hasKeys = !aggInfo.getGroupingExprs().isEmpty();
Review Comment:
**✅ Correctness fix**: The `!needsFinalize && !aggInfo.isMerge() && !enableLeBeforeAgg` guard correctly separates FIRST_MERGE (correctness-required, always HASH) from LOCAL phase (performance-only, flag controls). This diverges from BE's `!_needs_finalize && !enable_local_exchange_before_agg` early-return which conflates both intents — the FE planner is actually *more correct* here than BE. For the backward-compat path (legacy BE planner), the bug DORIS-25413 remains; consider adding a comment noting this divergence explicitly.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/HashJoinNode.java:
##########
@@ -285,4 +302,71 @@ public List<Expr> getOtherJoinConjuncts() {
public List<Expr> getMarkJoinConjuncts() {
return markJoinConjuncts;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+
+ LocalExchangeTypeRequire probeSideRequire;
+ LocalExchangeTypeRequire buildSideRequire;
+ LocalExchangeType outputType = null;
+
+ if (joinOp == JoinOperator.NULL_AWARE_LEFT_ANTI_JOIN) {
+ buildSideRequire = probeSideRequire = LocalExchangeTypeRequire.noRequire();
+ outputType = LocalExchangeType.NOOP;
+ } else if (distrMode == DistributionMode.BROADCAST) {
+ // BE: _child->is_serial_operator() ? PASSTHROUGH/PASS_TO_ONE : NOOP
+ boolean probeChildSerial = children.get(0).isSerialOperatorOnBe(
+ translatorContext.getConnectContext());
+ boolean buildChildSerial = children.get(1).isSerialOperatorOnBe(
+ translatorContext.getConnectContext());
+ probeSideRequire = probeChildSerial
+ ? LocalExchangeTypeRequire.requirePassthrough()
+ : LocalExchangeTypeRequire.noRequire();
+ buildSideRequire = buildChildSerial
+ ? LocalExchangeTypeRequire.requirePassToOne()
+ : LocalExchangeTypeRequire.noRequire();
+ // For serial probe: output is PASSTHROUGH (data from single instance).
+ // For non-serial probe: propagate probe side's actual distribution.
+ outputType = probeChildSerial ? LocalExchangeType.PASSTHROUGH : null;
+ } else if (isColocate() || isBucketShuffle()) {
+ probeSideRequire = LocalExchangeTypeRequire.requireBucketHash();
+ // For BUCKET_SHUFFLE with serial build child: use requireBucketHash() (not
+ // requirePassToOne()). Unlike BROADCAST joins, BUCKET_SHUFFLE has no shared
+ // hash table mechanism — PASS_TO_ONE routes all data to task 0 while tasks 1..N-1
+ // build empty hash tables, losing rows. BUCKET_HASH_SHUFFLE correctly distributes
+ // build data by bucket to match the probe side's bucket distribution.
+ // The serial exchange returns NOOP, so enforceRequire() will insert a
+ // BUCKET_HASH_SHUFFLE local exchange (with PASSTHROUGH fan-out for heavy-ops
Review Comment:
**🟡 Clarification**: For Colocate/BucketShuffle join, both probe and build sides use `requireBucketHash()`. For a serial build child (e.g., BucketShuffle join where the build Exchange is serial), the framework will insert `PASSTHROUGH → BUCKET_HASH_SHUFFLE` chain via the heavy-ops gate. This is correct but the comment only mentions the probe side — consider adding a brief note about the build side's serial handling too.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4765651549
# FE UT Coverage Report
Increment line coverage ` 74.69% (419/561)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_7fdb989a3a7e4ad0c700539a335598f4978f6938/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_7fdb989a3a7e4ad0c700539a335598f4978f6938/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3450954662
##########
fe/fe-core/src/main/java/org/apache/doris/planner/RepeatNode.java:
##########
@@ -86,11 +90,30 @@ public String getNodeExplainString(String detailPrefix, TExplainLevel detailLeve
// Determined by its child.
@Override
- public boolean isSerialOperator() {
- return children.get(0).isSerialOperator();
+ public boolean isSerialNode() {
+ return children.get(0).isSerialNode();
}
public GroupingInfo getGroupingInfo() {
return groupingInfo;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ // REPEAT (rollup/grouping sets) is NOT distribution-preserving: it NULLs grouping
+ // columns per set and produces GROUPING_ID, which is part of the downstream agg hash
+ // key but does not exist below the repeat. Forwarding the parent HASH require down
+ // would push the local exchange before the row expansion AND hash by the child
+ // distribution (a single upstream shuffle key) instead of the agg grouping_exprs,
+ // collapsing rows onto one instance (tpcds q67, +73%). Recurse with noRequire so the
+ // parent inserts its hash local exchange ABOVE the repeat using its own grouping_exprs
+ // (mirrors BE, whose RepeatOperatorX has a NOOP required_data_distribution).
+ Pair<PlanNode, LocalExchangeType> enforceResult
+ = enforceRequire(translatorContext, children.get(0), 0,
+ LocalExchangeTypeRequire.noRequire());
+ children = new java.util.ArrayList<>();
+ children.add(enforceResult.first);
Review Comment:
Repeat is not distribution-preserving here, but this return value forwards the child's distribution to the parent. A reduced failing shape is:
```text
Aggregation(requireHash by post-repeat grouping exprs/grouping id)
Repeat
child(output already BUCKET_HASH_SHUFFLE or GLOBAL_EXECUTION_HASH_SHUFFLE)
```
The method intentionally recurses with `noRequire()` so the parent hash requirement is not pushed below the row-expanding `RepeatNode`. However, if the child already reports a hash distribution, `enforceResult.second` is hash, and the parent `enforceRequire(..., requireHash())` can treat the Repeat output as satisfying its requirement and skip the local exchange above Repeat. Rows are then partitioned by pre-repeat child keys instead of the post-repeat grouping keys/grouping id. Please make Repeat report `NOOP` upward so any parent hash requirement is enforced above it, and add a hash-child unit case because the current test only covers a NOOP child.
```suggestion
return Pair.of(this, LocalExchangeType.NOOP);
```
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4767800324
<details>
<summary>TPC-DS: <b>Total hot run time: 173044 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 7fdb989a3a7e4ad0c700539a335598f4978f6938, data reload: false
query5 4290 624 485 485
query6 428 195 173 173
query7 4814 526 288 288
query8 357 214 208 208
query9 8767 4037 4086 4037
query10 459 300 259 259
query11 5974 2319 2127 2127
query12 156 105 97 97
query13 1341 610 430 430
query14 6378 5424 5085 5085
query14_1 4384 4358 4352 4352
query15 203 194 178 178
query16 1012 446 450 446
query17 1093 709 555 555
query18 2442 461 327 327
query19 201 189 138 138
query20 115 110 102 102
query21 221 137 116 116
query22 13736 13536 13457 13457
query23 17407 16475 16167 16167
query23_1 16299 16258 16263 16258
query24 7643 1741 1321 1321
query24_1 1332 1323 1293 1293
query25 544 436 365 365
query26 1304 335 171 171
query27 2698 531 346 346
query28 4493 2054 2031 2031
query29 1078 629 526 526
query30 319 247 196 196
query31 1123 1073 967 967
query32 110 65 70 65
query33 539 324 263 263
query34 1159 1179 661 661
query35 791 781 688 688
query36 1386 1411 1220 1220
query37 155 106 89 89
query38 1906 1737 1678 1678
query39 926 919 893 893
query39_1 873 872 900 872
query40 225 124 106 106
query41 70 68 68 68
query42 88 87 88 87
query43 320 330 279 279
query44 1457 773 785 773
query45 199 191 179 179
query46 1063 1224 786 786
query47 2351 2346 2224 2224
query48 382 467 298 298
query49 621 451 350 350
query50 995 353 254 254
query51 4389 4497 4339 4339
query52 80 80 68 68
query53 245 270 201 201
query54 261 215 188 188
query55 74 75 67 67
query56 222 234 228 228
query57 1441 1411 1301 1301
query58 237 212 206 206
query59 1616 1653 1393 1393
query60 268 248 228 228
query61 159 147 152 147
query62 706 647 578 578
query63 224 190 201 190
query64 2533 791 645 645
query65 4899 4806 4796 4796
query66 1785 469 349 349
query67 29878 29674 29636 29636
query68 3179 1684 909 909
query69 407 298 272 272
query70 1079 929 939 929
query71 299 258 212 212
query72 2871 2606 2315 2315
query73 862 819 448 448
query74 5114 4949 4777 4777
query75 2624 2579 2248 2248
query76 2328 1220 774 774
query77 362 382 276 276
query78 12612 12308 11869 11869
query79 1384 1123 773 773
query80 583 518 366 366
query81 448 282 245 245
query82 573 156 129 129
query83 351 269 247 247
query84 273 146 112 112
query85 848 507 407 407
query86 386 297 294 294
query87 1835 1834 1742 1742
query88 3704 2813 2769 2769
query89 422 377 326 326
query90 1848 186 184 184
query91 173 159 136 136
query92 66 59 59 59
query93 1512 1524 955 955
query94 546 359 333 333
query95 676 382 344 344
query96 1073 801 355 355
query97 2713 2726 2554 2554
query98 215 208 199 199
query99 1205 1146 1017 1017
Total cold run time: 258013 ms
Total hot run time: 173044 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4774904945
PR approved by at least one committer and no changes requested.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4669451448
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/7c898c900506116cfb44b47189025b6fbfa7e44c_7c898c900506116cfb44b47189025b6fbfa7e44c_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/7c898c900506116cfb44b47189025b6fbfa7e44c_7c898c900506116cfb44b47189025b6fbfa7e44c_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 78.39% (1915/2443) |
| Line Coverage | 64.93% (34232/52725) |
| Region Coverage | 65.36% (17613/26948) |
| Branch Coverage | 54.04% (9357/17316) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4680175630
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4680988934
<details>
<summary>TPC-H: <b>Total hot run time: 29316 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 1f614d8cadd1619a8c17dd6f7ec7b12ba6983286, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17641 4066 4003 4003
q2 q3 10770 1431 800 800
q4 4684 468 336 336
q5 7532 884 586 586
q6 183 174 136 136
q7 773 823 639 639
q8 9353 1531 1503 1503
q9 5585 4502 4491 4491
q10 6779 1816 1580 1580
q11 440 274 246 246
q12 625 413 286 286
q13 18136 3407 2856 2856
q14 258 257 254 254
q15 q16 794 779 715 715
q17 1002 988 1016 988
q18 6849 5908 5547 5547
q19 1172 1259 1082 1082
q20 494 411 259 259
q21 6109 2915 2702 2702
q22 464 370 307 307
Total cold run time: 99643 ms
Total hot run time: 29316 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4987 4838 4687 4687
q2 q3 4899 5293 4704 4704
q4 2168 2182 1390 1390
q5 4837 4919 4679 4679
q6 237 176 128 128
q7 1867 1743 1610 1610
q8 2447 2127 2074 2074
q9 8150 7941 7598 7598
q10 4761 4705 4259 4259
q11 534 387 359 359
q12 735 741 524 524
q13 3057 3409 2771 2771
q14 274 279 249 249
q15 q16 680 697 606 606
q17 1283 1266 1260 1260
q18 7232 6875 6786 6786
q19 1097 1102 1103 1102
q20 2240 2239 2013 2013
q21 5283 4514 4464 4464
q22 535 461 404 404
Total cold run time: 57303 ms
Total hot run time: 51667 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4686510040
# FE UT Coverage Report
Increment line coverage ` 74.61% (429/575)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_9ccb7f30b5fb41183c99a77cc3de3311715aa9b6/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_9ccb7f30b5fb41183c99a77cc3de3311715aa9b6/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3259594757
##########
fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java:
##########
@@ -1598,6 +1600,12 @@ public enum IgnoreSplitType {
"Whether to enable local shuffle on pipelineX engine."}, needForward = true)
private boolean enableLocalShuffle = true;
+ @VarAttrDef.VarAttr(
+ name = ENABLE_LOCAL_SHUFFLE_PLANNER, fuzzy = false, varType = VariableAnnotation.EXPERIMENTAL,
+ description = {"是否在FE规划Local Shuffle",
+ "Whether to plan local shuffle in frontend"}, needForward = true)
+ private boolean enableLocalShufflePlanner = true;
+
Review Comment:
Our deployment process upgrades BE before FE, so during a rolling upgrade an old BE never receives `LOCAL_EXCHANGE_NODE` from a new FE — the new thrift node type is only sent after all BEs in the cluster have been upgraded. Keeping the default `true` is intentional so we get the FE planner enabled out of the box once the rollout completes. If you have a deployment topology that flips this order, please set `enable_local_shuffle_planner=false` in fe.conf or via SQL and the legacy BE path stays bit-for-bit identical to the old behavior.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4680904587
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3432646806
##########
be/src/exec/pipeline/dependency.h:
##########
@@ -832,50 +832,44 @@ struct SetSharedState : public BasicSharedState {
Status hash_table_init();
};
-enum class ExchangeType : uint8_t {
- NOOP = 0,
- // Shuffle data by Crc32CHashPartitioner
- HASH_SHUFFLE = 1,
- // Round-robin passthrough data blocks.
- PASSTHROUGH = 2,
- // Shuffle data by Crc32HashPartitioner<ShuffleChannelIds> (e.g. same as storage engine).
- BUCKET_HASH_SHUFFLE = 3,
- // Passthrough data blocks to all channels.
- BROADCAST = 4,
- // Passthrough data to channels evenly in an adaptive way.
- ADAPTIVE_PASSTHROUGH = 5,
- // Send all data to the first channel.
- PASS_TO_ONE = 6,
-};
+inline bool is_shuffled_exchange(TLocalPartitionType::type idx) {
Review Comment:
dependency.h now directly exposes inline helpers and DataDistribution members that use TLocalPartitionType, but this header never includes the thrift header that defines that enum. This header is included directly from several translation units that only include Types_types.h or protobuf headers first, for example runtime/record_batch_queue.cpp and load/group_commit/group_commit_mgr.cpp, so the build depends on unrelated include order and can fail with TLocalPartitionType undeclared. Please include <gen_cpp/Partitions_types.h> from dependency.h itself before using the enum.
##########
fe/fe-core/src/main/java/org/apache/doris/common/profile/SummaryProfile.java:
##########
@@ -563,6 +567,10 @@ public void update(Map<String, String> summaryInfo) {
// This method is used to display the final data status when the overall query ends.
// This can avoid recalculating some strings and so on every time during the update process.
public void queryFinished() {
+ // Mark profile collection as complete. This is written last, after all BE fragment
+ // profiles have been merged (called post-waitForFragmentsDone). Pollers can wait for
+ // this field to avoid reading a partial profile.
Review Comment:
The completion marker is written before the optional SPLITS_ASSIGNMENT_WEIGHT field, but the new contract says readers can poll this marker to know the profile is fully collected. A polling reader can observe Is Profile Collection Completed=true while queryFinished() has not yet added the assignment-weight info string, and then read a partial profile. Move this marker write to the end of queryFinished() after the optional fields are added.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4741498480
<details>
<summary>ClickBench: <b>Total hot run time: 25.3 s</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 0436cc4cb573b104a59408a6169bd145bcfeff6c, data reload: false
query1 0.01 0.01 0.01
query2 0.10 0.05 0.05
query3 0.25 0.13 0.14
query4 1.61 0.15 0.14
query5 0.26 0.21 0.22
query6 1.26 1.06 1.08
query7 0.03 0.01 0.00
query8 0.06 0.04 0.04
query9 0.38 0.32 0.32
query10 0.56 0.55 0.60
query11 0.19 0.14 0.14
query12 0.18 0.16 0.14
query13 0.48 0.49 0.47
query14 1.01 0.99 1.00
query15 0.63 0.62 0.61
query16 0.33 0.33 0.32
query17 1.12 1.11 1.10
query18 0.22 0.22 0.22
query19 2.04 2.00 1.88
query20 0.02 0.01 0.02
query21 15.41 0.23 0.13
query22 4.74 0.06 0.06
query23 16.17 0.31 0.13
query24 2.95 0.45 0.34
query25 0.11 0.06 0.04
query26 0.73 0.22 0.14
query27 0.05 0.05 0.03
query28 3.54 0.93 0.54
query29 12.52 4.34 3.46
query30 0.27 0.16 0.17
query31 2.78 0.62 0.32
query32 3.23 0.59 0.49
query33 3.16 3.15 3.31
query34 15.57 4.21 3.58
query35 3.57 3.59 3.55
query36 0.55 0.44 0.42
query37 0.09 0.07 0.06
query38 0.06 0.04 0.04
query39 0.04 0.03 0.03
query40 0.19 0.16 0.15
query41 0.08 0.04 0.03
query42 0.04 0.03 0.03
query43 0.04 0.03 0.04
Total cold run time: 96.63 s
Total hot run time: 25.3 s
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4765205084
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4765629332
/review
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4767836895
<details>
<summary>ClickBench: <b>Total hot run time: 25.81 s</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 7fdb989a3a7e4ad0c700539a335598f4978f6938, data reload: false
query1 0.01 0.00 0.01
query2 0.14 0.09 0.09
query3 0.36 0.24 0.27
query4 1.61 0.25 0.24
query5 0.32 0.32 0.31
query6 1.16 0.68 0.67
query7 0.03 0.01 0.01
query8 0.10 0.08 0.07
query9 0.50 0.38 0.38
query10 0.58 0.62 0.60
query11 0.32 0.19 0.19
query12 0.32 0.19 0.19
query13 0.53 0.54 0.52
query14 0.93 0.92 0.92
query15 0.68 0.60 0.61
query16 0.38 0.39 0.38
query17 1.01 1.00 1.03
query18 0.30 0.29 0.29
query19 1.93 1.75 1.83
query20 0.02 0.01 0.01
query21 15.39 0.37 0.31
query22 4.86 0.14 0.14
query23 15.85 0.50 0.30
query24 2.53 0.56 0.43
query25 0.15 0.10 0.10
query26 0.77 0.28 0.22
query27 0.10 0.09 0.10
query28 3.49 0.87 0.51
query29 12.46 4.46 3.51
query30 0.38 0.25 0.27
query31 2.76 0.62 0.35
query32 3.23 0.60 0.48
query33 2.92 3.05 2.99
query34 15.70 4.00 3.32
query35 3.25 3.24 3.28
query36 0.63 0.51 0.51
query37 0.13 0.10 0.10
query38 0.08 0.06 0.06
query39 0.07 0.07 0.06
query40 0.22 0.18 0.17
query41 0.12 0.09 0.08
query42 0.09 0.06 0.06
query43 0.08 0.07 0.08
Total cold run time: 96.49 s
Total hot run time: 25.81 s
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4767041113
# FE Regression Coverage Report
Increment line coverage ` 81.28% (456/561)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "zclllyybb (via GitHub)" <gi...@apache.org>.
zclllyybb commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4766364688
/review
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4767842287
<details>
<summary>ClickBench: <b>Total hot run time: 26.07 s</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b, data reload: false
query1 0.01 0.01 0.01
query2 0.15 0.09 0.09
query3 0.37 0.24 0.25
query4 1.60 0.24 0.25
query5 0.34 0.36 0.31
query6 1.15 0.68 0.67
query7 0.04 0.01 0.01
query8 0.09 0.08 0.07
query9 0.52 0.39 0.39
query10 0.58 0.57 0.59
query11 0.32 0.19 0.18
query12 0.33 0.19 0.20
query13 0.54 0.52 0.52
query14 0.93 0.92 0.92
query15 0.69 0.60 0.60
query16 0.39 0.39 0.40
query17 1.05 1.00 1.01
query18 0.33 0.30 0.30
query19 1.93 1.83 1.80
query20 0.02 0.02 0.01
query21 15.39 0.37 0.31
query22 4.88 0.14 0.14
query23 15.84 0.51 0.32
query24 2.52 0.58 0.45
query25 0.16 0.11 0.10
query26 0.76 0.28 0.22
query27 0.10 0.09 0.10
query28 3.48 0.93 0.52
query29 12.44 4.57 3.58
query30 0.39 0.25 0.26
query31 2.77 0.64 0.35
query32 3.24 0.60 0.49
query33 2.96 2.96 2.98
query34 15.78 4.13 3.40
query35 3.29 3.28 3.31
query36 0.65 0.52 0.50
query37 0.13 0.10 0.10
query38 0.08 0.07 0.07
query39 0.07 0.06 0.06
query40 0.21 0.18 0.17
query41 0.14 0.09 0.07
query42 0.08 0.07 0.07
query43 0.08 0.07 0.06
Total cold run time: 96.82 s
Total hot run time: 26.07 s
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4687151641
# BE Regression && UT Coverage Report
Increment line coverage `88.29% (362/410)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_9ccb7f30b5fb41183c99a77cc3de3311715aa9b6_merge/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_9ccb7f30b5fb41183c99a77cc3de3311715aa9b6_merge/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 73.99% (28307/38256) |
| Line Coverage | 57.96% (308498/532265) |
| Region Coverage | 54.86% (258701/471560) |
| Branch Coverage | 56.21% (112201/199624) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4708791066
# FE UT Coverage Report
Increment line coverage ` 74.73% (420/562)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_832ba5f0bf8ab5846bb316e94f1099ae58fe466f/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_832ba5f0bf8ab5846bb316e94f1099ae58fe466f/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3413413674
##########
fe/fe-core/src/main/java/org/apache/doris/planner/ScanNode.java:
##########
@@ -733,7 +737,7 @@ public ScanContext getScanContext() {
}
@Override
- public boolean isSerialOperator() {
+ public boolean isSerialNode() {
ConnectContext context = ConnectContext.get();
if (context == null) {
return numScanBackends() <= 0;
Review Comment:
I don't think this is a regression — I checked the actual before/after. Master's `ScanNode.isSerialOperator()` body is byte-identical to this PR's `isSerialNode()` (both: `numScanBackends() <= 0 || getScanRangeNum() < parallelExecInstanceNum * numScanBackends() || isForceToLocalShuffle()`); neither version ever contained `fragment.useSerialSource(context)`. At the decision point (`Coordinator.assignScanRanges`, ~`Coordinator.java:2995`) master called `isSerialOperator()` and this PR calls `isSerialNode()` — same body, pure rename.
The `useSerialSource()` gate that matters there is in the *caller* (`ignoreStorageDataDistribution`, ~`Coordinator.java:2939`), which is unchanged. So the legacy scan-range assignment behaves exactly as on master. Could you point me at the specific old code where `isSerialNode()`/`isSerialOperator()` was `numScanBackends() <= 0 || fragment.useSerialSource(context)`? I couldn't find that shape in master.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AssertNumRowsNode.java:
##########
@@ -78,7 +84,17 @@ public int getNumInstances() {
}
@Override
- public boolean isSerialOperator() {
+ public boolean isSerialNode() {
return true;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(PlanTranslatorContext translatorContext,
+ PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+
+ Pair<PlanNode, LocalExchangeType> enforceResult = enforceRequire(
+ translatorContext, children.get(0), 0, LocalExchangeTypeRequire.requirePassthrough());
Review Comment:
The BE operator requires PASSTHROUGH, and the FE planner mirrors BE's `required_data_distribution` 1:1, so this matches: `AssertNumRowsOperatorX::required_data_distribution()` returns `{TLocalPartitionType::PASSTHROUGH}` (`assert_num_rows_operator.h:49`). Returning NOOP here would diverge from BE-native behavior. The "only one row" observation is about output cardinality, but `required_data_distribution` describes the input side — AssertNumRows is serial (1 task) and the PASSTHROUGH fans the (possibly N-task) child into it, same as BE.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -824,14 +828,97 @@ public void foreachDownInCurrentFragment(Consumer<PlanNode> visitor) {
});
}
- // Operators need to be executed serially. (e.g. finalized agg without key)
- public boolean isSerialOperator() {
+ /**
+ * Node-level "is this operator inherently serial" property — answers without looking
+ * at the fragment. Default false; subclasses override (e.g. finalized agg without key,
+ * UNPARTITIONED ExchangeNode with merge sort).
+ *
+ * Use ONLY in framework-internal places where we are already iterating within a
+ * fragment whose serial-source mode is fixed: {@link #shouldResetSerialFlagForChild}
+ * inputs, {@link #createLocalExchange} heavy-op gate, and child.isSerialNode() checks
+ * embedded inside an enforceRequire path. Do NOT use it when computing a
+ * {@link LocalExchangeNode.LocalExchangeTypeRequire} on a child — call
+ * {@link #isSerialOperatorOnBe} instead.
+ */
+ public boolean isSerialNode() {
Review Comment:
It has to stay `public`: `Coordinator` (package `org.apache.doris.qe`) calls `node.get().isSerialNode()` in `assignScanRanges`, so a cross-package caller needs public visibility — `protected` wouldn't compile.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AddLocalExchange.java:
##########
@@ -0,0 +1,152 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.planner;
+
+import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.glue.translator.PlanTranslatorContext;
+import org.apache.doris.nereids.trees.plans.distribute.DistributedPlan;
+import org.apache.doris.nereids.trees.plans.distribute.FragmentIdMapping;
+import org.apache.doris.nereids.trees.plans.distribute.PipelineDistributedPlan;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeType;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeTypeRequire;
+import org.apache.doris.planner.LocalExchangeNode.RequireHash;
+
+/**
+ * FE-side local exchange planner — inserts {@link LocalExchangeNode} into each fragment's
+ * plan tree so that within-fragment data redistribution is decided at planning time
+ * instead of at BE pipeline-build time.
+ *
+ * <h3>When this runs</h3>
+ * Invoked from {@code NereidsPlanner.addLocalExchangeAfterDistribute()} right after
+ * {@code DistributePlanner} has assigned instances to fragments and before the plan is
+ * serialized to BE. Gated by session variable {@code enable_local_shuffle_planner}
+ * (default true) and {@code enable_local_shuffle}; when either is off this pass is
+ * skipped entirely and BE falls back to its own {@code _plan_local_exchange}. The two
+ * paths are mutually exclusive: BE consults {@code runtime_state.h::plan_local_shuffle()}
+ * to know whether it should plan LE itself.
+ *
+ * <h3>What it changes</h3>
+ * <ul>
+ * <li>For each fragment with {@code maxPerBeInstances > 1}, walks the plan tree
+ * bottom-up via {@link PlanNode#enforceAndDeriveLocalExchange} and inserts
+ * LocalExchangeNodes where children's output distribution doesn't satisfy the
+ * parent's requirement.</li>
+ * <li>May wrap the fragment root with an extra PASSTHROUGH LE so the data sink
+ * (DataStreamSink / OlapTableSink) runs with the full instance count even when
+ * the root operator is serial — see {@link #addLocalExchangeForFragment}.</li>
+ * <li>Does NOT modify the fragment sink itself, fragment boundaries, or instance
+ * assignment.</li>
+ * </ul>
+ *
+ * <h3>Per-BE instance semantics</h3>
+ * Skips fragments where every BE has at most 1 instance. Using a global instance count
+ * would insert LE for "2 BEs × 1 instance" cases, which BE's own
+ * {@code _plan_local_exchange} would not — leading to pipeline task-count mismatch and
+ * deadlock. See {@link #addLocalExchange}.
+ *
+ * <h3>Reading order</h3>
+ * Start with {@link PlanNode#enforceRequire} (the recursion engine), then individual
+ * {@code enforceAndDeriveLocalExchange} overrides on PlanNode subclasses.
+ */
+public class AddLocalExchange {
+ /** addLocalExchange with distributed plans, skipping single-instance fragments.
+ * BE's _plan_local_exchange checks _num_instances which is the per-BE instance count.
+ * With _num_instances<=1 all pipelines on that BE have 1 task so local exchange is a no-op.
+ * We must use the same per-BE semantics: skip when every BE has at most 1 instance.
+ * Using global instanceCount would insert LE for fragments where 2 BEs each have 1 instance
+ * (global=2, per-BE=1), causing pipeline task mismatch and deadlock. */
+ public void addLocalExchange(FragmentIdMapping<DistributedPlan> distributedPlans,
+ PlanTranslatorContext context) {
+ for (DistributedPlan plan : distributedPlans.values()) {
+ PipelineDistributedPlan pipePlan = (PipelineDistributedPlan) plan;
+ long maxPerBeInstances = pipePlan.getInstanceJobs().stream()
+ .collect(java.util.stream.Collectors.groupingBy(
+ j -> j.getAssignedWorker().id(), java.util.stream.Collectors.counting()))
+ .values().stream().mapToLong(Long::longValue).max().orElse(0);
+ if (maxPerBeInstances <= 1) {
+ continue;
+ }
+ PlanFragment fragment = pipePlan.getFragmentJob().getFragment();
+ addLocalExchangeForFragment(fragment, context);
+ }
+ }
+
+ private void addLocalExchangeForFragment(PlanFragment fragment, PlanTranslatorContext context) {
+ DataSink sink = fragment.getSink();
+ LocalExchangeTypeRequire require = sink == null
+ ? LocalExchangeTypeRequire.noRequire() : sink.getLocalExchangeTypeRequire();
+ PlanNode root = fragment.getPlanRoot();
+ context.setHasSerialAncestorInPipeline(root, false);
+ Pair<PlanNode, LocalExchangeType> output = root
+ .enforceAndDeriveLocalExchange(context, null, require);
+ PlanNode newRoot = output.first;
+ // The fragment data sink (DataStreamSink, OlapTableSink) runs in the same pipeline
+ // as the root. If the root will be serial on BE, the sink pipeline has 1 task —
+ // only instance 0 sends data, others hang or miss writes.
+ // Insert PASSTHROUGH fan-out so sink runs with _num_instances tasks.
+ // This matches BE-native's default required_data_distribution():
+ // _child->is_serial_operator() ? PASSTHROUGH : NOOP
+ if (newRoot.isSerialOperatorOnBe(context.getConnectContext())) {
+ newRoot = new LocalExchangeNode(context.nextPlanNodeId(), newRoot,
+ LocalExchangeType.PASSTHROUGH, null);
+ }
+ if (newRoot != root) {
+ fragment.setPlanRoot(newRoot);
+ }
+ }
+
+ public static boolean isColocated(PlanNode plan) {
+ if (plan instanceof AggregationNode) {
+ return ((AggregationNode) plan).isColocate() && isColocated(plan.getChild(0));
+ } else if (plan instanceof OlapScanNode) {
+ return true;
+ } else if (plan instanceof SelectNode) {
+ return isColocated(plan.getChild(0));
+ } else if (plan instanceof HashJoinNode) {
+ return ((HashJoinNode) plan).isColocate()
+ && (isColocated(plan.getChild(0)) || isColocated(plan.getChild(1)));
+ } else if (plan instanceof SetOperationNode) {
+ if (!((SetOperationNode) plan).isColocate()) {
+ return false;
+ }
+ for (PlanNode child : plan.getChildren()) {
+ if (isColocated(child)) {
+ return true;
+ }
+ }
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ public static LocalExchangeType resolveExchangeType(LocalExchangeTypeRequire require) {
Review Comment:
I'd prefer to keep `preferType()` as `GLOBAL` and not align it down to `LOCAL`. The observation is right (it's not authoritative — `resolveExchangeType()` decides the actual type, and `hasPartitionExprs()` only uses `isHashShuffle()`), but the value itself is a deliberate safe default:
`GLOBAL_EXECUTION_HASH_SHUFFLE` is the unconditionally-valid hash partition (full cross-backend redistribution). `LOCAL_EXECUTION_HASH_SHUFFLE` only rebalances within a backend, so it's correct *only if* each key's rows are already confined to one backend — it carries a precondition. For the generic `RequireHash` ("any hash"), the canonical/safe representative should be the precondition-free one (GLOBAL). `resolveExchangeType()` then specializes RequireHash→LOCAL *only* in the FE-planned intra-fragment context where that precondition holds and GLOBAL's `shuffle_idx_to_instance_idx` could be empty.
Making `preferType()` return LOCAL would make the abstract default the precondition-bearing type — a latent footgun if any future caller (or `noopTo()`) ever used `preferType()` as a real inserted type. I'll add a comment near `resolveExchangeType()`/`RequireHash` documenting that the LOCAL specialization is intentional and scoped, rather than changing the default.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4707952956
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4701410428
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3412123587
##########
gensrc/thrift/Partitions.thrift:
##########
@@ -55,6 +55,40 @@ enum TPartitionType {
MERGE_PARTITIONED = 9
}
+enum TLocalPartitionType {
+ NOOP = 0,
+ // used to resume the global hash distribution because other distribution break the global hash distribution,
+ // such as PASSTHROUGH. and then JoinNode can shuffle data by the same hash distribution.
+ //
+ // for example: look here, need resume to GLOBAL_EXECUTION_HASH_SHUFFLE
+ // ↓
+ // Node -> LocalExchangeNode(PASSTHROUGH) → JoinNode → LocalExchangeNode(GLOBAL_EXECUTION_HASH_SHUFFLE) → JoinNode
+ // ExchangeNode(BROADCAST) ↗ ↑
+ // ExchangeNode(GLOBAL_EXECUTION_HASH_SHUFFLE)
+ GLOBAL_EXECUTION_HASH_SHUFFLE = 1,
+ // used to rebalance data for rebalance data and add parallelism
+ //
+ // for example: look here, need use LOCAL_EXECUTION_HASH_SHUFFLE to rebalance data
+ // ↓
+ // Scan(hash(id)) -> LocalExchangeNode(LOCAL_EXECUTION_HASH_SHUFFLE(id, name)) → AggregationNode(group by(id,name))
Review Comment:
Thanks — you're right that the original wording was imprecise, and I've reworded the comment (`603c87096bf`). The key point is that this is a **performance (parallelism)** rebalance, not a correctness one:
- `group by (id, name)` is already correct on a scan hash-distributed by `id`, because `id` is a *subset* of the grouping keys — every `(id, name)` group is fully contained in the backend that owns its `id`. So `hash(id)` already co-locates each group, exactly as you noted; no reshuffle is needed for correctness.
- What `LOCAL_EXECUTION_HASH_SHUFFLE` adds here is **parallelism**: when there are few distinct `id` values the data is concentrated on a few local instances, so we re-partition by the full key `(id, name)` to spread the aggregation across all local instances (coarse → fine).
Your suggested `scan(hash(id, name)) -> agg(group by id)` goes the other way (fine → coarse), so it wouldn't add parallelism. More importantly it isn't a `LOCAL_EXECUTION_HASH_SHUFFLE` scenario at all: under `hash(id, name)` the rows for a single `id` are scattered across *different backends* (e.g. `(5, A)` on be1, `(5, B)` on be2), and a local exchange only rebalances among the local instances of one backend — it can't gather an `id` across backends. That re-partition needs a cross-backend hash exchange (`GLOBAL_EXECUTION_HASH_SHUFFLE` / the network shuffle), which is exactly what `LOCAL_EXECUTION_HASH_SHUFFLE` is *not* — so it wouldn't showcase this type's advantage.
So I kept the original (coarse → fine) example but rewrote the explanation to make the "parallelism, not correctness" intent explicit.
##########
gensrc/thrift/Partitions.thrift:
##########
@@ -55,6 +55,40 @@ enum TPartitionType {
MERGE_PARTITIONED = 9
}
+enum TLocalPartitionType {
+ NOOP = 0,
+ // used to resume the global hash distribution because other distribution break the global hash distribution,
+ // such as PASSTHROUGH. and then JoinNode can shuffle data by the same hash distribution.
+ //
+ // for example: look here, need resume to GLOBAL_EXECUTION_HASH_SHUFFLE
+ // ↓
+ // Node -> LocalExchangeNode(PASSTHROUGH) → JoinNode → LocalExchangeNode(GLOBAL_EXECUTION_HASH_SHUFFLE) → JoinNode
+ // ExchangeNode(BROADCAST) ↗ ↑
+ // ExchangeNode(GLOBAL_EXECUTION_HASH_SHUFFLE)
+ GLOBAL_EXECUTION_HASH_SHUFFLE = 1,
+ // used to rebalance data for rebalance data and add parallelism
+ //
+ // for example: look here, need use LOCAL_EXECUTION_HASH_SHUFFLE to rebalance data
+ // ↓
+ // Scan(hash(id)) -> LocalExchangeNode(LOCAL_EXECUTION_HASH_SHUFFLE(id, name)) → AggregationNode(group by(id,name))
+ //
+ // the LOCAL_EXECUTION_HASH_SHUFFLE is necessary because the hash distribution of scan node is based on id,
+ // but the hash distribution of aggregation node is based on id and name, so we need to rebalance data by both
+ // id and name to make sure the data with same id and name can be sent to the same instance of aggregation node.
+ // and we can not use GLOBAL_EXECUTION_HASH_SHUFFLE(id, name) here, because
+ // `TPipelineFragmentParams.shuffle_idx_to_instance_idx` is used to mapping partial global instance index to local
+ // instance index, and discard the other backend's instance index, the data not belong to the local instance will be
+ // discarded, which cause data loss.
+ LOCAL_EXECUTION_HASH_SHUFFLE = 2,
+ BUCKET_HASH_SHUFFLE = 3,
+ // round-robin partition, used to rebalance data for rebalance data and add parallelism
+ PASSTHROUGH = 4,
+ ADAPTIVE_PASSTHROUGH = 5,
+ BROADCAST = 6,
+ PASS_TO_ONE = 7,
+ LOCAL_MERGE_SORT = 8
Review Comment:
Done (`603c87096bf`). Added a comment for every value, grounded in the BE exchanger implementations, with short examples where they help:
- `NOOP` — no exchange; `DataDistribution::need_local_exchange()` is false, filtered out before BE's exchanger factory.
- `BUCKET_HASH_SHUFFLE` — `BucketShuffleExchanger`; route each row to the instance owning its bucket via `bucket_seq_to_instance_idx` (colocate / bucket-shuffle join).
- `PASSTHROUGH` — round-robin *whole blocks* (`block N -> instance N % local_instance_count`), no re-partition; e.g. fan a serial source out to N instances.
- `ADAPTIVE_PASSTHROUGH` — row-level round-robin for the first `>= local_instance_count` blocks (even initial spread), then cheap whole-block PASSTHROUGH; round-robin, not a hash shuffle, switch driven by block count.
- `BROADCAST` — copy every block to all instances (broadcast-join build side).
- `PASS_TO_ONE` — funnel to instance 0; degrades to BROADCAST when `enable_share_hash_table_for_broadcast_join` is off.
- `LOCAL_MERGE_SORT` — k-way merge of already-sorted inputs (legacy BE path only; FE never emits it).
##########
gensrc/thrift/PlanNodes.thrift:
##########
@@ -1427,6 +1428,24 @@ struct TExchangeNode {
4: optional Partitions.TPartitionType partition_type
}
+struct TLocalExchangeNode {
+ 1: required Partitions.TLocalPartitionType partition_type
Review Comment:
Done (`603c87096bf`) — changed `partition_type` to `optional`. Since it was the only `required` field, making it optional drops Thrift's positional constructor, so I also updated the single FE call site to the no-arg ctor + setter: `new TLocalExchangeNode(); ...setPartitionType(exchangeType.toThrift())`. BE reads the field value directly (always set by FE), so no BE change is needed.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4708902228
<details>
<summary>TPC-DS: <b>Total hot run time: 168821 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 14ff5b48070e26ba3f1518c301fce97c36998331, data reload: false
query5 4342 636 467 467
query6 430 182 161 161
query7 4825 574 308 308
query8 367 231 215 215
query9 8746 4101 4029 4029
query10 444 308 250 250
query11 5909 2345 2116 2116
query12 160 100 101 100
query13 1258 603 417 417
query14 6965 5360 5064 5064
query14_1 4393 4370 4408 4370
query15 205 202 178 178
query16 1035 447 464 447
query17 1120 713 590 590
query18 2712 477 349 349
query19 203 187 144 144
query20 123 106 105 105
query21 223 139 119 119
query22 13608 13555 13307 13307
query23 17468 16461 16226 16226
query23_1 16234 16158 16408 16158
query24 7615 1762 1308 1308
query24_1 1312 1328 1329 1328
query25 565 449 406 406
query26 1312 309 171 171
query27 2591 527 335 335
query28 4415 2071 2062 2062
query29 1089 643 497 497
query30 309 236 195 195
query31 1130 1088 961 961
query32 117 65 63 63
query33 534 324 251 251
query34 1203 1179 673 673
query35 761 786 667 667
query36 1371 1373 1245 1245
query37 150 100 88 88
query38 3168 3127 3052 3052
query39 934 920 912 912
query39_1 909 870 882 870
query40 221 117 99 99
query41 61 59 57 57
query42 96 91 92 91
query43 313 314 274 274
query44
query45 194 187 185 185
query46 1090 1242 743 743
query47 2386 2361 2267 2267
query48 397 393 293 293
query49 620 451 342 342
query50 1056 354 255 255
query51 4492 4383 4332 4332
query52 86 87 74 74
query53 242 271 189 189
query54 256 212 197 197
query55 76 74 68 68
query56 231 247 211 211
query57 1436 1429 1301 1301
query58 251 210 215 210
query59 1565 1617 1433 1433
query60 280 242 227 227
query61 156 149 146 146
query62 698 649 587 587
query63 229 186 191 186
query64 2461 783 604 604
query65
query66 1754 446 332 332
query67 29250 29687 29517 29517
query68
query69 406 283 264 264
query70 1000 966 938 938
query71 308 216 212 212
query72 2968 2613 2290 2290
query73 853 738 451 451
query74 5115 4922 4777 4777
query75 2657 2555 2227 2227
query76 2305 1155 855 855
query77 345 390 278 278
query78 12512 12450 11751 11751
query79 1479 1019 767 767
query80 1286 443 402 402
query81 519 284 240 240
query82 628 152 121 121
query83 334 273 239 239
query84
query85 869 494 406 406
query86 421 299 280 280
query87 3387 3300 3135 3135
query88 3642 2793 2754 2754
query89 424 379 329 329
query90 1934 182 180 180
query91 172 165 131 131
query92 60 62 58 58
query93 1692 1377 942 942
query94 730 369 310 310
query95 689 452 340 340
query96 1011 763 354 354
query97 2722 2726 2587 2587
query98 211 205 197 197
query99 1149 1166 1035 1035
Total cold run time: 251974 ms
Total hot run time: 168821 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4674684816
### Follow-up on the [P1] check (AddLocalExchange) — removed it entirely
After thinking it through (and with the CI evidence), the serial→non-serial validation isn't worth keeping in any form — neither throw nor warn:
1. **It reads the same `isSerialOperatorOnBe` flag it would validate**, so it's blind to the one class of bug that actually matters — a *wrong serial flag*. The framework and the check would agree on the same wrong answer; a check can't validate an input it shares with the thing it's checking.
2. **It fires on legitimate plans** — a serial child feeding a parent that requires PASSTHROUGH/noRequire (TableFunction, NLJ, Agg) needs no LE and is correct. So as a `warn` it cries wolf, and as a `throw` it breaks those plans (the 9 P0 patterns from the last run).
3. **The one class it *could* catch** — a `require`→LE logic regression (e.g. a broken `satisfy()`) — the regression/RQG already catch end-to-end, just without the FE fail-fast.
Net: it adds noise and false confidence, not safety. The real oracle is the end-to-end run (which is exactly what surfaced the throw's false positives — no FE self-check did). The principled direction is a single source of truth for the serial/distribution decision (BE reading the FE-written flag) so there's no FE-prediction-vs-BE-reality gap to police in the first place.
Removed the method + its call. Correctness still comes from `enforceRequire` inserting the LEs, validated by the regression/RQG.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4476727978
Thank you for your contribution to Apache Doris.
Don't know what should be done next? See [How to process your PR](https://cwiki.apache.org/confluence/display/DORIS/How+to+process+your+PR).
Please clearly describe your PR:
1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
3. What features were added. Why was this function added?
4. Which code was refactored and why was this part of the code refactored?
5. Which functions were optimized and what is the difference before and after the optimization?
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4486932993
run build all
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4487791049
# BE UT Coverage Report
Increment line coverage `11.11% (46/414)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/9016e5a105f84231f1a535be233aaf3e595c711d_9016e5a105f84231f1a535be233aaf3e595c711d/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/9016e5a105f84231f1a535be233aaf3e595c711d_9016e5a105f84231f1a535be233aaf3e595c711d/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 53.56% (20734/38712) |
| Line Coverage | 37.19% (196094/527345) |
| Region Coverage | 33.52% (153693/458547) |
| Branch Coverage | 34.56% (66979/193832) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4505876206
# BE Regression && UT Coverage Report
Increment line coverage `16.84% (80/475)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_f2d43a29db5822a72153fd88cbcd8bead33cbc6d_merge/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_f2d43a29db5822a72153fd88cbcd8bead33cbc6d_merge/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 57.50% (21769/37857) |
| Line Coverage | 40.72% (214336/526315) |
| Region Coverage | 37.18% (172374/463674) |
| Branch Coverage | 37.96% (73965/194865) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4506404038
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4499305087
# BE Regression && UT Coverage Report
Increment line coverage `71.74% (297/414)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_3ac8525eaac942c0bbeb93cf46d4c95ecf353156_merge/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_3ac8525eaac942c0bbeb93cf46d4c95ecf353156_merge/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 63.42% (24060/37938) |
| Line Coverage | 47.17% (248369/526552) |
| Region Coverage | 44.26% (205193/463569) |
| Branch Coverage | 45.42% (88533/194940) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4495475930
# FE UT Coverage Report
Increment line coverage ` 74.54% (448/601)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_ef4ea66a66f075dfb3259a519599ed361cd2d371/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_ef4ea66a66f075dfb3259a519599ed361cd2d371/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4551497474
<details>
<summary>TPC-DS: <b>Total hot run time: 172334 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit a68f47c588b0124cf5474ff805c847df5dfc781e, data reload: false
query5 4336 665 532 532
query6 326 210 198 198
query7 4272 571 287 287
query8 329 236 254 236
query9 8797 4126 4093 4093
query10 463 360 308 308
query11 5793 2654 2204 2204
query12 183 141 125 125
query13 1348 629 479 479
query14 6158 5545 5208 5208
query14_1 4535 4506 4473 4473
query15 213 207 186 186
query16 1017 470 434 434
query17 1126 731 590 590
query18 2725 488 356 356
query19 220 209 166 166
query20 138 131 135 131
query21 213 140 127 127
query22 13765 13631 13421 13421
query23 17521 16516 16123 16123
query23_1 16515 16264 16423 16264
query24 7419 1788 1353 1353
query24_1 1323 1337 1346 1337
query25 583 497 450 450
query26 1330 325 178 178
query27 2678 599 365 365
query28 4425 2009 2020 2009
query29 1005 659 538 538
query30 306 244 208 208
query31 1136 1080 954 954
query32 89 82 79 79
query33 581 369 316 316
query34 1193 1167 649 649
query35 783 822 702 702
query36 1460 1382 1249 1249
query37 157 110 98 98
query38 3237 3182 3095 3095
query39 928 937 884 884
query39_1 899 866 886 866
query40 234 159 134 134
query41 73 70 69 69
query42 114 115 116 115
query43 334 339 301 301
query44
query45 219 214 206 206
query46 1082 1250 729 729
query47 2433 2403 2246 2246
query48 423 445 315 315
query49 665 507 404 404
query50 988 359 262 262
query51 4470 4384 4332 4332
query52 110 108 97 97
query53 250 292 210 210
query54 342 290 284 284
query55 98 95 93 93
query56 326 327 329 327
query57 1451 1416 1347 1347
query58 309 280 287 280
query59 1629 1670 1468 1468
query60 342 330 330 330
query61 187 186 219 186
query62 697 646 589 589
query63 247 214 210 210
query64 2343 782 647 647
query65
query66 1667 479 359 359
query67 29756 29770 29650 29650
query68
query69 473 345 316 316
query70 1090 1034 1015 1015
query71 307 266 277 266
query72 3044 2772 2451 2451
query73 851 757 449 449
query74 5130 4992 4809 4809
query75 2712 2641 2278 2278
query76 2284 1178 770 770
query77 405 423 336 336
query78 12419 12457 11922 11922
query79 1500 1001 765 765
query80 1333 556 469 469
query81 519 281 244 244
query82 1275 164 123 123
query83 353 280 261 261
query84 269 143 118 118
query85 939 546 458 458
query86 461 331 337 331
query87 3440 3385 3238 3238
query88 3648 2769 2747 2747
query89 458 397 347 347
query90 1896 193 188 188
query91 184 174 143 143
query92 82 78 79 78
query93 1472 1608 938 938
query94 731 356 334 334
query95 680 463 378 378
query96 1108 776 343 343
query97 2753 2783 2614 2614
query98 249 230 227 227
query99 1170 1160 1025 1025
Total cold run time: 256307 ms
Total hot run time: 172334 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4551008689
# Cloud UT Coverage Report
Increment line coverage ` ` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/ccfac05f6b543ebe78f973987084a8de173759c9_ccfac05f6b543ebe78f973987084a8de173759c9_cloud/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/ccfac05f6b543ebe78f973987084a8de173759c9_ccfac05f6b543ebe78f973987084a8de173759c9_cloud/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 78.06% (1854/2375) |
| Line Coverage | 64.51% (33328/51665) |
| Region Coverage | 65.18% (16519/25343) |
| Branch Coverage | 55.75% (8839/15854) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3259596727
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -933,4 +1020,229 @@ private String mergeIcebergAccessPathsWithId(
}
return StringUtils.join(mergeDisplayAccessPaths, ", ");
}
+
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ ArrayList<PlanNode> newChildren = Lists.newArrayList();
+ for (int i = 0; i < children.size(); i++) {
+ Pair<PlanNode, LocalExchangeType> childOutput
+ = enforceRequire(translatorContext, children.get(i), i, LocalExchangeTypeRequire.noRequire());
+ newChildren.add(childOutput.first);
+ }
+ this.children = newChildren;
+ return Pair.of(this, LocalExchangeType.NOOP);
+ }
+
+ /**
+ * Unified framework method: propagate serial flag → recurse child → satisfy check → Layer 1 skip → insert LE.
+ * Replaces the old enforceChild/enforceChildExchange/forceEnforceChildExchange trio.
+ *
+ * <h3>Data flow</h3>
+ * <ul>
+ * <li><b>serial-ancestor flag</b> ({@link PlanTranslatorContext#hasSerialAncestorInPipeline})
+ * — flows root → leaf during traversal. Mirrors BE's
+ * {@code any_of(operators[idx..end], is_serial_operator)} check used by
+ * {@code _add_local_exchange} to skip LE insertion when an ancestor in the same
+ * pipeline is already serial. Reset at pipeline boundaries via
+ * {@link #shouldResetSerialFlagForChild}.</li>
+ * <li><b>shuffle-for-correctness flag</b>
+ * ({@link PlanTranslatorContext#hasShuffleForCorrectnessAncestor}) — also flows
+ * root → leaf. Mirrors BE's {@code _followed_by_shuffled_operator}: tells a
+ * child whether some downstream operator depends on hash distribution for
+ * correctness, so {@code SetOperationNode} can pre-shuffle union branches.</li>
+ * <li><b>return value</b> {@code Pair<PlanNode, LocalExchangeType>} — first is the
+ * (possibly LE-wrapped) child; second is the actual output distribution as
+ * observed by the parent. Caller's {@code require.satisfy(output)} decides
+ * whether more LE is needed.</li>
+ * <li><b>parent.require</b> describes the constraint on the child output —
+ * computed inside the parent's {@code enforceAndDeriveLocalExchange} per child.</li>
+ * </ul>
+ *
+ * <h3>Invariants</h3>
+ * <ul>
+ * <li>Every serial → non-serial transition has an LE somewhere between them
+ * (enforced by framework step 3 below, validated by
+ * {@link AddLocalExchange#validateNoSerialWithoutLocalExchange}).</li>
+ * <li>{@code LocalExchangeNode} itself is always non-serial — setting it serial
+ * would defeat its purpose of fanning a 1-task pipeline back to N tasks.</li>
+ * <li>For pipeline-breaking parents ({@code shouldResetSerialFlagForChild=true}),
+ * the child starts a fresh pipeline so {@code hasSerialAncestor} is reset; the
+ * node's own {@code isSerialNode()} still composes in for the child's view.</li>
+ * <li>{@code RequireHash} accepts any hash flavour; {@code RequireSpecific} demands
+ * an exact match (with the one PASSTHROUGH/ADAPTIVE_PASSTHROUGH compatibility).
+ * Pick the looser one whenever correctness allows — see
+ * {@link LocalExchangeNode.LocalExchangeTypeRequire}.</li>
+ * </ul>
+ *
+ * <h3>Layers</h3>
+ * Layer 1 (shouldSkipLE): mirrors BE's need_to_local_exchange — skip when this node or
+ * an ancestor in the same pipeline is serial (operators[idx..end] has serial → skip).
+ * Layer 2 (require/output): each Node declares require and output in enforceAndDeriveLocalExchange.
+ */
+ protected Pair<PlanNode, LocalExchangeType> enforceRequire(
+ PlanTranslatorContext translatorContext, PlanNode child, int childIndex,
+ LocalExchangeTypeRequire require) {
+ // 1. Propagate serial-ancestor flag to child.
+ // For pipeline-splitting operators (shouldReset=true, e.g. non-streaming AGG):
+ // Drop inherited serial flag from parent (parent is in a different pipeline),
+ // but keep this node's own serial status (child is in the same pipeline as this
+ // node's sink, e.g. Exchange is in AGG_Sink pipeline).
+ // For non-splitting operators (shouldReset=false, e.g. streaming AGG):
+ // Inherit parent's serial flag + this node's own.
+ boolean inheritedSerial = shouldResetSerialFlagForChild(childIndex)
+ ? false : translatorContext.hasSerialAncestorInPipeline(this);
+ boolean childHasSerialAncestor = inheritedSerial || isSerialNode();
+ translatorContext.setHasSerialAncestorInPipeline(child, childHasSerialAncestor);
+
+ // 1b. Propagate shuffle-for-correctness-ancestor flag to child.
+ // Mirrors BE's _followed_by_shuffled_operator: a downstream operator needs hash
+ // distribution for correctness, and the chain to here goes through HASH or NOOP
+ // requirements (so the dependency is preserved).
+ // propagate = ((inheritedShuffled || self.requiresShuffleForCorrectness)
+ // && require is hash)
+ // || (inheritedShuffled && require is noop/passthrough)
+ boolean inheritedShuffled = translatorContext.hasShuffleForCorrectnessAncestor(this);
+ boolean selfOrInheritedShuffled = inheritedShuffled || requiresShuffleForCorrectness();
+ boolean requireIsHash = require.preferType().isHashShuffle();
+ boolean requireIsNoop = require.preferType() == LocalExchangeNode.LocalExchangeType.NOOP;
+ boolean childShuffledAncestor = (selfOrInheritedShuffled && requireIsHash)
+ || (inheritedShuffled && requireIsNoop);
+ translatorContext.setHasShuffleForCorrectnessAncestor(child, childShuffledAncestor);
+
+ // 2. Recurse child (Layer 2: child declares its own require/output)
+ Pair<PlanNode, LocalExchangeType> childOutput =
+ child.enforceAndDeriveLocalExchange(translatorContext, this, require);
+
+ // Steps 2.5 and 3 both react to a serial child but address different concerns:
+ // - Step 2.5 rewrites the OUTPUT-side view (what we tell satisfy/parent about
+ // the child's actual distribution). A serial pipeline runs with 1 task so
+ // its distribution claim is meaningless — flatten to NOOP so the satisfy
+ // check below doesn't get fooled by a stale "I output BUCKET_HASH" claim.
+ // - Step 3 rewrites the REQUIRE-side decision (what we want from the child).
+ // If we previously asked for nothing (noRequire) but the child turns out
+ // to be serial and we're not, upgrade to requirePassthrough so an LE is
+ // inserted to restore parallelism.
+
+ // 2.5. Serial child override (output side): if child is serial on BE, force its
+ // reported output to NOOP. Distribution is irrelevant when the child runs
+ // with 1 task; downstream parallelism is restored either by step 3 (LE
+ // insertion) or skipped entirely by step 4b (we're also serial).
+ if (childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ childOutput = Pair.of(childOutput.first, LocalExchangeType.NOOP);
+ }
+
+ // 3. Framework-level serial child check (require side, mirrors BE base class
+ // required_data_distribution): if child will be serial on BE but this node is
+ // not serial, the pipeline has a 1-task serial child feeding an N-task non-serial
+ // parent. Without LE, pipeline splits (AGG/JOIN) create paired pipelines with
+ // mismatched num_tasks → crash. Upgrade noRequire to requirePassthrough so an
+ // LE is inserted below to restore parallelism.
+ if (require instanceof LocalExchangeNode.NoRequire
+ && childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())
+ && !isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ require = LocalExchangeTypeRequire.requirePassthrough();
+ }
+
+ // 4. Satisfy check: child output meets requirement → done
+ if (require.satisfy(childOutput.second)) {
+ return childOutput;
+ }
+
+ // 4. Layer 1: skip LE when serial operator or ancestor in same pipeline
+ // Equivalent to BE's need_to_local_exchange: any_of(operators[idx..end], is_serial) → skip
+ if (translatorContext.hasSerialAncestorInPipeline(this) || isSerialNode()) {
+ return childOutput;
Review Comment:
Good catch — fixed in 15d92ba024b. Both the Layer 1 skip and the serial-ancestor propagation in `enforceRequire` now use `isSerialOperatorOnBe(translatorContext.getConnectContext())` instead of the raw `isSerialNode()`.
Verified that BE's `OperatorBase` constructs from the Thrift `is_serial_operator` flag (which FE writes via `isSerialOperatorOnBe`, not `isSerialNode`) — so `Pipeline::need_to_local_exchange`'s `op->is_serial_operator()` check returns false when `fragment.useSerialSource(ctx)` is false, even if `isSerialNode()` is true. The previous code would have over-skipped LocalExchange in exactly the scenarios you listed (`ignore_storage_data_distribution=false`, query cache, NAAJ).
Updated `LocalShuffleNodeCoverageTest.testMaterializationNode` and `testSetOperationAndAssertNumRowsNode` to reflect the corrected behavior: in the fragment-less unit-test path `isSerialOperatorOnBe` returns false (the `fragment != null` guard) so the framework no longer skips Layer 1 and inserts the required LocalExchange.
Other `isSerialNode()` call sites in PlanNode.java were audited and left as-is:
- `toThrift()` already uses `isSerialOperatorOnBe`
- `hasSerialChildren()` is a pure node-level tree walk used only for fragment-internal heuristics
- `createLocalExchange()` heavy-op gate is already inside a `fragment.useSerialSource(ctx)` branch, so `isSerialNode` and `isSerialOperatorOnBe` are equivalent there
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4479135984
# FE UT Coverage Report
Increment line coverage ` 73.93% (431/583)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_15d92ba024b265d9441145d7bd26d93590aa9c63/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_15d92ba024b265d9441145d7bd26d93590aa9c63/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4476744283
/review
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4506413620
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4508244661
# FE Regression Coverage Report
Increment line coverage ` 80.07% (486/607)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_846d365a88901fa32fdf44b42b6cd04b900da259_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_846d365a88901fa32fdf44b42b6cd04b900da259_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4679953916
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4681351704
<details>
<summary>TPC-DS: <b>Total hot run time: 169122 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 784476ead971eeebeb0b637f6da4a434e380ce36, data reload: false
query5 4308 607 472 472
query6 441 196 171 171
query7 4868 553 291 291
query8 369 219 203 203
query9 8761 3991 3991 3991
query10 445 308 264 264
query11 5985 2348 2166 2166
query12 150 102 99 99
query13 1255 609 440 440
query14 6414 5335 5013 5013
query14_1 4358 4375 4344 4344
query15 199 192 180 180
query16 1021 443 420 420
query17 1102 669 541 541
query18 2552 459 337 337
query19 202 172 136 136
query20 121 107 105 105
query21 214 137 116 116
query22 13606 13622 13502 13502
query23 17416 16526 16210 16210
query23_1 16217 16392 16323 16323
query24 7498 1774 1288 1288
query24_1 1328 1322 1313 1313
query25 560 453 384 384
query26 1304 323 161 161
query27 2676 548 336 336
query28 4444 1990 2004 1990
query29 1127 615 492 492
query30 315 239 203 203
query31 1102 1073 953 953
query32 114 61 61 61
query33 538 327 259 259
query34 1178 1158 658 658
query35 754 800 669 669
query36 1400 1408 1245 1245
query37 159 109 96 96
query38 3231 3128 3040 3040
query39 939 917 897 897
query39_1 900 903 880 880
query40 222 123 104 104
query41 73 67 68 67
query42 98 97 96 96
query43 321 328 289 289
query44
query45 201 186 181 181
query46 1050 1197 759 759
query47 2386 2440 2266 2266
query48 401 420 298 298
query49 633 514 378 378
query50 1017 374 265 265
query51 4452 4402 4298 4298
query52 91 92 80 80
query53 257 266 193 193
query54 280 235 209 209
query55 81 75 75 75
query56 268 243 239 239
query57 1466 1415 1361 1361
query58 248 210 223 210
query59 1609 1643 1446 1446
query60 293 259 248 248
query61 184 201 155 155
query62 685 648 592 592
query63 231 180 187 180
query64 2557 795 582 582
query65
query66 1798 461 339 339
query67 29668 29620 29483 29483
query68
query69 423 297 264 264
query70 971 960 950 950
query71 296 215 202 202
query72 2931 2641 2360 2360
query73 835 768 435 435
query74 5130 4967 4806 4806
query75 2655 2566 2227 2227
query76 2300 1142 780 780
query77 342 370 296 296
query78 12331 12461 11815 11815
query79 1465 1062 758 758
query80 787 477 365 365
query81 475 278 237 237
query82 563 152 117 117
query83 344 280 247 247
query84
query85 891 513 422 422
query86 411 311 273 273
query87 3372 3320 3161 3161
query88 3578 2709 2703 2703
query89 418 379 331 331
query90 1770 174 174 174
query91 178 159 165 159
query92 65 60 58 58
query93 1437 1475 968 968
query94 602 356 307 307
query95 655 384 363 363
query96 1101 881 338 338
query97 2715 2717 2607 2607
query98 221 207 201 201
query99 1156 1189 1023 1023
Total cold run time: 250999 ms
Total hot run time: 169122 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4674136542
<details>
<summary>TPC-H: <b>Total hot run time: 28685 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 7e15d5b3b9109d275feadd1af404dcc86ee9b487, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17744 4275 4026 4026
q2 q3 10665 1391 828 828
q4 4690 477 347 347
q5 7608 879 596 596
q6 190 179 145 145
q7 810 847 651 651
q8 9386 1625 1553 1553
q9 5562 4467 4499 4467
q10 6741 1827 1517 1517
q11 452 280 266 266
q12 629 426 294 294
q13 18141 3438 2802 2802
q14 298 262 247 247
q15 q16 818 772 709 709
q17 5288 1050 785 785
q18 6923 5724 5634 5634
q19 2805 1352 953 953
q20 520 417 266 266
q21 5981 2592 2298 2298
q22 434 356 301 301
Total cold run time: 105685 ms
Total hot run time: 28685 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4434 4276 4314 4276
q2 q3 4514 4906 4306 4306
q4 2097 2201 1401 1401
q5 4419 4323 4695 4323
q6 254 204 145 145
q7 2120 1803 1688 1688
q8 2515 2202 2208 2202
q9 8267 8301 8383 8301
q10 4879 4916 4369 4369
q11 579 445 404 404
q12 796 761 545 545
q13 3451 3789 3147 3147
q14 319 319 290 290
q15 q16 755 769 653 653
q17 1404 1351 1314 1314
q18 7901 7330 6818 6818
q19 1122 1108 1106 1106
q20 2279 2304 1952 1952
q21 5275 4603 4497 4497
q22 519 473 422 422
Total cold run time: 57899 ms
Total hot run time: 52159 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4682187087
/review
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4668765617
Thanks for the thorough review! Addressed in 089459fbaae (review fixes) + 7c898c90050 (num_tasks). Verified against the local-shuffle regression (`test_local_shuffle_rqg_bugs`: 25 bugs, FE==BE, no crash) + the FE planner unit tests; `force_passthrough` additionally checked on a 4-BE cluster.
### @morrySnow
- **[P1] throw instead of log (AddLocalExchange):** restored to `throw` — this was originally an assert, downgraded to a warning while debugging. Verified it never misfires across the regression + unit tests.
- **[P2] make the regression fail the suite (`test_local_shuffle_rqg_bugs`):** Bug 3 now asserts FE==BE (row count + order-insensitive content); Bug 4/5 raise `assertTrue(false)` on an unexpected exception instead of only logging. (Bug 1/2/6/7/8 already asserted in their catch.)
- **[P2] preserve `enable_broadcast_join_force_passthrough` (HashJoinNode):** now mirrored. Investigated on a 4-BE cluster with a forced non-serial probe (`experimental_ignore_storage_data_distribution=false`): it is a **no-op** there — `enforceRequire` only inserts a PASSTHROUGH local exchange to fan a *serial* (1-task) source out to N tasks; an already-N-task source satisfies passthrough, so no exchange is added (identical plan and results vs BE-native, no crash). The check is kept to match BE's intent; a true rebalance of a non-serial probe (forcing the exchange) is a perf-only follow-up.
- **🟡 `resolveExchangeType` unused `child` param:** removed all three unused params (`translatorContext`/`parent`/`child`) — only `require` is read. Updated all 6 call sites + the unit test (also resolves the SetOperationNode null-`firstChild` note).
- **🟠 `_propagate_local_exchange_num_tasks` convergence loops:** replaced the two iterative `while (changed)` passes with a single Kahn topological sweep over the pipeline DAG — each pipeline is visited once, after all its upstreams, so num_tasks is set in one pass. It can't loop infinitely; added a cycle `DCHECK` instead of a max-iteration guard. (Commit 7c898c90050.)
- **🟡 build-side comment (HashJoinNode):** added a note that both probe and build sides require `BUCKET_HASH_SHUFFLE`.
- **🟢 `_operator_id` starting from -1 / ✅ AggregationNode correctness:** thanks — acknowledged, no change needed.
### @BiteTheDDDDt
- **`local_exchange_sink_operator` redundancy with `init_partitioner`:** extracted `_create_partitioner(state, bucket_count)`, now shared by `init()` (BE-native) and `init_partitioner()` (FE-planned) — removes the three duplicated hash-partitioner blocks.
- **BE computing `required_data_distribution` / parallelism:** retained for the **BE-native fallback** path (`enable_local_shuffle_planner=false`). The FE-planned path doesn't read it, but keeping it preserves the BE-native planner as a fallback option.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4708089765
run buildall
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4738677003
# BE UT Coverage Report
Increment line coverage `15.27% (62/406)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/0436cc4cb573b104a59408a6169bd145bcfeff6c_0436cc4cb573b104a59408a6169bd145bcfeff6c/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/0436cc4cb573b104a59408a6169bd145bcfeff6c_0436cc4cb573b104a59408a6169bd145bcfeff6c/report/index.html)
| Category | Coverage |
|-------------------|--------------------|
| Function Coverage | 54.45% (21370/39248) |
| Line Coverage | 38.06% (204368/536967) |
| Region Coverage | 34.07% (160400/470836) |
| Branch Coverage | 35.05% (70191/200259) |
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3417864896
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -939,4 +1031,263 @@ private String mergeIcebergAccessPathsWithId(
}
return StringUtils.join(mergeDisplayAccessPaths, ", ");
}
+
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ ArrayList<PlanNode> newChildren = Lists.newArrayList();
+ for (int i = 0; i < children.size(); i++) {
+ Pair<PlanNode, LocalExchangeType> childOutput
+ = enforceRequire(translatorContext, children.get(i), i, LocalExchangeTypeRequire.noRequire());
+ newChildren.add(childOutput.first);
+ }
+ this.children = newChildren;
+ return Pair.of(this, LocalExchangeType.NOOP);
+ }
+
+ /**
+ * Unified framework method: propagate serial flag → recurse child → satisfy check → Layer 1 skip → insert LE.
+ * Replaces the old enforceChild/enforceChildExchange/forceEnforceChildExchange trio.
+ *
+ * <h3>Data flow</h3>
+ * <ul>
+ * <li><b>serial-ancestor flag</b> ({@link PlanTranslatorContext#hasSerialAncestorInPipeline})
+ * — flows root → leaf during traversal. Mirrors BE's
+ * {@code any_of(operators[idx..end], is_serial_operator)} check used by
+ * {@code _add_local_exchange} to skip LE insertion when an ancestor in the same
+ * pipeline is already serial. Reset at pipeline boundaries via
+ * {@link #shouldResetSerialFlagForChild}.</li>
+ * <li><b>shuffle-for-correctness flag</b>
+ * ({@link PlanTranslatorContext#hasShuffleForCorrectnessAncestor}) — also flows
+ * root → leaf. Mirrors BE's {@code _followed_by_shuffled_operator}: tells a
+ * child whether some downstream operator depends on hash distribution for
+ * correctness, so {@code SetOperationNode} can pre-shuffle union branches.</li>
+ * <li><b>return value</b> {@code Pair<PlanNode, LocalExchangeType>} — first is the
+ * (possibly LE-wrapped) child; second is the actual output distribution as
+ * observed by the parent. Caller's {@code require.satisfy(output)} decides
+ * whether more LE is needed.</li>
+ * <li><b>parent.require</b> describes the constraint on the child output —
+ * computed inside the parent's {@code enforceAndDeriveLocalExchange} per child.</li>
+ * </ul>
+ *
+ * <h3>Invariants</h3>
+ * <ul>
+ * <li>Where a serial → non-serial transition needs redistribution, framework step 3 inserts
+ * the LE (e.g. a serial source fanned out via PASSTHROUGH). This is not a hard invariant:
+ * a serial child feeding a parent that requires PASSTHROUGH / noRequire (TableFunction,
+ * NLJ, Agg) is already correct and needs no LE, so it is intentionally not validated by a
+ * post-pass.</li>
+ * <li>{@code LocalExchangeNode} itself is always non-serial — setting it serial
+ * would defeat its purpose of fanning a 1-task pipeline back to N tasks.</li>
+ * <li>For pipeline-breaking parents ({@code shouldResetSerialFlagForChild=true}),
+ * the child starts a fresh pipeline so {@code hasSerialAncestor} is reset; the
+ * node's own {@code isSerialNode()} still composes in for the child's view.</li>
+ * <li>{@code RequireHash} accepts any hash flavour; {@code RequireSpecific} demands
+ * an exact match (with the one PASSTHROUGH/ADAPTIVE_PASSTHROUGH compatibility).
+ * Pick the looser one whenever correctness allows — see
+ * {@link LocalExchangeNode.LocalExchangeTypeRequire}.</li>
+ * </ul>
+ *
+ * <h3>Layers</h3>
+ * Layer 1 (shouldSkipLE): mirrors BE's need_to_local_exchange — skip when this node or
+ * an ancestor in the same pipeline is serial (operators[idx..end] has serial → skip).
+ * Layer 2 (require/output): each Node declares require and output in enforceAndDeriveLocalExchange.
+ */
+ protected Pair<PlanNode, LocalExchangeType> enforceRequire(
+ PlanTranslatorContext translatorContext, PlanNode child, int childIndex,
+ LocalExchangeTypeRequire require) {
+ // 1. Propagate serial-ancestor flag to child.
+ // For pipeline-splitting operators (shouldReset=true, e.g. non-streaming AGG):
+ // Drop inherited serial flag from parent (parent is in a different pipeline),
+ // but keep this node's own serial status (child is in the same pipeline as this
+ // node's sink, e.g. Exchange is in AGG_Sink pipeline).
+ // For non-splitting operators (shouldReset=false, e.g. streaming AGG):
+ // Inherit parent's serial flag + this node's own.
+ boolean inheritedSerial = shouldResetSerialFlagForChild(childIndex)
+ ? false : translatorContext.hasSerialAncestorInPipeline(this);
+ // Use isSerialOperatorOnBe (= isSerialNode && fragment.useSerialSource) instead of the
+ // raw isSerialNode(). BE's OperatorBase reads the Thrift `is_serial_operator` flag —
+ // which is what FE writes via isSerialOperatorOnBe — so when the fragment is not in
+ // serial-source mode, BE treats this operator as non-serial regardless of isSerialNode.
+ // Using isSerialNode here would set the child's serial-ancestor flag wider than BE's
+ // view and over-skip required LocalExchanges downstream.
+ boolean childHasSerialAncestor = inheritedSerial
+ || isSerialOperatorOnBe(translatorContext.getConnectContext());
+ translatorContext.setHasSerialAncestorInPipeline(child, childHasSerialAncestor);
+
+ // 1b. Propagate shuffle-for-correctness-ancestor flag to child.
+ // Mirrors BE's _followed_by_shuffled_operator: a downstream operator needs hash
+ // distribution for correctness, and the chain to here goes through HASH or NOOP
+ // requirements (so the dependency is preserved).
+ // propagate = ((inheritedShuffled || self.requiresShuffleForCorrectness)
+ // && require is hash)
+ // || (inheritedShuffled && require is noop/passthrough)
+ boolean inheritedShuffled = translatorContext.hasShuffleForCorrectnessAncestor(this);
+ boolean selfOrInheritedShuffled = inheritedShuffled || requiresShuffleForCorrectness();
+ boolean requireIsHash = require.preferType().isHashShuffle();
+ boolean requireIsNoop = require.preferType() == LocalExchangeNode.LocalExchangeType.NOOP;
+ boolean childShuffledAncestor = (selfOrInheritedShuffled && requireIsHash)
+ || (inheritedShuffled && requireIsNoop);
+ translatorContext.setHasShuffleForCorrectnessAncestor(child, childShuffledAncestor);
+
+ // 2. Recurse child (Layer 2: child declares its own require/output)
+ Pair<PlanNode, LocalExchangeType> childOutput =
+ child.enforceAndDeriveLocalExchange(translatorContext, this, require);
+
+ // Steps 2.5 and 3 both react to a serial child but address different concerns:
+ // - Step 2.5 rewrites the OUTPUT-side view (what we tell satisfy/parent about
+ // the child's actual distribution). A serial pipeline runs with 1 task so
+ // its distribution claim is meaningless — flatten to NOOP so the satisfy
+ // check below doesn't get fooled by a stale "I output BUCKET_HASH" claim.
+ // - Step 3 rewrites the REQUIRE-side decision (what we want from the child).
+ // If we previously asked for nothing (noRequire) but the child turns out
+ // to be serial and we're not, upgrade to requirePassthrough so an LE is
+ // inserted to restore parallelism.
+
+ // 2.5. Serial child override (output side): if child is serial on BE, force its
+ // reported output to NOOP. Distribution is irrelevant when the child runs
+ // with 1 task; downstream parallelism is restored either by step 3 (LE
+ // insertion) or skipped entirely by step 4b (we're also serial).
+ if (childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ childOutput = Pair.of(childOutput.first, LocalExchangeType.NOOP);
+ }
+
+ // 3. Framework-level serial child check (require side, mirrors BE base class
+ // required_data_distribution): if child will be serial on BE but this node is
+ // not serial, the pipeline has a 1-task serial child feeding an N-task non-serial
+ // parent. Without LE, pipeline splits (AGG/JOIN) create paired pipelines with
+ // mismatched num_tasks → crash. Upgrade noRequire to requirePassthrough so an
+ // LE is inserted below to restore parallelism.
+ if (require instanceof LocalExchangeNode.NoRequire
+ && childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())
+ && !isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ require = LocalExchangeTypeRequire.requirePassthrough();
+ }
+
+ // 4. Satisfy check: child output meets requirement → done
+ if (require.satisfy(childOutput.second)) {
+ return childOutput;
+ }
+
+ // 4. Layer 1: skip LE when serial operator or ancestor in same pipeline
+ // Equivalent to BE's need_to_local_exchange: any_of(operators[idx..end], is_serial) → skip.
+ // Use isSerialOperatorOnBe (not isSerialNode) because BE's Pipeline::need_to_local_exchange
+ // checks op->is_serial_operator() which reads the Thrift flag set from isSerialOperatorOnBe;
+ // when fragment.useSerialSource is false, BE treats this node as non-serial.
+ if (translatorContext.hasSerialAncestorInPipeline(this)
Review Comment:
This mirrors BE's `Pipeline::need_to_local_exchange` exactly (`pipeline.cpp:41-45`):
```cpp
// If serial operator exists after `idx`-th operator, we should not improve parallelism.
if (std::any_of(_operators.begin() + idx, _operators.end(),
[](OperatorPtr op){ return op->is_serial_operator(); })) {
return false;
}
```
`hasSerialAncestorInPipeline(this)` = a serial operator exists between this node and the pipeline's sink (i.e. an operator after `idx`); `isSerialOperatorOnBe(this)` = the operator at `idx` is serial. Together they are `any_of(operators[idx..end], is_serial)`. The reason it skips the LE: if any operator from here toward the sink is serial, that downstream segment runs with a single task — there is no parallelism to rebalance, so a local exchange would be pointless (and BE skips it for the same reason).
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -939,4 +1031,263 @@ private String mergeIcebergAccessPathsWithId(
}
return StringUtils.join(mergeDisplayAccessPaths, ", ");
}
+
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ ArrayList<PlanNode> newChildren = Lists.newArrayList();
+ for (int i = 0; i < children.size(); i++) {
+ Pair<PlanNode, LocalExchangeType> childOutput
+ = enforceRequire(translatorContext, children.get(i), i, LocalExchangeTypeRequire.noRequire());
+ newChildren.add(childOutput.first);
+ }
+ this.children = newChildren;
+ return Pair.of(this, LocalExchangeType.NOOP);
+ }
+
+ /**
+ * Unified framework method: propagate serial flag → recurse child → satisfy check → Layer 1 skip → insert LE.
+ * Replaces the old enforceChild/enforceChildExchange/forceEnforceChildExchange trio.
+ *
+ * <h3>Data flow</h3>
+ * <ul>
+ * <li><b>serial-ancestor flag</b> ({@link PlanTranslatorContext#hasSerialAncestorInPipeline})
+ * — flows root → leaf during traversal. Mirrors BE's
+ * {@code any_of(operators[idx..end], is_serial_operator)} check used by
+ * {@code _add_local_exchange} to skip LE insertion when an ancestor in the same
+ * pipeline is already serial. Reset at pipeline boundaries via
+ * {@link #shouldResetSerialFlagForChild}.</li>
+ * <li><b>shuffle-for-correctness flag</b>
+ * ({@link PlanTranslatorContext#hasShuffleForCorrectnessAncestor}) — also flows
+ * root → leaf. Mirrors BE's {@code _followed_by_shuffled_operator}: tells a
+ * child whether some downstream operator depends on hash distribution for
+ * correctness, so {@code SetOperationNode} can pre-shuffle union branches.</li>
+ * <li><b>return value</b> {@code Pair<PlanNode, LocalExchangeType>} — first is the
+ * (possibly LE-wrapped) child; second is the actual output distribution as
+ * observed by the parent. Caller's {@code require.satisfy(output)} decides
+ * whether more LE is needed.</li>
+ * <li><b>parent.require</b> describes the constraint on the child output —
+ * computed inside the parent's {@code enforceAndDeriveLocalExchange} per child.</li>
+ * </ul>
+ *
+ * <h3>Invariants</h3>
+ * <ul>
+ * <li>Where a serial → non-serial transition needs redistribution, framework step 3 inserts
+ * the LE (e.g. a serial source fanned out via PASSTHROUGH). This is not a hard invariant:
+ * a serial child feeding a parent that requires PASSTHROUGH / noRequire (TableFunction,
+ * NLJ, Agg) is already correct and needs no LE, so it is intentionally not validated by a
+ * post-pass.</li>
+ * <li>{@code LocalExchangeNode} itself is always non-serial — setting it serial
+ * would defeat its purpose of fanning a 1-task pipeline back to N tasks.</li>
+ * <li>For pipeline-breaking parents ({@code shouldResetSerialFlagForChild=true}),
+ * the child starts a fresh pipeline so {@code hasSerialAncestor} is reset; the
+ * node's own {@code isSerialNode()} still composes in for the child's view.</li>
+ * <li>{@code RequireHash} accepts any hash flavour; {@code RequireSpecific} demands
+ * an exact match (with the one PASSTHROUGH/ADAPTIVE_PASSTHROUGH compatibility).
+ * Pick the looser one whenever correctness allows — see
+ * {@link LocalExchangeNode.LocalExchangeTypeRequire}.</li>
+ * </ul>
+ *
+ * <h3>Layers</h3>
+ * Layer 1 (shouldSkipLE): mirrors BE's need_to_local_exchange — skip when this node or
+ * an ancestor in the same pipeline is serial (operators[idx..end] has serial → skip).
+ * Layer 2 (require/output): each Node declares require and output in enforceAndDeriveLocalExchange.
+ */
+ protected Pair<PlanNode, LocalExchangeType> enforceRequire(
+ PlanTranslatorContext translatorContext, PlanNode child, int childIndex,
+ LocalExchangeTypeRequire require) {
+ // 1. Propagate serial-ancestor flag to child.
+ // For pipeline-splitting operators (shouldReset=true, e.g. non-streaming AGG):
+ // Drop inherited serial flag from parent (parent is in a different pipeline),
+ // but keep this node's own serial status (child is in the same pipeline as this
+ // node's sink, e.g. Exchange is in AGG_Sink pipeline).
+ // For non-splitting operators (shouldReset=false, e.g. streaming AGG):
+ // Inherit parent's serial flag + this node's own.
+ boolean inheritedSerial = shouldResetSerialFlagForChild(childIndex)
+ ? false : translatorContext.hasSerialAncestorInPipeline(this);
+ // Use isSerialOperatorOnBe (= isSerialNode && fragment.useSerialSource) instead of the
+ // raw isSerialNode(). BE's OperatorBase reads the Thrift `is_serial_operator` flag —
+ // which is what FE writes via isSerialOperatorOnBe — so when the fragment is not in
+ // serial-source mode, BE treats this operator as non-serial regardless of isSerialNode.
+ // Using isSerialNode here would set the child's serial-ancestor flag wider than BE's
+ // view and over-skip required LocalExchanges downstream.
+ boolean childHasSerialAncestor = inheritedSerial
+ || isSerialOperatorOnBe(translatorContext.getConnectContext());
+ translatorContext.setHasSerialAncestorInPipeline(child, childHasSerialAncestor);
+
+ // 1b. Propagate shuffle-for-correctness-ancestor flag to child.
+ // Mirrors BE's _followed_by_shuffled_operator: a downstream operator needs hash
+ // distribution for correctness, and the chain to here goes through HASH or NOOP
+ // requirements (so the dependency is preserved).
+ // propagate = ((inheritedShuffled || self.requiresShuffleForCorrectness)
+ // && require is hash)
+ // || (inheritedShuffled && require is noop/passthrough)
+ boolean inheritedShuffled = translatorContext.hasShuffleForCorrectnessAncestor(this);
+ boolean selfOrInheritedShuffled = inheritedShuffled || requiresShuffleForCorrectness();
+ boolean requireIsHash = require.preferType().isHashShuffle();
+ boolean requireIsNoop = require.preferType() == LocalExchangeNode.LocalExchangeType.NOOP;
+ boolean childShuffledAncestor = (selfOrInheritedShuffled && requireIsHash)
+ || (inheritedShuffled && requireIsNoop);
+ translatorContext.setHasShuffleForCorrectnessAncestor(child, childShuffledAncestor);
+
+ // 2. Recurse child (Layer 2: child declares its own require/output)
+ Pair<PlanNode, LocalExchangeType> childOutput =
+ child.enforceAndDeriveLocalExchange(translatorContext, this, require);
+
+ // Steps 2.5 and 3 both react to a serial child but address different concerns:
+ // - Step 2.5 rewrites the OUTPUT-side view (what we tell satisfy/parent about
+ // the child's actual distribution). A serial pipeline runs with 1 task so
+ // its distribution claim is meaningless — flatten to NOOP so the satisfy
+ // check below doesn't get fooled by a stale "I output BUCKET_HASH" claim.
+ // - Step 3 rewrites the REQUIRE-side decision (what we want from the child).
+ // If we previously asked for nothing (noRequire) but the child turns out
+ // to be serial and we're not, upgrade to requirePassthrough so an LE is
+ // inserted to restore parallelism.
+
+ // 2.5. Serial child override (output side): if child is serial on BE, force its
+ // reported output to NOOP. Distribution is irrelevant when the child runs
+ // with 1 task; downstream parallelism is restored either by step 3 (LE
+ // insertion) or skipped entirely by step 4b (we're also serial).
+ if (childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ childOutput = Pair.of(childOutput.first, LocalExchangeType.NOOP);
+ }
+
+ // 3. Framework-level serial child check (require side, mirrors BE base class
+ // required_data_distribution): if child will be serial on BE but this node is
+ // not serial, the pipeline has a 1-task serial child feeding an N-task non-serial
+ // parent. Without LE, pipeline splits (AGG/JOIN) create paired pipelines with
+ // mismatched num_tasks → crash. Upgrade noRequire to requirePassthrough so an
+ // LE is inserted below to restore parallelism.
+ if (require instanceof LocalExchangeNode.NoRequire
+ && childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())
+ && !isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ require = LocalExchangeTypeRequire.requirePassthrough();
+ }
+
+ // 4. Satisfy check: child output meets requirement → done
+ if (require.satisfy(childOutput.second)) {
+ return childOutput;
+ }
+
+ // 4. Layer 1: skip LE when serial operator or ancestor in same pipeline
+ // Equivalent to BE's need_to_local_exchange: any_of(operators[idx..end], is_serial) → skip.
+ // Use isSerialOperatorOnBe (not isSerialNode) because BE's Pipeline::need_to_local_exchange
+ // checks op->is_serial_operator() which reads the Thrift flag set from isSerialOperatorOnBe;
+ // when fragment.useSerialSource is false, BE treats this node as non-serial.
+ if (translatorContext.hasSerialAncestorInPipeline(this)
+ || isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ return childOutput;
+ }
+
+ // 5. Resolve exchange type and create LE node
+ LocalExchangeType preferType = AddLocalExchange.resolveExchangeType(require);
+ List<Expr> distributeExprs = getLocalExchangeDistributeExprs(childIndex, selfOrInheritedShuffled);
+ PlanNode leNode = createLocalExchange(translatorContext, childOutput.first, preferType, distributeExprs);
+ return Pair.of(leNode, preferType);
+ }
+
+ /**
+ * Create a LocalExchangeNode wrapping child with the given exchange type.
+ * No child-type skip — matches BE's _add_local_exchange which inserts LE for any child
+ * type without checking instanceof.
+ *
+ * Handles heavy-ops bottleneck avoidance (mirrors BE pipeline_fragment_context.cpp):
+ * when upstream has 1 task (serial source) and exchange is heavy (hash/bucket/adaptive),
+ * insert a PASSTHROUGH fan-out first to avoid single-task bottleneck on the heavy
+ * exchange sink. Only applies to local-shuffle (pooling scan) fragments.
+ */
+ protected PlanNode createLocalExchange(PlanTranslatorContext translatorContext,
+ PlanNode child, LocalExchangeType exchangeType, List<Expr> distributeExprs) {
+ if (fragment != null && fragment.useSerialSource(translatorContext.getConnectContext())
+ && exchangeType.isHeavyOperation() && child.isSerialNode()) {
+ PlanNode ptNode = new LocalExchangeNode(translatorContext.nextPlanNodeId(),
+ child, LocalExchangeType.PASSTHROUGH, null);
+ return new LocalExchangeNode(translatorContext.nextPlanNodeId(), ptNode,
+ exchangeType, distributeExprs);
+ }
+ return new LocalExchangeNode(translatorContext.nextPlanNodeId(), child,
+ exchangeType, distributeExprs);
+ }
+
+ /**
+ * Whether the child at {@code childIndex} starts a new pipeline context, causing
+ * its serial-ancestor flag to be reset to {@code false} rather than inherited from this node.
+ * Override to return {@code true} for pipeline-splitting nodes (LocalExchangeNode) and nodes
+ * whose children run in an independent pipeline segment (SortNode above analytic, etc.).
Review Comment:
Good catch — you're right, fixed in `8f1befd3016` (`above` → `before`). The window-input `SortNode` is the **child** of `AnalyticEvalNode`: `PhysicalPlanTranslator.visitPhysicalWindow` does `inputPlanFragment.addPlanRoot(analyticEvalNode)`, which stacks the analytic node on top of its input sort. So the SortNode is below/before the analytic, not above it.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -939,4 +1031,263 @@ private String mergeIcebergAccessPathsWithId(
}
return StringUtils.join(mergeDisplayAccessPaths, ", ");
}
+
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ ArrayList<PlanNode> newChildren = Lists.newArrayList();
+ for (int i = 0; i < children.size(); i++) {
+ Pair<PlanNode, LocalExchangeType> childOutput
+ = enforceRequire(translatorContext, children.get(i), i, LocalExchangeTypeRequire.noRequire());
+ newChildren.add(childOutput.first);
+ }
+ this.children = newChildren;
+ return Pair.of(this, LocalExchangeType.NOOP);
+ }
+
+ /**
+ * Unified framework method: propagate serial flag → recurse child → satisfy check → Layer 1 skip → insert LE.
+ * Replaces the old enforceChild/enforceChildExchange/forceEnforceChildExchange trio.
+ *
+ * <h3>Data flow</h3>
+ * <ul>
+ * <li><b>serial-ancestor flag</b> ({@link PlanTranslatorContext#hasSerialAncestorInPipeline})
+ * — flows root → leaf during traversal. Mirrors BE's
+ * {@code any_of(operators[idx..end], is_serial_operator)} check used by
+ * {@code _add_local_exchange} to skip LE insertion when an ancestor in the same
+ * pipeline is already serial. Reset at pipeline boundaries via
+ * {@link #shouldResetSerialFlagForChild}.</li>
+ * <li><b>shuffle-for-correctness flag</b>
+ * ({@link PlanTranslatorContext#hasShuffleForCorrectnessAncestor}) — also flows
+ * root → leaf. Mirrors BE's {@code _followed_by_shuffled_operator}: tells a
+ * child whether some downstream operator depends on hash distribution for
+ * correctness, so {@code SetOperationNode} can pre-shuffle union branches.</li>
+ * <li><b>return value</b> {@code Pair<PlanNode, LocalExchangeType>} — first is the
+ * (possibly LE-wrapped) child; second is the actual output distribution as
+ * observed by the parent. Caller's {@code require.satisfy(output)} decides
+ * whether more LE is needed.</li>
+ * <li><b>parent.require</b> describes the constraint on the child output —
+ * computed inside the parent's {@code enforceAndDeriveLocalExchange} per child.</li>
+ * </ul>
+ *
+ * <h3>Invariants</h3>
+ * <ul>
+ * <li>Where a serial → non-serial transition needs redistribution, framework step 3 inserts
+ * the LE (e.g. a serial source fanned out via PASSTHROUGH). This is not a hard invariant:
+ * a serial child feeding a parent that requires PASSTHROUGH / noRequire (TableFunction,
+ * NLJ, Agg) is already correct and needs no LE, so it is intentionally not validated by a
+ * post-pass.</li>
+ * <li>{@code LocalExchangeNode} itself is always non-serial — setting it serial
+ * would defeat its purpose of fanning a 1-task pipeline back to N tasks.</li>
+ * <li>For pipeline-breaking parents ({@code shouldResetSerialFlagForChild=true}),
+ * the child starts a fresh pipeline so {@code hasSerialAncestor} is reset; the
+ * node's own {@code isSerialNode()} still composes in for the child's view.</li>
+ * <li>{@code RequireHash} accepts any hash flavour; {@code RequireSpecific} demands
+ * an exact match (with the one PASSTHROUGH/ADAPTIVE_PASSTHROUGH compatibility).
+ * Pick the looser one whenever correctness allows — see
+ * {@link LocalExchangeNode.LocalExchangeTypeRequire}.</li>
+ * </ul>
+ *
+ * <h3>Layers</h3>
+ * Layer 1 (shouldSkipLE): mirrors BE's need_to_local_exchange — skip when this node or
+ * an ancestor in the same pipeline is serial (operators[idx..end] has serial → skip).
+ * Layer 2 (require/output): each Node declares require and output in enforceAndDeriveLocalExchange.
+ */
+ protected Pair<PlanNode, LocalExchangeType> enforceRequire(
+ PlanTranslatorContext translatorContext, PlanNode child, int childIndex,
+ LocalExchangeTypeRequire require) {
+ // 1. Propagate serial-ancestor flag to child.
+ // For pipeline-splitting operators (shouldReset=true, e.g. non-streaming AGG):
+ // Drop inherited serial flag from parent (parent is in a different pipeline),
+ // but keep this node's own serial status (child is in the same pipeline as this
+ // node's sink, e.g. Exchange is in AGG_Sink pipeline).
+ // For non-splitting operators (shouldReset=false, e.g. streaming AGG):
+ // Inherit parent's serial flag + this node's own.
+ boolean inheritedSerial = shouldResetSerialFlagForChild(childIndex)
+ ? false : translatorContext.hasSerialAncestorInPipeline(this);
+ // Use isSerialOperatorOnBe (= isSerialNode && fragment.useSerialSource) instead of the
+ // raw isSerialNode(). BE's OperatorBase reads the Thrift `is_serial_operator` flag —
+ // which is what FE writes via isSerialOperatorOnBe — so when the fragment is not in
+ // serial-source mode, BE treats this operator as non-serial regardless of isSerialNode.
+ // Using isSerialNode here would set the child's serial-ancestor flag wider than BE's
+ // view and over-skip required LocalExchanges downstream.
+ boolean childHasSerialAncestor = inheritedSerial
+ || isSerialOperatorOnBe(translatorContext.getConnectContext());
+ translatorContext.setHasSerialAncestorInPipeline(child, childHasSerialAncestor);
+
+ // 1b. Propagate shuffle-for-correctness-ancestor flag to child.
+ // Mirrors BE's _followed_by_shuffled_operator: a downstream operator needs hash
+ // distribution for correctness, and the chain to here goes through HASH or NOOP
+ // requirements (so the dependency is preserved).
+ // propagate = ((inheritedShuffled || self.requiresShuffleForCorrectness)
+ // && require is hash)
+ // || (inheritedShuffled && require is noop/passthrough)
+ boolean inheritedShuffled = translatorContext.hasShuffleForCorrectnessAncestor(this);
+ boolean selfOrInheritedShuffled = inheritedShuffled || requiresShuffleForCorrectness();
+ boolean requireIsHash = require.preferType().isHashShuffle();
+ boolean requireIsNoop = require.preferType() == LocalExchangeNode.LocalExchangeType.NOOP;
+ boolean childShuffledAncestor = (selfOrInheritedShuffled && requireIsHash)
+ || (inheritedShuffled && requireIsNoop);
+ translatorContext.setHasShuffleForCorrectnessAncestor(child, childShuffledAncestor);
+
+ // 2. Recurse child (Layer 2: child declares its own require/output)
+ Pair<PlanNode, LocalExchangeType> childOutput =
+ child.enforceAndDeriveLocalExchange(translatorContext, this, require);
+
+ // Steps 2.5 and 3 both react to a serial child but address different concerns:
+ // - Step 2.5 rewrites the OUTPUT-side view (what we tell satisfy/parent about
+ // the child's actual distribution). A serial pipeline runs with 1 task so
+ // its distribution claim is meaningless — flatten to NOOP so the satisfy
+ // check below doesn't get fooled by a stale "I output BUCKET_HASH" claim.
+ // - Step 3 rewrites the REQUIRE-side decision (what we want from the child).
+ // If we previously asked for nothing (noRequire) but the child turns out
+ // to be serial and we're not, upgrade to requirePassthrough so an LE is
+ // inserted to restore parallelism.
+
+ // 2.5. Serial child override (output side): if child is serial on BE, force its
+ // reported output to NOOP. Distribution is irrelevant when the child runs
+ // with 1 task; downstream parallelism is restored either by step 3 (LE
+ // insertion) or skipped entirely by step 4b (we're also serial).
+ if (childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ childOutput = Pair.of(childOutput.first, LocalExchangeType.NOOP);
+ }
+
+ // 3. Framework-level serial child check (require side, mirrors BE base class
+ // required_data_distribution): if child will be serial on BE but this node is
+ // not serial, the pipeline has a 1-task serial child feeding an N-task non-serial
+ // parent. Without LE, pipeline splits (AGG/JOIN) create paired pipelines with
+ // mismatched num_tasks → crash. Upgrade noRequire to requirePassthrough so an
+ // LE is inserted below to restore parallelism.
+ if (require instanceof LocalExchangeNode.NoRequire
+ && childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())
+ && !isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ require = LocalExchangeTypeRequire.requirePassthrough();
+ }
+
+ // 4. Satisfy check: child output meets requirement → done
+ if (require.satisfy(childOutput.second)) {
+ return childOutput;
+ }
+
+ // 4. Layer 1: skip LE when serial operator or ancestor in same pipeline
+ // Equivalent to BE's need_to_local_exchange: any_of(operators[idx..end], is_serial) → skip.
+ // Use isSerialOperatorOnBe (not isSerialNode) because BE's Pipeline::need_to_local_exchange
+ // checks op->is_serial_operator() which reads the Thrift flag set from isSerialOperatorOnBe;
+ // when fragment.useSerialSource is false, BE treats this node as non-serial.
+ if (translatorContext.hasSerialAncestorInPipeline(this)
+ || isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ return childOutput;
+ }
+
+ // 5. Resolve exchange type and create LE node
+ LocalExchangeType preferType = AddLocalExchange.resolveExchangeType(require);
+ List<Expr> distributeExprs = getLocalExchangeDistributeExprs(childIndex, selfOrInheritedShuffled);
+ PlanNode leNode = createLocalExchange(translatorContext, childOutput.first, preferType, distributeExprs);
+ return Pair.of(leNode, preferType);
+ }
+
+ /**
+ * Create a LocalExchangeNode wrapping child with the given exchange type.
+ * No child-type skip — matches BE's _add_local_exchange which inserts LE for any child
+ * type without checking instanceof.
+ *
+ * Handles heavy-ops bottleneck avoidance (mirrors BE pipeline_fragment_context.cpp):
+ * when upstream has 1 task (serial source) and exchange is heavy (hash/bucket/adaptive),
+ * insert a PASSTHROUGH fan-out first to avoid single-task bottleneck on the heavy
+ * exchange sink. Only applies to local-shuffle (pooling scan) fragments.
+ */
+ protected PlanNode createLocalExchange(PlanTranslatorContext translatorContext,
+ PlanNode child, LocalExchangeType exchangeType, List<Expr> distributeExprs) {
+ if (fragment != null && fragment.useSerialSource(translatorContext.getConnectContext())
+ && exchangeType.isHeavyOperation() && child.isSerialNode()) {
+ PlanNode ptNode = new LocalExchangeNode(translatorContext.nextPlanNodeId(),
+ child, LocalExchangeType.PASSTHROUGH, null);
+ return new LocalExchangeNode(translatorContext.nextPlanNodeId(), ptNode,
+ exchangeType, distributeExprs);
+ }
+ return new LocalExchangeNode(translatorContext.nextPlanNodeId(), child,
+ exchangeType, distributeExprs);
+ }
+
+ /**
+ * Whether the child at {@code childIndex} starts a new pipeline context, causing
+ * its serial-ancestor flag to be reset to {@code false} rather than inherited from this node.
+ * Override to return {@code true} for pipeline-splitting nodes (LocalExchangeNode) and nodes
+ * whose children run in an independent pipeline segment (SortNode above analytic, etc.).
+ */
+ protected boolean shouldResetSerialFlagForChild(int childIndex) {
Review Comment:
Yes — `shouldResetSerialFlagForChild` defines FE's pipeline breakers, and it must match BE's pipeline construction. The serial-ancestor flag (`hasSerialAncestorInPipeline`, which drives the Layer-1 LE skip above) is computed per pipeline, so if FE and BE disagree on where a pipeline ends, the flag — and thus LE insertion — diverges from BE. The nodes that override it (`SortNode`, `AnalyticEvalNode`, `HashJoinNode`, `NestedLoopJoinNode`, `AggregationNode`, `SetOperationNode`, `PartitionSortNode`, `RecursiveCteNode`, `LocalExchangeNode`) are exactly BE's pipeline-breaking operators (sort sink, analytic, join build, agg sink, set op, …).
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4702158070
# FE Regression Coverage Report
Increment line coverage ` 80.17% (461/575)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_8db568b5100ed397fc6347033402642e90e5509e_merge_fe/increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_8db568b5100ed397fc6347033402642e90e5509e_merge_fe/report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4708776883
<details>
<summary>TPC-H: <b>Total hot run time: 29190 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit fe9a3316be056faed18351cf64096e36790c6c89, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17590 4182 4104 4104
q2 q3 10761 1445 847 847
q4 4689 480 338 338
q5 7540 889 583 583
q6 187 174 140 140
q7 797 863 632 632
q8 9388 1579 1636 1579
q9 5661 4519 4585 4519
q10 6761 1811 1523 1523
q11 441 270 262 262
q12 636 427 288 288
q13 18098 3531 2861 2861
q14 271 255 242 242
q15 q16 785 788 712 712
q17 888 899 991 899
q18 6965 5781 5579 5579
q19 1161 1292 1103 1103
q20 494 409 261 261
q21 5240 2628 2416 2416
q22 441 360 302 302
Total cold run time: 98794 ms
Total hot run time: 29190 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4471 4343 4377 4343
q2 q3 4531 4958 4439 4439
q4 2099 2218 1386 1386
q5 4521 4323 4319 4319
q6 224 178 132 132
q7 1738 1680 1604 1604
q8 2897 2256 2165 2165
q9 8351 8373 8233 8233
q10 4833 4758 4350 4350
q11 563 443 376 376
q12 741 757 542 542
q13 3319 3686 2979 2979
q14 300 303 287 287
q15 q16 728 760 633 633
q17 1372 1348 1332 1332
q18 8095 7429 7344 7344
q19 1180 1122 1135 1122
q20 2238 2223 1953 1953
q21 5311 4569 4464 4464
q22 573 446 408 408
Total cold run time: 58085 ms
Total hot run time: 52411 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3414779422
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -939,4 +1031,263 @@ private String mergeIcebergAccessPathsWithId(
}
return StringUtils.join(mergeDisplayAccessPaths, ", ");
}
+
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ ArrayList<PlanNode> newChildren = Lists.newArrayList();
+ for (int i = 0; i < children.size(); i++) {
+ Pair<PlanNode, LocalExchangeType> childOutput
+ = enforceRequire(translatorContext, children.get(i), i, LocalExchangeTypeRequire.noRequire());
+ newChildren.add(childOutput.first);
+ }
+ this.children = newChildren;
+ return Pair.of(this, LocalExchangeType.NOOP);
+ }
+
+ /**
+ * Unified framework method: propagate serial flag → recurse child → satisfy check → Layer 1 skip → insert LE.
+ * Replaces the old enforceChild/enforceChildExchange/forceEnforceChildExchange trio.
+ *
+ * <h3>Data flow</h3>
+ * <ul>
+ * <li><b>serial-ancestor flag</b> ({@link PlanTranslatorContext#hasSerialAncestorInPipeline})
+ * — flows root → leaf during traversal. Mirrors BE's
+ * {@code any_of(operators[idx..end], is_serial_operator)} check used by
+ * {@code _add_local_exchange} to skip LE insertion when an ancestor in the same
+ * pipeline is already serial. Reset at pipeline boundaries via
+ * {@link #shouldResetSerialFlagForChild}.</li>
+ * <li><b>shuffle-for-correctness flag</b>
+ * ({@link PlanTranslatorContext#hasShuffleForCorrectnessAncestor}) — also flows
+ * root → leaf. Mirrors BE's {@code _followed_by_shuffled_operator}: tells a
+ * child whether some downstream operator depends on hash distribution for
+ * correctness, so {@code SetOperationNode} can pre-shuffle union branches.</li>
+ * <li><b>return value</b> {@code Pair<PlanNode, LocalExchangeType>} — first is the
+ * (possibly LE-wrapped) child; second is the actual output distribution as
+ * observed by the parent. Caller's {@code require.satisfy(output)} decides
+ * whether more LE is needed.</li>
+ * <li><b>parent.require</b> describes the constraint on the child output —
+ * computed inside the parent's {@code enforceAndDeriveLocalExchange} per child.</li>
+ * </ul>
+ *
+ * <h3>Invariants</h3>
+ * <ul>
+ * <li>Where a serial → non-serial transition needs redistribution, framework step 3 inserts
+ * the LE (e.g. a serial source fanned out via PASSTHROUGH). This is not a hard invariant:
+ * a serial child feeding a parent that requires PASSTHROUGH / noRequire (TableFunction,
+ * NLJ, Agg) is already correct and needs no LE, so it is intentionally not validated by a
+ * post-pass.</li>
+ * <li>{@code LocalExchangeNode} itself is always non-serial — setting it serial
+ * would defeat its purpose of fanning a 1-task pipeline back to N tasks.</li>
+ * <li>For pipeline-breaking parents ({@code shouldResetSerialFlagForChild=true}),
+ * the child starts a fresh pipeline so {@code hasSerialAncestor} is reset; the
+ * node's own {@code isSerialNode()} still composes in for the child's view.</li>
+ * <li>{@code RequireHash} accepts any hash flavour; {@code RequireSpecific} demands
+ * an exact match (with the one PASSTHROUGH/ADAPTIVE_PASSTHROUGH compatibility).
+ * Pick the looser one whenever correctness allows — see
+ * {@link LocalExchangeNode.LocalExchangeTypeRequire}.</li>
+ * </ul>
+ *
+ * <h3>Layers</h3>
+ * Layer 1 (shouldSkipLE): mirrors BE's need_to_local_exchange — skip when this node or
+ * an ancestor in the same pipeline is serial (operators[idx..end] has serial → skip).
+ * Layer 2 (require/output): each Node declares require and output in enforceAndDeriveLocalExchange.
+ */
+ protected Pair<PlanNode, LocalExchangeType> enforceRequire(
+ PlanTranslatorContext translatorContext, PlanNode child, int childIndex,
+ LocalExchangeTypeRequire require) {
+ // 1. Propagate serial-ancestor flag to child.
+ // For pipeline-splitting operators (shouldReset=true, e.g. non-streaming AGG):
+ // Drop inherited serial flag from parent (parent is in a different pipeline),
+ // but keep this node's own serial status (child is in the same pipeline as this
+ // node's sink, e.g. Exchange is in AGG_Sink pipeline).
+ // For non-splitting operators (shouldReset=false, e.g. streaming AGG):
+ // Inherit parent's serial flag + this node's own.
+ boolean inheritedSerial = shouldResetSerialFlagForChild(childIndex)
+ ? false : translatorContext.hasSerialAncestorInPipeline(this);
+ // Use isSerialOperatorOnBe (= isSerialNode && fragment.useSerialSource) instead of the
+ // raw isSerialNode(). BE's OperatorBase reads the Thrift `is_serial_operator` flag —
+ // which is what FE writes via isSerialOperatorOnBe — so when the fragment is not in
+ // serial-source mode, BE treats this operator as non-serial regardless of isSerialNode.
+ // Using isSerialNode here would set the child's serial-ancestor flag wider than BE's
+ // view and over-skip required LocalExchanges downstream.
+ boolean childHasSerialAncestor = inheritedSerial
+ || isSerialOperatorOnBe(translatorContext.getConnectContext());
+ translatorContext.setHasSerialAncestorInPipeline(child, childHasSerialAncestor);
+
+ // 1b. Propagate shuffle-for-correctness-ancestor flag to child.
+ // Mirrors BE's _followed_by_shuffled_operator: a downstream operator needs hash
+ // distribution for correctness, and the chain to here goes through HASH or NOOP
+ // requirements (so the dependency is preserved).
+ // propagate = ((inheritedShuffled || self.requiresShuffleForCorrectness)
+ // && require is hash)
+ // || (inheritedShuffled && require is noop/passthrough)
+ boolean inheritedShuffled = translatorContext.hasShuffleForCorrectnessAncestor(this);
+ boolean selfOrInheritedShuffled = inheritedShuffled || requiresShuffleForCorrectness();
+ boolean requireIsHash = require.preferType().isHashShuffle();
+ boolean requireIsNoop = require.preferType() == LocalExchangeNode.LocalExchangeType.NOOP;
+ boolean childShuffledAncestor = (selfOrInheritedShuffled && requireIsHash)
+ || (inheritedShuffled && requireIsNoop);
+ translatorContext.setHasShuffleForCorrectnessAncestor(child, childShuffledAncestor);
+
+ // 2. Recurse child (Layer 2: child declares its own require/output)
+ Pair<PlanNode, LocalExchangeType> childOutput =
+ child.enforceAndDeriveLocalExchange(translatorContext, this, require);
+
+ // Steps 2.5 and 3 both react to a serial child but address different concerns:
+ // - Step 2.5 rewrites the OUTPUT-side view (what we tell satisfy/parent about
+ // the child's actual distribution). A serial pipeline runs with 1 task so
+ // its distribution claim is meaningless — flatten to NOOP so the satisfy
+ // check below doesn't get fooled by a stale "I output BUCKET_HASH" claim.
+ // - Step 3 rewrites the REQUIRE-side decision (what we want from the child).
+ // If we previously asked for nothing (noRequire) but the child turns out
+ // to be serial and we're not, upgrade to requirePassthrough so an LE is
+ // inserted to restore parallelism.
+
+ // 2.5. Serial child override (output side): if child is serial on BE, force its
+ // reported output to NOOP. Distribution is irrelevant when the child runs
+ // with 1 task; downstream parallelism is restored either by step 3 (LE
+ // insertion) or skipped entirely by step 4b (we're also serial).
+ if (childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ childOutput = Pair.of(childOutput.first, LocalExchangeType.NOOP);
+ }
+
+ // 3. Framework-level serial child check (require side, mirrors BE base class
+ // required_data_distribution): if child will be serial on BE but this node is
+ // not serial, the pipeline has a 1-task serial child feeding an N-task non-serial
+ // parent. Without LE, pipeline splits (AGG/JOIN) create paired pipelines with
+ // mismatched num_tasks → crash. Upgrade noRequire to requirePassthrough so an
+ // LE is inserted below to restore parallelism.
+ if (require instanceof LocalExchangeNode.NoRequire
+ && childOutput.first.isSerialOperatorOnBe(translatorContext.getConnectContext())
+ && !isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ require = LocalExchangeTypeRequire.requirePassthrough();
+ }
+
+ // 4. Satisfy check: child output meets requirement → done
+ if (require.satisfy(childOutput.second)) {
+ return childOutput;
+ }
+
+ // 4. Layer 1: skip LE when serial operator or ancestor in same pipeline
+ // Equivalent to BE's need_to_local_exchange: any_of(operators[idx..end], is_serial) → skip.
+ // Use isSerialOperatorOnBe (not isSerialNode) because BE's Pipeline::need_to_local_exchange
+ // checks op->is_serial_operator() which reads the Thrift flag set from isSerialOperatorOnBe;
+ // when fragment.useSerialSource is false, BE treats this node as non-serial.
+ if (translatorContext.hasSerialAncestorInPipeline(this)
+ || isSerialOperatorOnBe(translatorContext.getConnectContext())) {
+ return childOutput;
+ }
+
+ // 5. Resolve exchange type and create LE node
+ LocalExchangeType preferType = AddLocalExchange.resolveExchangeType(require);
+ List<Expr> distributeExprs = getLocalExchangeDistributeExprs(childIndex, selfOrInheritedShuffled);
+ PlanNode leNode = createLocalExchange(translatorContext, childOutput.first, preferType, distributeExprs);
+ return Pair.of(leNode, preferType);
+ }
+
+ /**
+ * Create a LocalExchangeNode wrapping child with the given exchange type.
+ * No child-type skip — matches BE's _add_local_exchange which inserts LE for any child
+ * type without checking instanceof.
+ *
+ * Handles heavy-ops bottleneck avoidance (mirrors BE pipeline_fragment_context.cpp):
+ * when upstream has 1 task (serial source) and exchange is heavy (hash/bucket/adaptive),
+ * insert a PASSTHROUGH fan-out first to avoid single-task bottleneck on the heavy
+ * exchange sink. Only applies to local-shuffle (pooling scan) fragments.
+ */
+ protected PlanNode createLocalExchange(PlanTranslatorContext translatorContext,
+ PlanNode child, LocalExchangeType exchangeType, List<Expr> distributeExprs) {
+ if (fragment != null && fragment.useSerialSource(translatorContext.getConnectContext())
+ && exchangeType.isHeavyOperation() && child.isSerialNode()) {
+ PlanNode ptNode = new LocalExchangeNode(translatorContext.nextPlanNodeId(),
+ child, LocalExchangeType.PASSTHROUGH, null);
+ return new LocalExchangeNode(translatorContext.nextPlanNodeId(), ptNode,
+ exchangeType, distributeExprs);
+ }
+ return new LocalExchangeNode(translatorContext.nextPlanNodeId(), child,
+ exchangeType, distributeExprs);
+ }
+
+ /**
+ * Whether the child at {@code childIndex} starts a new pipeline context, causing
+ * its serial-ancestor flag to be reset to {@code false} rather than inherited from this node.
+ * Override to return {@code true} for pipeline-splitting nodes (LocalExchangeNode) and nodes
+ * whose children run in an independent pipeline segment (SortNode above analytic, etc.).
Review Comment:
above -> before?
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "morrySnow (via GitHub)" <gi...@apache.org>.
morrySnow commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3412891906
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PlanTranslatorContext.java:
##########
@@ -109,10 +109,36 @@ public class PlanTranslatorContext {
private final Map<ScanNode, Set<SlotId>> statsUnknownColumnsMap = Maps.newHashMap();
+ // Per-node "is there a serial operator between me and the pipeline's sink" flag.
+ // Mirrors BE's any_of(operators[idx..end], is_serial_operator) check used by
+ // _add_local_exchange / need_to_local_exchange to skip LE insertion when an ancestor
+ // in the same pipeline is already serial (the whole pipeline runs with 1 task, so an
+ // extra LE would be a no-op). Written by AddLocalExchange entry + PlanNode.enforceRequire
+ // step 1 (root → leaf during traversal). Read by PlanNode.enforceRequire step 4 (Layer 1
+ // skip) and by child overrides that compute their require. Reset to false at fragment
+ // root and across pipeline boundaries (see shouldResetSerialFlagForChild).
+ private final Map<PlanNodeId, Boolean> serialAncestorInPipelineMap = Maps.newHashMap();
+
+ // Per-node "is there a downstream operator that depends on hash distribution for
+ // correctness, with HASH/NOOP path connecting it to me" flag. Mirrors BE's
+ // _followed_by_shuffled_operator propagation in pipeline_fragment_context.cpp.
+ // Written by PlanNode.enforceRequire step 1b (root → leaf). Read by SetOperationNode
+ // to decide whether to propagate hash requirement to its inputs (only when downstream
+ // needs shuffle for correctness, not just for performance like StreamingAgg pre-agg).
+ private final Map<PlanNodeId, Boolean> shuffledAncestorMap = Maps.newHashMap();
+
+ // Whether the current fragment uses LocalShuffleAssignedJob (pooling scan with
+ // ignoreDataDistribution → _parallel_instances=1 in BE). When true, serial operators
+ // indicate real pipeline bottlenecks needing PASSTHROUGH fan-out (heavy_ops).
private boolean isTopMaterializeNode = true;
private final Set<SlotId> virtualColumnIds = Sets.newHashSet();
+ // Used by AddLocalExchange: tracks whether any serial operator exists
Review Comment:
**Dead code**: Field `serialAncestorInCurrentPipeline` and its getter/setter (L420-424) are never called by any code. The actual serial-ancestor propagation uses `serialAncestorInPipelineMap` (the per-PlanNodeId map).
Consider removing this field and its accessors to avoid future maintainers confusing the two mechanisms.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/ScanNode.java:
##########
@@ -733,7 +737,7 @@ public ScanContext getScanContext() {
}
@Override
- public boolean isSerialOperator() {
+ public boolean isSerialNode() {
ConnectContext context = ConnectContext.get();
if (context == null) {
return numScanBackends() <= 0;
Review Comment:
**Potential regression**: `isSerialNode()` removed `fragment.useSerialSource(context)` condition, but `Coordinator.assignScanRanges` (legacy path) calls `isSerialNode()` directly to decide whether to assign all scan ranges to the first instance.
Old logic: `numScanBackends() <= 0 || fragment.useSerialSource(context)` (OR semantics).
New logic: `numScanBackends() <= 0 || getScanRangeNum() < ... || isForceToLocalShuffle()`.
The `fragment.useSerialSource()` check is gone.
When a fragment is in serial-source (pooling) mode with a multi-backend scan and sufficient scan ranges, `isSerialNode()` returns `false`, causing scan ranges to be scattered across instances. But the downstream ExchangeNode is serial (1 task), expecting data from only one instance — data from non-first instances is lost.
**Suggestion**: Either add `|| fragment != null && fragment.useSerialSource(context)` back to `isSerialNode()`, or override `isSerialOperatorOnBe()` in ScanNode with OR semantics and change Coordinator to call `isSerialOperatorOnBe()`. Note: `isSerialOperatorOnBe` uses AND (`isSerialNode() && fragment.useSerialSource()`) which cannot express the old OR behavior.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/BucketedAggregationNode.java:
##########
@@ -97,7 +98,7 @@ public String getNodeExplainString(String detailPrefix, TExplainLevel detailLeve
}
@Override
- public boolean isSerialOperator() {
+ public boolean isSerialOperatorOnBe(ConnectContext context) {
Review Comment:
**Wrong method override**: This overrides `isSerialOperatorOnBe(ConnectContext context)` to directly return `false`, while every other node overrides `isSerialNode()`.
`BucketedAggregationNode extends AggregationNode`, which overrides `isSerialNode()` as `aggInfo.getGroupingExprs().isEmpty() && needsFinalize`. If a BucketedAgg without GROUP BY keys is ever introduced (despite the comment saying it won't happen), `isSerialNode()` would return `true` but `isSerialOperatorOnBe()` would still return `false`, causing inconsistent serial semantics.
No bug currently (BucketedAgg always has GROUP BY keys), but the inconsistent override pattern is a future bug waiting to happen.
**Suggestion**: Override `isSerialNode()` to return `false` instead, matching the comment ("never a serial operator") and following the same pattern as all other node types.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/MaterializationNode.java:
##########
@@ -227,7 +231,17 @@ public void setTopMaterializeNode(boolean topMaterializeNode) {
}
@Override
- public boolean isSerialOperator() {
+ public boolean isSerialNode() {
return true;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ Pair<PlanNode, LocalExchangeType> enforceResult = enforceRequire(
+ translatorContext, children.get(0), 0, LocalExchangeTypeRequire.requirePassthrough());
+ children = new ArrayList<>();
Review Comment:
**Style inconsistency**: Uses `new ArrayList<>()` while other nodes (AggregationNode, HashJoinNode, AssertNumRowsNode, etc.) use `Lists.newArrayList()`. Consider using `Lists.newArrayList()` for consistency.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AggregationNode.java:
##########
@@ -258,4 +268,155 @@ public boolean isQueryCacheCandidate() {
public void setQueryCacheCandidate(boolean queryCacheCandidate) {
this.queryCacheCandidate = queryCacheCandidate;
}
+
+ @Override
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+
+ ConnectContext connectContext = translatorContext.getConnectContext();
+ SessionVariable sessionVariable = connectContext.getSessionVariable();
+ // PR #62438: when false, non-finalize agg falls back to BE base class.
+ boolean enableLeBeforeAgg = sessionVariable.enableLocalExchangeBeforeAgg;
+ boolean hasKeys = !aggInfo.getGroupingExprs().isEmpty();
+
+ // Each branch mirrors the corresponding BE operator's required_data_distribution()
+ // check order 1:1. The helper baseClassRequire() expands BE's base class behavior.
+ LocalExchangeTypeRequire requireChild;
+ if (canUseDistinctStreamingAgg(sessionVariable)) {
+ // DistinctStreamingAggOperatorX. Two flavors share this operator class:
+ // - streaming preagg (useStreamingPreagg=true): performance-only,
+ // flag controls
+ // - non-streaming dedup (useStreamingPreagg=false): correctness-required,
+ // always HASH regardless of flag
+ // Diverges from BE: BE's `!_needs_finalize && !enable_local_exchange_before_agg`
+ // early return catches non-streaming dedup too, causing the same family of
+ // wrong-result bug as AggSink (DORIS-25413).
+ if (needsFinalize && !hasKeys) {
+ requireChild = LocalExchangeTypeRequire.noRequire();
+ } else if (!needsFinalize && useStreamingPreagg && !enableLeBeforeAgg) {
+ requireChild = baseClassRequire(connectContext);
+ } else if (needsFinalize || (hasKeys && !useStreamingPreagg)) {
+ requireChild = AddLocalExchange.isColocated(this)
+ ? LocalExchangeTypeRequire.requireHash()
+ : parentRequire.autoRequireHash();
+ } else if (sessionVariable.enableDistinctStreamingAggForcePassthrough) {
+ requireChild = LocalExchangeTypeRequire.requirePassthrough();
+ } else {
+ requireChild = baseClassRequire(connectContext);
+ }
+ } else if (useStreamingPreagg) {
+ // StreamingAggOperatorX
+ if (children.get(0) instanceof HashJoinNode
+ && sessionVariable.enableStreamingAggHashJoinForcePassthrough) {
+ requireChild = LocalExchangeTypeRequire.requirePassthrough();
+ } else if (!needsFinalize && !enableLeBeforeAgg) {
+ requireChild = baseClassRequire(connectContext);
+ } else if (!hasKeys) {
+ requireChild = needsFinalize
+ ? LocalExchangeTypeRequire.noRequire()
+ : baseClassRequire(connectContext);
+ } else {
+ requireChild = LocalExchangeTypeRequire.requireHash();
+ }
+ } else {
+ // AggSinkOperatorX — covers finalize phase AND non-finalize phases (LOCAL
+ // preagg / FIRST_MERGE dedup). Streaming preagg goes through the StreamingAgg
+ // branch above, not here.
+ //
+ // Phase semantics for !needsFinalize:
+ // - FIRST / SECOND (LOCAL phase, !isMerge): performance-only, flag controls
+ // - FIRST_MERGE (correctness-required): always HASH regardless of flag
+ //
+ // Diverges from BE here: BE's `!_needs_finalize && !enable_local_exchange_before_agg`
+ // early return also catches FIRST_MERGE, dropping the HASH requirement and
+ // causing wrong-result (e.g. PASSTHROUGH over serial child breaks the
+ // group-by-key invariant — DORIS-25413).
+ if (!hasKeys) {
+ requireChild = needsFinalize
+ ? LocalExchangeTypeRequire.noRequire()
+ : baseClassRequire(connectContext);
+ } else if (!needsFinalize && !aggInfo.isMerge() && !enableLeBeforeAgg) {
+ // LOCAL phase (FIRST preagg / SECOND distinct local) + user opted out
+ // of pre-agg LE → base class decides: serial child → PASSTHROUGH
+ // (parallelism), non-serial child → NOOP (no LE).
+ requireChild = baseClassRequire(connectContext);
+ } else if (!needsFinalize || AddLocalExchange.isColocated(this)) {
+ // FIRST_MERGE (correctness) or finalize+colocate → HASH.
+ requireChild = parentRequire.autoRequireHash();
+ } else if (hasPartitionExprs(parentRequire)) {
+ // FE-only heuristic: finalize non-colocate with parent hash requirement
+ // → inherit parent's specific hash type.
+ requireChild = parentRequire.autoRequireHash();
+ } else {
+ // FE-only heuristic: finalize non-colocate without parent hash → skip
+ // LE (child Exchange already provides hash distribution).
+ requireChild = LocalExchangeTypeRequire.noRequire();
+ }
+ }
+
+ Pair<PlanNode, LocalExchangeType> enforceResult
+ = enforceRequire(translatorContext, children.get(0), 0, requireChild);
+ children = Lists.newArrayList(enforceResult.first);
+ return Pair.of(this, enforceResult.second);
+ }
+
+ /** BE base class required_data_distribution: serial child → PASSTHROUGH, else → NOOP. */
+ private LocalExchangeTypeRequire baseClassRequire(ConnectContext connectContext) {
+ return children.get(0).isSerialOperatorOnBe(connectContext)
+ ? LocalExchangeTypeRequire.requirePassthrough()
+ : LocalExchangeTypeRequire.noRequire();
+ }
+
+ @Override
+ protected List<Expr> getSemanticPartitionExprs() {
+ return aggInfo.getGroupingExprs();
+ }
+
+ @Override
+ protected List<Expr> getLocalExchangeDistributeExprs(int childIndex, boolean followedByShuffled) {
+ // Mirror BE AggSinkOperatorX::update_operator / StreamingAggOperatorX::update_operator:
+ // _partition_exprs = (distribute_expr_lists set && (followed_by_shuffled || has_distinct))
+ // ? distribute_expr_lists[0] : grouping_exprs
+ // The HASH LocalExchange must partition by _partition_exprs so a streaming partial preagg
+ // locally collapses same-key rows. Using child distribution (default) for a non-shuffled
+ // chain scatters same-group rows across N instances, leaving partial_preagg essentially a
+ // no-op and breaking row-arrival order at downstream merge-finalize (e.g. group_concat).
+ List<Expr> childDist = getChildDistributeExprList(childIndex);
+ boolean hasDistinct = aggInfo.getAggregateExprs().stream()
+ .map(FunctionCallExpr::getFnName)
+ .filter(name -> name != null)
+ .map(name -> name.getFunction())
+ .filter(name -> name != null)
+ .anyMatch(name -> name.startsWith("multi_distinct_"));
Review Comment:
**Fragile string-based detection**: `hasDistinct` uses `name.startsWith("multi_distinct_")` to detect multi-distinct aggregate functions. This prefix-matching on function names is fragile — if Doris renames multi-distinct functions or adds new functions with the same prefix but different semantics, this check silently breaks.
The BE equivalent uses structural checks (`tnode.agg_node.__isset.aggregate_functions`), which is more robust.
**Suggestion**: Add an API on `AggInfo` (e.g., `hasDistinctAggregates()` or `isMultiDistinct()`) that exposes the presence of multi-distinct aggregates structurally, rather than having callers infer it from function name strings.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/AddLocalExchange.java:
##########
@@ -0,0 +1,152 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.planner;
+
+import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.glue.translator.PlanTranslatorContext;
+import org.apache.doris.nereids.trees.plans.distribute.DistributedPlan;
+import org.apache.doris.nereids.trees.plans.distribute.FragmentIdMapping;
+import org.apache.doris.nereids.trees.plans.distribute.PipelineDistributedPlan;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeType;
+import org.apache.doris.planner.LocalExchangeNode.LocalExchangeTypeRequire;
+import org.apache.doris.planner.LocalExchangeNode.RequireHash;
+
+/**
+ * FE-side local exchange planner — inserts {@link LocalExchangeNode} into each fragment's
+ * plan tree so that within-fragment data redistribution is decided at planning time
+ * instead of at BE pipeline-build time.
+ *
+ * <h3>When this runs</h3>
+ * Invoked from {@code NereidsPlanner.addLocalExchangeAfterDistribute()} right after
+ * {@code DistributePlanner} has assigned instances to fragments and before the plan is
+ * serialized to BE. Gated by session variable {@code enable_local_shuffle_planner}
+ * (default true) and {@code enable_local_shuffle}; when either is off this pass is
+ * skipped entirely and BE falls back to its own {@code _plan_local_exchange}. The two
+ * paths are mutually exclusive: BE consults {@code runtime_state.h::plan_local_shuffle()}
+ * to know whether it should plan LE itself.
+ *
+ * <h3>What it changes</h3>
+ * <ul>
+ * <li>For each fragment with {@code maxPerBeInstances > 1}, walks the plan tree
+ * bottom-up via {@link PlanNode#enforceAndDeriveLocalExchange} and inserts
+ * LocalExchangeNodes where children's output distribution doesn't satisfy the
+ * parent's requirement.</li>
+ * <li>May wrap the fragment root with an extra PASSTHROUGH LE so the data sink
+ * (DataStreamSink / OlapTableSink) runs with the full instance count even when
+ * the root operator is serial — see {@link #addLocalExchangeForFragment}.</li>
+ * <li>Does NOT modify the fragment sink itself, fragment boundaries, or instance
+ * assignment.</li>
+ * </ul>
+ *
+ * <h3>Per-BE instance semantics</h3>
+ * Skips fragments where every BE has at most 1 instance. Using a global instance count
+ * would insert LE for "2 BEs × 1 instance" cases, which BE's own
+ * {@code _plan_local_exchange} would not — leading to pipeline task-count mismatch and
+ * deadlock. See {@link #addLocalExchange}.
+ *
+ * <h3>Reading order</h3>
+ * Start with {@link PlanNode#enforceRequire} (the recursion engine), then individual
+ * {@code enforceAndDeriveLocalExchange} overrides on PlanNode subclasses.
+ */
+public class AddLocalExchange {
+ /** addLocalExchange with distributed plans, skipping single-instance fragments.
+ * BE's _plan_local_exchange checks _num_instances which is the per-BE instance count.
+ * With _num_instances<=1 all pipelines on that BE have 1 task so local exchange is a no-op.
+ * We must use the same per-BE semantics: skip when every BE has at most 1 instance.
+ * Using global instanceCount would insert LE for fragments where 2 BEs each have 1 instance
+ * (global=2, per-BE=1), causing pipeline task mismatch and deadlock. */
+ public void addLocalExchange(FragmentIdMapping<DistributedPlan> distributedPlans,
+ PlanTranslatorContext context) {
+ for (DistributedPlan plan : distributedPlans.values()) {
+ PipelineDistributedPlan pipePlan = (PipelineDistributedPlan) plan;
+ long maxPerBeInstances = pipePlan.getInstanceJobs().stream()
+ .collect(java.util.stream.Collectors.groupingBy(
+ j -> j.getAssignedWorker().id(), java.util.stream.Collectors.counting()))
+ .values().stream().mapToLong(Long::longValue).max().orElse(0);
+ if (maxPerBeInstances <= 1) {
+ continue;
+ }
+ PlanFragment fragment = pipePlan.getFragmentJob().getFragment();
+ addLocalExchangeForFragment(fragment, context);
+ }
+ }
+
+ private void addLocalExchangeForFragment(PlanFragment fragment, PlanTranslatorContext context) {
+ DataSink sink = fragment.getSink();
+ LocalExchangeTypeRequire require = sink == null
+ ? LocalExchangeTypeRequire.noRequire() : sink.getLocalExchangeTypeRequire();
+ PlanNode root = fragment.getPlanRoot();
+ context.setHasSerialAncestorInPipeline(root, false);
+ Pair<PlanNode, LocalExchangeType> output = root
+ .enforceAndDeriveLocalExchange(context, null, require);
+ PlanNode newRoot = output.first;
+ // The fragment data sink (DataStreamSink, OlapTableSink) runs in the same pipeline
+ // as the root. If the root will be serial on BE, the sink pipeline has 1 task —
+ // only instance 0 sends data, others hang or miss writes.
+ // Insert PASSTHROUGH fan-out so sink runs with _num_instances tasks.
+ // This matches BE-native's default required_data_distribution():
+ // _child->is_serial_operator() ? PASSTHROUGH : NOOP
+ if (newRoot.isSerialOperatorOnBe(context.getConnectContext())) {
+ newRoot = new LocalExchangeNode(context.nextPlanNodeId(), newRoot,
+ LocalExchangeType.PASSTHROUGH, null);
+ }
+ if (newRoot != root) {
+ fragment.setPlanRoot(newRoot);
+ }
+ }
+
+ public static boolean isColocated(PlanNode plan) {
+ if (plan instanceof AggregationNode) {
+ return ((AggregationNode) plan).isColocate() && isColocated(plan.getChild(0));
+ } else if (plan instanceof OlapScanNode) {
+ return true;
+ } else if (plan instanceof SelectNode) {
+ return isColocated(plan.getChild(0));
+ } else if (plan instanceof HashJoinNode) {
+ return ((HashJoinNode) plan).isColocate()
+ && (isColocated(plan.getChild(0)) || isColocated(plan.getChild(1)));
+ } else if (plan instanceof SetOperationNode) {
+ if (!((SetOperationNode) plan).isColocate()) {
+ return false;
+ }
+ for (PlanNode child : plan.getChildren()) {
+ if (isColocated(child)) {
+ return true;
+ }
+ }
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ public static LocalExchangeType resolveExchangeType(LocalExchangeTypeRequire require) {
Review Comment:
**`preferType()` / `resolveExchangeType()` inconsistency**: `RequireHash.preferType()` (at LocalExchangeNode L240-241) returns `GLOBAL_EXECUTION_HASH_SHUFFLE`, but `resolveExchangeType()` always returns `LOCAL_EXECUTION_HASH_SHUFFLE` for `RequireHash`. This means `preferType()` is not authoritative — the actual exchange type inserted is controlled by `resolveExchangeType()`.
Currently `hasPartitionExprs()` uses `preferType().isHashShuffle()` which works correctly (both GLOBAL and LOCAL return true), but the discrepancy could confuse future maintainers.
**Suggestion**: Change `RequireHash.preferType()` to return `LOCAL_EXECUTION_HASH_SHUFFLE` to align with `resolveExchangeType()`.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/PlanNode.java:
##########
@@ -939,4 +1026,261 @@ private String mergeIcebergAccessPathsWithId(
}
return StringUtils.join(mergeDisplayAccessPaths, ", ");
}
+
+ public Pair<PlanNode, LocalExchangeType> enforceAndDeriveLocalExchange(
+ PlanTranslatorContext translatorContext, PlanNode parent, LocalExchangeTypeRequire parentRequire) {
+ ArrayList<PlanNode> newChildren = Lists.newArrayList();
+ for (int i = 0; i < children.size(); i++) {
+ Pair<PlanNode, LocalExchangeType> childOutput
+ = enforceRequire(translatorContext, children.get(i), i, LocalExchangeTypeRequire.noRequire());
+ newChildren.add(childOutput.first);
+ }
+ this.children = newChildren;
+ return Pair.of(this, LocalExchangeType.NOOP);
+ }
+
+ /**
+ * Unified framework method: propagate serial flag → recurse child → satisfy check → Layer 1 skip → insert LE.
+ * Replaces the old enforceChild/enforceChildExchange/forceEnforceChildExchange trio.
+ *
+ * <h3>Data flow</h3>
+ * <ul>
+ * <li><b>serial-ancestor flag</b> ({@link PlanTranslatorContext#hasSerialAncestorInPipeline})
+ * — flows root → leaf during traversal. Mirrors BE's
+ * {@code any_of(operators[idx..end], is_serial_operator)} check used by
+ * {@code _add_local_exchange} to skip LE insertion when an ancestor in the same
+ * pipeline is already serial. Reset at pipeline boundaries via
+ * {@link #shouldResetSerialFlagForChild}.</li>
+ * <li><b>shuffle-for-correctness flag</b>
+ * ({@link PlanTranslatorContext#hasShuffleForCorrectnessAncestor}) — also flows
+ * root → leaf. Mirrors BE's {@code _followed_by_shuffled_operator}: tells a
+ * child whether some downstream operator depends on hash distribution for
+ * correctness, so {@code SetOperationNode} can pre-shuffle union branches.</li>
+ * <li><b>return value</b> {@code Pair<PlanNode, LocalExchangeType>} — first is the
+ * (possibly LE-wrapped) child; second is the actual output distribution as
+ * observed by the parent. Caller's {@code require.satisfy(output)} decides
+ * whether more LE is needed.</li>
+ * <li><b>parent.require</b> describes the constraint on the child output —
+ * computed inside the parent's {@code enforceAndDeriveLocalExchange} per child.</li>
+ * </ul>
+ *
+ * <h3>Invariants</h3>
+ * <ul>
+ * <li>Every serial → non-serial transition has an LE somewhere between them
+ * (enforced by framework step 3 below, validated by
+ * {@link AddLocalExchange#validateNoSerialWithoutLocalExchange}).</li>
Review Comment:
**Missing method referenced in javadoc**: The javadoc references `AddLocalExchange#validateNoSerialWithoutLocalExchange` but this method doesn't exist in `AddLocalExchange`. The framework cannot enforce the invariant "every serial to non-serial transition has an LE somewhere between them" without this validation method.
Suggestion: either implement the method (a post-pass validation walk over the plan tree after `addLocalExchange` completes), or remove the reference from the javadoc.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4708881365
<details>
<summary>TPC-DS: <b>Total hot run time: 168379 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit fe9a3316be056faed18351cf64096e36790c6c89, data reload: false
query5 4318 629 495 495
query6 450 199 177 177
query7 4801 586 305 305
query8 371 218 197 197
query9 8764 4098 4072 4072
query10 436 303 260 260
query11 5924 2332 2143 2143
query12 159 99 96 96
query13 1245 593 384 384
query14 6421 5861 5078 5078
query14_1 4439 4492 4305 4305
query15 207 196 176 176
query16 995 456 402 402
query17 934 714 548 548
query18 2429 486 329 329
query19 196 180 134 134
query20 109 106 104 104
query21 218 136 117 117
query22 13678 13585 13396 13396
query23 17365 16540 16236 16236
query23_1 16363 16255 16259 16255
query24 7500 1825 1299 1299
query24_1 1295 1328 1339 1328
query25 532 434 368 368
query26 1308 327 169 169
query27 2684 567 330 330
query28 4497 2031 2016 2016
query29 1064 635 497 497
query30 315 268 207 207
query31 1112 1079 977 977
query32 108 64 61 61
query33 532 330 271 271
query34 1220 1196 674 674
query35 774 796 696 696
query36 1379 1415 1257 1257
query37 161 110 97 97
query38 3233 3179 3070 3070
query39 953 912 895 895
query39_1 901 903 890 890
query40 224 127 107 107
query41 71 67 67 67
query42 98 96 94 94
query43 318 334 283 283
query44
query45 195 187 184 184
query46 1101 1244 743 743
query47 2395 2374 2210 2210
query48 402 421 310 310
query49 663 492 357 357
query50 1031 364 258 258
query51 4445 4403 4242 4242
query52 92 93 81 81
query53 246 287 189 189
query54 292 234 234 234
query55 86 79 74 74
query56 236 247 230 230
query57 1416 1406 1340 1340
query58 255 218 227 218
query59 1618 1659 1463 1463
query60 304 260 257 257
query61 201 187 173 173
query62 718 661 584 584
query63 232 193 193 193
query64 2582 776 583 583
query65
query66 1785 460 336 336
query67 29729 29127 29627 29127
query68
query69 432 302 266 266
query70 988 958 914 914
query71 306 220 207 207
query72 2904 2604 2337 2337
query73 862 758 453 453
query74 5109 4902 4779 4779
query75 2684 2582 2225 2225
query76 2343 1241 810 810
query77 394 376 269 269
query78 12447 12508 11947 11947
query79 1549 1058 792 792
query80 1265 481 391 391
query81 500 281 243 243
query82 575 163 122 122
query83 346 272 251 251
query84
query85 896 497 411 411
query86 405 294 271 271
query87 3421 3361 3228 3228
query88 3690 2718 2695 2695
query89 434 390 334 334
query90 1849 179 180 179
query91 171 162 130 130
query92 66 63 57 57
query93 1523 1495 892 892
query94 706 366 323 323
query95 673 463 349 349
query96 1117 876 353 353
query97 2679 2756 2558 2558
query98 211 212 201 201
query99 1155 1179 1027 1027
Total cold run time: 252218 ms
Total hot run time: 168379 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on code in PR #63366:
URL: https://github.com/apache/doris/pull/63366#discussion_r3413491799
##########
be/src/exec/pipeline/dependency.h:
##########
@@ -832,50 +832,44 @@ struct SetSharedState : public BasicSharedState {
Status hash_table_init();
};
-enum class ExchangeType : uint8_t {
- NOOP = 0,
- // Shuffle data by Crc32CHashPartitioner
- HASH_SHUFFLE = 1,
- // Round-robin passthrough data blocks.
- PASSTHROUGH = 2,
- // Shuffle data by Crc32HashPartitioner<ShuffleChannelIds> (e.g. same as storage engine).
- BUCKET_HASH_SHUFFLE = 3,
- // Passthrough data blocks to all channels.
- BROADCAST = 4,
- // Passthrough data to channels evenly in an adaptive way.
- ADAPTIVE_PASSTHROUGH = 5,
- // Send all data to the first channel.
- PASS_TO_ONE = 6,
-};
+inline bool is_shuffled_exchange(TLocalPartitionType::type idx) {
+ return idx == TLocalPartitionType::GLOBAL_EXECUTION_HASH_SHUFFLE ||
+ idx == TLocalPartitionType::LOCAL_EXECUTION_HASH_SHUFFLE ||
+ idx == TLocalPartitionType::BUCKET_HASH_SHUFFLE;
+}
Review Comment:
`is_shuffled_exchange` is defined once (inline here in `dependency.h`) and reused at `pipeline.h:72/79`, `local_exchange_sink_operator.cpp:120`, and `local_exchange_source_operator.cpp:34` — there is no duplicate copy in `operator.cpp`. If you spotted a separate function in `operator.cpp` doing the same hash-type check, point me at it and I'll unify them.
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "924060929 (via GitHub)" <gi...@apache.org>.
924060929 commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4765391971
/review
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4766384189
# FE UT Coverage Report
Increment line coverage ` 74.69% (419/561)` :tada:
[Increment coverage report](http://coverage.selectdb-in.cc/coverage/63366_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b/fe_increment_report/index.html)
[Complete coverage report](http://coverage.selectdb-in.cc/coverage/63366_1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b/fe_report/index.html)
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4767719239
<details>
<summary>TPC-H: <b>Total hot run time: 29079 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 7fdb989a3a7e4ad0c700539a335598f4978f6938, data reload: false
------ Round 1 ----------------------------------
============================================
q1 17593 4008 3986 3986
q2 2010 324 188 188
q3 10864 1453 811 811
q4 4777 475 341 341
q5 8475 863 577 577
q6 340 169 137 137
q7 878 845 624 624
q8 10673 1636 1581 1581
q9 5779 4482 4511 4482
q10 6890 1798 1516 1516
q11 455 275 246 246
q12 639 423 300 300
q13 18123 3327 2764 2764
q14 266 261 243 243
q15 q16 785 781 712 712
q17 973 971 936 936
q18 7290 5881 5572 5572
q19 1191 1254 1012 1012
q20 499 397 267 267
q21 5650 2639 2481 2481
q22 435 368 303 303
Total cold run time: 104585 ms
Total hot run time: 29079 ms
----- Round 2, with runtime_filter_mode=off -----
============================================
q1 4326 4266 4229 4229
q2 309 341 216 216
q3 4575 4968 4446 4446
q4 2061 2157 1358 1358
q5 4435 4291 4280 4280
q6 233 180 133 133
q7 2047 1905 1571 1571
q8 2482 2134 2092 2092
q9 8057 7995 8080 7995
q10 4814 4756 4270 4270
q11 574 433 509 433
q12 730 766 538 538
q13 3312 3586 2964 2964
q14 291 326 275 275
q15 q16 716 722 663 663
q17 1363 1331 1321 1321
q18 8074 7348 7112 7112
q19 1092 1063 1077 1063
q20 2233 2287 1956 1956
q21 5223 4513 4422 4422
q22 517 444 401 401
Total cold run time: 57464 ms
Total hot run time: 51738 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4767723647
<details>
<summary>TPC-H: <b>Total hot run time: 29190 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 1f47a2e9dcf76c032362ef2feeb9d24a55a18c2b, data reload: false
------ Round 1 ----------------------------------
============================================
q1 17690 4024 4100 4024
q2 2007 310 186 186
q3 10908 1437 814 814
q4 4761 475 341 341
q5 8448 854 566 566
q6 363 173 141 141
q7 852 850 640 640
q8 10668 1645 1579 1579
q9 5683 4570 4501 4501
q10 6853 1820 1534 1534
q11 451 274 247 247
q12 652 425 289 289
q13 18163 3947 2789 2789
q14 267 263 232 232
q15 q16 791 788 709 709
q17 1041 999 948 948
q18 6796 5789 5638 5638
q19 1167 1332 1051 1051
q20 485 415 259 259
q21 5505 2650 2407 2407
q22 428 366 295 295
Total cold run time: 103979 ms
Total hot run time: 29190 ms
----- Round 2, with runtime_filter_mode=off -----
============================================
q1 4422 4342 4433 4342
q2 318 367 241 241
q3 4674 5020 4414 4414
q4 2082 2169 1384 1384
q5 4492 4353 4350 4350
q6 239 217 238 217
q7 2147 1836 1601 1601
q8 2548 2196 2352 2196
q9 8197 8025 8183 8025
q10 4836 4789 4391 4391
q11 589 415 399 399
q12 753 758 534 534
q13 3331 3747 3022 3022
q14 283 303 272 272
q15 q16 743 732 631 631
q17 1399 1399 1354 1354
q18 7904 7300 6826 6826
q19 1116 1080 1107 1080
q20 2229 2227 1978 1978
q21 5359 4651 4551 4551
q22 526 458 407 407
Total cold run time: 58187 ms
Total hot run time: 52215 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4551487122
<details>
<summary>TPC-DS: <b>Total hot run time: 171534 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit ccfac05f6b543ebe78f973987084a8de173759c9, data reload: false
query5 4311 657 522 522
query6 324 218 195 195
query7 4288 580 322 322
query8 336 233 221 221
query9 8798 4009 3999 3999
query10 466 346 323 323
query11 5782 2428 2370 2370
query12 181 133 123 123
query13 1277 626 447 447
query14 6157 5506 5179 5179
query14_1 4479 4494 4454 4454
query15 217 208 188 188
query16 1001 485 456 456
query17 1167 758 631 631
query18 2596 480 358 358
query19 211 198 157 157
query20 136 132 130 130
query21 218 137 114 114
query22 13726 13683 13370 13370
query23 17436 16487 16233 16233
query23_1 16332 16261 16202 16202
query24 7477 1774 1305 1305
query24_1 1288 1335 1308 1308
query25 551 466 404 404
query26 1306 315 164 164
query27 2711 592 338 338
query28 4408 1981 1973 1973
query29 963 615 485 485
query30 299 232 195 195
query31 1137 1087 954 954
query32 89 76 73 73
query33 582 345 285 285
query34 1165 1137 679 679
query35 780 788 712 712
query36 1382 1380 1215 1215
query37 156 104 93 93
query38 3217 3180 3055 3055
query39 957 918 897 897
query39_1 884 893 883 883
query40 226 147 124 124
query41 64 61 61 61
query42 109 111 111 111
query43 334 337 293 293
query44
query45 214 202 196 196
query46 1115 1193 748 748
query47 2325 2336 2239 2239
query48 415 410 297 297
query49 635 532 402 402
query50 1059 366 256 256
query51 4474 4416 4328 4328
query52 107 107 98 98
query53 254 284 201 201
query54 327 287 270 270
query55 95 93 87 87
query56 313 326 326 326
query57 1453 1425 1319 1319
query58 315 290 282 282
query59 1600 1692 1408 1408
query60 320 322 298 298
query61 159 157 160 157
query62 713 646 590 590
query63 252 194 196 194
query64 2406 782 641 641
query65
query66 1704 479 353 353
query67 29840 29683 29073 29073
query68
query69 448 344 310 310
query70 1049 956 930 930
query71 314 272 271 271
query72 3048 2490 2407 2407
query73 843 734 428 428
query74 5137 5006 4745 4745
query75 2690 2626 2259 2259
query76 2281 1172 771 771
query77 414 424 345 345
query78 12488 12287 11832 11832
query79 1492 1075 796 796
query80 676 574 494 494
query81 451 285 247 247
query82 1376 164 131 131
query83 363 291 265 265
query84 274 148 124 124
query85 998 645 470 470
query86 420 349 346 346
query87 3434 3393 3245 3245
query88 3722 2749 2794 2749
query89 447 392 345 345
query90 1965 193 184 184
query91 189 172 140 140
query92 77 81 76 76
query93 1459 1555 922 922
query94 574 343 303 303
query95 693 383 459 383
query96 1109 823 362 362
query97 2711 2723 2632 2632
query98 237 238 228 228
query99 1145 1177 1035 1035
Total cold run time: 255136 ms
Total hot run time: 171534 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org
Re: [PR] [refactor](local shuffle) Move local exchange planning from BE to FE [doris]
Posted by "hello-stephen (via GitHub)" <gi...@apache.org>.
hello-stephen commented on PR #63366:
URL: https://github.com/apache/doris/pull/63366#issuecomment-4551434262
<details>
<summary>TPC-H: <b>Total hot run time: 31459 ms</b></summary>
```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit ccfac05f6b543ebe78f973987084a8de173759c9, data reload: false
------ Round 1 ----------------------------------
orders Doris NULL NULL 0 0 0 NULL 0 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 17809 4007 3989 3989
q2 q3 10694 1384 835 835
q4 4690 474 351 351
q5 7571 2290 2157 2157
q6 247 178 141 141
q7 941 813 645 645
q8 9380 1653 1609 1609
q9 5552 5056 5080 5056
q10 6406 2229 1883 1883
q11 435 268 247 247
q12 650 437 298 298
q13 18211 3389 2780 2780
q14 266 261 235 235
q15 q16 821 772 706 706
q17 993 935 845 845
q18 6908 5717 5522 5522
q19 1183 1312 1105 1105
q20 511 423 269 269
q21 5606 2649 2468 2468
q22 441 366 318 318
Total cold run time: 99315 ms
Total hot run time: 31459 ms
----- Round 2, with runtime_filter_mode=off -----
orders Doris NULL NULL 150000000 42 6422171781 NULL 22778155 NULL NULL 2023-12-26 18:27:23 2023-12-26 18:42:55 NULL utf-8 NULL NULL
============================================
q1 4370 4296 4280 4280
q2 q3 4525 4981 4343 4343
q4 2067 2201 1391 1391
q5 4430 4266 4336 4266
q6 231 172 128 128
q7 2449 1929 1707 1707
q8 2542 2238 2208 2208
q9 8346 8164 8306 8164
q10 4847 4792 4315 4315
q11 568 429 384 384
q12 740 776 570 570
q13 3315 3673 2928 2928
q14 303 313 286 286
q15 q16 734 776 644 644
q17 1388 1356 1488 1356
q18 7982 7459 7354 7354
q19 1242 1190 1093 1093
q20 2215 2235 1962 1962
q21 5276 4582 4464 4464
q22 506 471 407 407
Total cold run time: 58076 ms
Total hot run time: 52250 ms
```
</details>
--
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: commits-unsubscribe@doris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org