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


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java:
##########
@@ -2496,14 +2497,56 @@ public PlanFragment visitPhysicalSetOperation(
             setOperationNode.setColocate(true);
         }
 
-        // TODO: open comment when support `enable_local_shuffle_planner`
-        // for (Plan child : setOperation.children()) {
-        //     PhysicalPlan childPhysicalPlan = (PhysicalPlan) child;
-        //     if 
(JoinUtils.isStorageBucketed(childPhysicalPlan.getPhysicalProperties())) {
-        //         
setOperationNode.setDistributionMode(DistributionMode.BUCKET_SHUFFLE);
-        //         break;
-        //     }
-        // }
+        // Mark BUCKET_SHUFFLE only when every child is bucket-distributed on 
the same storage
+        // layout, i.e. ChildrenPropertiesRegulator actually aligned all 
children to one basic
+        // child's buckets: it enforces the non-basic children to the basic 
child's storage
+        // layout, and only does so when set-op bucket shuffle is allowed, so 
the marker follows
+        // the regulator's decision without re-checking the session variables 
here (that would
+        // risk drifting out of sync with the request-derivation gate).
+        //
+        // A single storage-bucketed child is not proof on its own that this 
set operation chose
+        // bucket shuffle: under a non-bucket (ANY) request a child can stay 
storage-bucketed
+        // independently while the other children are left unaligned, and 
marking from that child
+        // alone would drive PlanFragment.hasBucketShuffleNode(), bucket 
assignment and non-
+        // INTERSECT receiver fill-up on a set operation whose rows are not 
actually bucket-co-
+        // located. Requiring one shared storage layout keeps the marker in 
step with
+        // ChildOutputPropertyDeriver, which likewise only claims a bucket 
distribution when all
+        // children agree on the layout.
+        //
+        // Unlike hash join, BUCKET_SHUFFLE is not exclusive with isColocate 
above: for a set
+        // operation isColocate describes the bucket-aligned scheduling of the 
fragment (the
+        // basic child scans buckets directly), while BUCKET_SHUFFLE describes 
how the other
+        // children arrive (bucket-shuffle exchanges). Both routes converge to 
the same
+        // bucket-hash local exchange requirement in 
SetOperationNode.enforceAndDeriveLocalExchange.
+        boolean anyStorageBucketed = false;
+        boolean allChildrenBucketAligned = !setOperation.children().isEmpty();
+        DistributionSpecHash basicLayout = null;
+        for (Plan child : setOperation.children()) {
+            DistributionSpec childSpec = ((PhysicalPlan) 
child).getPhysicalProperties().getDistributionSpec();
+            if (!(childSpec instanceof DistributionSpecHash)) {
+                allChildrenBucketAligned = false;
+                break;
+            }
+            DistributionSpecHash childHash = (DistributionSpecHash) childSpec;
+            ShuffleType shuffleType = childHash.getShuffleType();
+            if ((shuffleType != ShuffleType.NATURAL && shuffleType != 
ShuffleType.STORAGE_BUCKETED)

Review Comment:
   This marker still is not using the same proof as 
`ChildOutputPropertyDeriver`. The deriver maps every child's 
`orderedShuffledColumns` through `getRegularChildrenOutputs()` and requires all 
children to land on the same set-output positions before it claims a bucketed 
set-operation output; if that proof fails it returns `createAnyFromHash(...)` 
so parents insert a real exchange. This translator loop only checks that the 
children are `NATURAL`/`STORAGE_BUCKETED` on the same table/index/partitions. A 
same-layout shape can still have the bucket key feeding output position `k` in 
one nested child and output position `v` in another, so the deriver refuses the 
bucket property while the legacy node is still marked `BUCKET_SHUFFLE`. Since 
that marker drives `hasBucketShuffleNode()`, bucket assignment, and 
non-`INTERSECT` receiver fill-up, please mirror the output-position proof here 
(or carry an explicit regulator-aligned marker) before setting `BUCKET_SHUFFLE`.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java:
##########
@@ -2496,14 +2497,56 @@ public PlanFragment visitPhysicalSetOperation(
             setOperationNode.setColocate(true);
         }
 
-        // TODO: open comment when support `enable_local_shuffle_planner`
-        // for (Plan child : setOperation.children()) {
-        //     PhysicalPlan childPhysicalPlan = (PhysicalPlan) child;
-        //     if 
(JoinUtils.isStorageBucketed(childPhysicalPlan.getPhysicalProperties())) {
-        //         
setOperationNode.setDistributionMode(DistributionMode.BUCKET_SHUFFLE);
-        //         break;
-        //     }
-        // }
+        // Mark BUCKET_SHUFFLE only when every child is bucket-distributed on 
the same storage
+        // layout, i.e. ChildrenPropertiesRegulator actually aligned all 
children to one basic
+        // child's buckets: it enforces the non-basic children to the basic 
child's storage
+        // layout, and only does so when set-op bucket shuffle is allowed, so 
the marker follows
+        // the regulator's decision without re-checking the session variables 
here (that would
+        // risk drifting out of sync with the request-derivation gate).
+        //
+        // A single storage-bucketed child is not proof on its own that this 
set operation chose
+        // bucket shuffle: under a non-bucket (ANY) request a child can stay 
storage-bucketed
+        // independently while the other children are left unaligned, and 
marking from that child
+        // alone would drive PlanFragment.hasBucketShuffleNode(), bucket 
assignment and non-
+        // INTERSECT receiver fill-up on a set operation whose rows are not 
actually bucket-co-
+        // located. Requiring one shared storage layout keeps the marker in 
step with
+        // ChildOutputPropertyDeriver, which likewise only claims a bucket 
distribution when all
+        // children agree on the layout.
+        //
+        // Unlike hash join, BUCKET_SHUFFLE is not exclusive with isColocate 
above: for a set
+        // operation isColocate describes the bucket-aligned scheduling of the 
fragment (the
+        // basic child scans buckets directly), while BUCKET_SHUFFLE describes 
how the other
+        // children arrive (bucket-shuffle exchanges). Both routes converge to 
the same
+        // bucket-hash local exchange requirement in 
SetOperationNode.enforceAndDeriveLocalExchange.
+        boolean anyStorageBucketed = false;
+        boolean allChildrenBucketAligned = !setOperation.children().isEmpty();
+        DistributionSpecHash basicLayout = null;
+        for (Plan child : setOperation.children()) {
+            DistributionSpec childSpec = ((PhysicalPlan) 
child).getPhysicalProperties().getDistributionSpec();
+            if (!(childSpec instanceof DistributionSpecHash)) {
+                allChildrenBucketAligned = false;
+                break;
+            }
+            DistributionSpecHash childHash = (DistributionSpecHash) childSpec;
+            ShuffleType shuffleType = childHash.getShuffleType();
+            if ((shuffleType != ShuffleType.NATURAL && shuffleType != 
ShuffleType.STORAGE_BUCKETED)
+                    || childHash.getTableId() < 0) {
+                allChildrenBucketAligned = false;
+                break;
+            }
+            anyStorageBucketed |= shuffleType == ShuffleType.STORAGE_BUCKETED;
+            if (basicLayout == null) {
+                basicLayout = childHash;
+            } else if (childHash.getTableId() != basicLayout.getTableId()

Review Comment:
   This new marker also exposes a downstream fill-up bug for `INTERSECT`: 
`UnassignedScanBucketOlapTableJob.shouldFillUpInstances()` tries to skip 
intersect set ops, but it imports the Nereids algebra `Intersect` and checks 
`setOperationNode instanceof Intersect` while the translated node is 
`org.apache.doris.planner.IntersectNode`. After this line marks a 
bucket-shuffle `PhysicalIntersect`, that skip never fires, so bucket-pruned 
intersect fragments create missing-bucket receivers and shuffle rows into 
buckets that cannot produce output because the anchor side is empty. Please fix 
the downstream type check (for example, check planner `IntersectNode`) and add 
coverage that bucket-shuffle `INTERSECT` does not trigger fill-up while 
`UNION`/`EXCEPT` still do.



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

To unsubscribe, e-mail: [email protected]

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


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

Reply via email to