[SPARK-58323][SQL] Materialize AliasAware output ordering and partitioning to avoid StackOverflowError#57502
Closed
peter-toth wants to merge 2 commits into
Closed
Conversation
…oning to avoid StackOverflowError ### What changes were proposed in this pull request? `AliasAwareQueryOutputOrdering.outputOrdering` and `PartitioningPreservingUnaryExecNode.outputPartitioning` (and `BroadcastHashJoinExec`'s output-partitioning expansion) build their results with `multiTransform`, which returns a `LazyList`, and store the bounded result unforced. Force them into strict collections with `.toList` (after the existing `.take(...)`, so the short-circuit is preserved). ### Why are the changes needed? Each plan node re-wraps the child ordering/partitioning's unforced `LazyList`, so across a deep projection chain the deferred nesting grows unbounded and overflows the stack when the ordering/partitioning is later serialized or deeply traversed -- e.g. task serialization of a `SortedMergeCoalescedRDD` (SPARK-55715) that captures the child ordering, observed as a driver `StackOverflowError` at `DAGScheduler.submitMissingTasks`. ### Does this PR introduce any user-facing change? No. ### How was this patch tested? New test in `ProjectedOrderingAndPartitioningSuite` (fails before this change: `sameOrderExpressions` was a `LazyList`); existing suite passes. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: Claude Opus 4.8
dongjoon-hyun
approved these changes
Jul 24, 2026
dongjoon-hyun
left a comment
Member
There was a problem hiding this comment.
+1, LGTM (Pending CIs).
Member
|
Since SPARK-42049 is Apache Spark 3.4.0, do we need to backport this until |
Contributor
Author
|
Thanks @dongjoon-hyun for the quick review! The only place where this issue surfaced is |
…ut partitioning The `.toList` on the BroadcastHashJoinExec expansion (added for strictness) also makes the existing `case p :: Nil => p` branch reachable: a single-element expansion now returns the `HashPartitioning` directly instead of a one-element `PartitioningCollection` (previously unreachable because the expansion was an unforced `LazyList`, which the `List`-cons pattern never matched). This is the cleaner, intended form. Update the `BroadcastJoinSuite` assertion to that contract and clarify the code comment.
uros-b
approved these changes
Jul 24, 2026
peter-toth
added a commit
that referenced
this pull request
Jul 25, 2026
…oning to avoid StackOverflowError ### What changes were proposed in this pull request? `AliasAwareQueryOutputOrdering.outputOrdering` and `PartitioningPreservingUnaryExecNode.outputPartitioning` (and `BroadcastHashJoinExec`'s output-partitioning expansion) build their results with `multiTransform`, which returns a `LazyList`, and store the bounded result unforced. This forces them into strict collections with `.toList`, right after the existing `.take(...)` so the lazy short-circuit during candidate generation is preserved. Note: for `BroadcastHashJoinExec`, using a strict `List` also makes the existing `case p :: Nil => p` unwrap in `outputPartitioning` reachable — so a single-element expansion now returns the `HashPartitioning` directly instead of a one-element `PartitioningCollection` (that branch was previously dead because the expansion was an unforced `LazyList`, which a `List`-cons pattern never matches). This is the cleaner, intended form; the corresponding `BroadcastJoinSuite` assertion is updated accordingly. ### Why are the changes needed? Each plan node re-wraps the child ordering/partitioning's unforced `LazyList`, so across a deep projection chain the deferred nesting grows unbounded and overflows the stack when the ordering/partitioning is later serialized or deeply traversed. Concretely, task serialization of a `SortedMergeCoalescedRDD` (SPARK-55715) that captures the child `outputOrdering` was observed to fail on the driver with a `StackOverflowError` at `DAGScheduler.submitMissingTasks` on a large bucketed `MERGE`. The lazy `LazyList` in `SortOrder.sameOrderExpressions` (and in the projected `PartitioningCollection`) is the root cause; this has existed since the `multiTransform` path was introduced in SPARK-42049. ### Does this PR introduce _any_ user-facing change? No. ### How was this patch tested? New test in `ProjectedOrderingAndPartitioningSuite` asserting `sameOrderExpressions` and the projected `PartitioningCollection.partitionings` are strict (not `LazyList`); it fails before this change (they were `LazyList`) and passes after. `BroadcastJoinSuite` / `BroadcastJoinSuiteAE` updated for the single-element output-partitioning form and pass. Existing `ProjectedOrderingAndPartitioningSuite` / `PlannerSuite` / `EnsureRequirementsSuite` pass. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: Claude Opus 4.8 Closes #57502 from peter-toth/SPARK-58323-aliasaware-strict-ordering-partitioning. Authored-by: Peter Toth <peter.toth@gmail.com> Signed-off-by: Peter Toth <peter.toth@gmail.com> (cherry picked from commit faa1d1e) Signed-off-by: Peter Toth <peter.toth@gmail.com>
peter-toth
added a commit
that referenced
this pull request
Jul 25, 2026
…oning to avoid StackOverflowError `AliasAwareQueryOutputOrdering.outputOrdering` and `PartitioningPreservingUnaryExecNode.outputPartitioning` (and `BroadcastHashJoinExec`'s output-partitioning expansion) build their results with `multiTransform`, which returns a `LazyList`, and store the bounded result unforced. This forces them into strict collections with `.toList`, right after the existing `.take(...)` so the lazy short-circuit during candidate generation is preserved. Note: for `BroadcastHashJoinExec`, using a strict `List` also makes the existing `case p :: Nil => p` unwrap in `outputPartitioning` reachable — so a single-element expansion now returns the `HashPartitioning` directly instead of a one-element `PartitioningCollection` (that branch was previously dead because the expansion was an unforced `LazyList`, which a `List`-cons pattern never matches). This is the cleaner, intended form; the corresponding `BroadcastJoinSuite` assertion is updated accordingly. Each plan node re-wraps the child ordering/partitioning's unforced `LazyList`, so across a deep projection chain the deferred nesting grows unbounded and overflows the stack when the ordering/partitioning is later serialized or deeply traversed. Concretely, task serialization of a `SortedMergeCoalescedRDD` (SPARK-55715) that captures the child `outputOrdering` was observed to fail on the driver with a `StackOverflowError` at `DAGScheduler.submitMissingTasks` on a large bucketed `MERGE`. The lazy `LazyList` in `SortOrder.sameOrderExpressions` (and in the projected `PartitioningCollection`) is the root cause; this has existed since the `multiTransform` path was introduced in SPARK-42049. No. New test in `ProjectedOrderingAndPartitioningSuite` asserting `sameOrderExpressions` and the projected `PartitioningCollection.partitionings` are strict (not `LazyList`); it fails before this change (they were `LazyList`) and passes after. `BroadcastJoinSuite` / `BroadcastJoinSuiteAE` updated for the single-element output-partitioning form and pass. Existing `ProjectedOrderingAndPartitioningSuite` / `PlannerSuite` / `EnsureRequirementsSuite` pass. Generated-by: Claude Opus 4.8 Closes #57502 from peter-toth/SPARK-58323-aliasaware-strict-ordering-partitioning. Authored-by: Peter Toth <peter.toth@gmail.com> Signed-off-by: Peter Toth <peter.toth@gmail.com> (cherry picked from commit faa1d1e) Signed-off-by: Peter Toth <peter.toth@gmail.com>
Contributor
Author
Contributor
Author
|
Thanks for the review @dongjoon-hyun and @uros-b! I will open separate backport PR to older branches. |
This was referenced Jul 25, 2026
peter-toth
added a commit
that referenced
this pull request
Jul 26, 2026
…rtitioning to avoid StackOverflowError ### What changes were proposed in this pull request? Backport of SPARK-58323 (#57502) to branch-4.1. `AliasAwareQueryOutputOrdering.outputOrdering` and `BroadcastHashJoinExec`'s output-partitioning expansion build their results with `multiTransform`, which returns a `LazyList`, and store the bounded result unforced. This forces them into strict collections with `.toList`, right after the existing `.take(...)` so the lazy short-circuit during candidate generation is preserved. Tailored for branch-4.1 (differs from the master PR): - The `PartitioningPreservingUnaryExecNode.outputPartitioning` change is omitted: on branch-4.1 that method already materializes a strict collection (`...iterator.flatMap(...).take(...).toSeq` over an `Iterator`), so there is no unforced `LazyList` to fix. - The `BroadcastJoinSuite` single-element assertion change is omitted: on branch-4.1 `expandOutputPartitioning` always returns a `PartitioningCollection` (no `case p :: Nil` unwrap), so the single-element output shape is unchanged. ### Why are the changes needed? Each plan node re-wraps the child ordering/partitioning's unforced `LazyList`, so across a deep projection chain the deferred nesting grows unbounded and overflows the stack when the ordering/partitioning is later serialized or deeply traversed. On master this was observed as a driver `StackOverflowError` at `DAGScheduler.submitMissingTasks` during task serialization of a `SortedMergeCoalescedRDD` (SPARK-55715) on a large bucketed `MERGE`. That RDD and code path do not exist on branch-4.1, so this backport is defensive hardening of the same latent lazy-collection-in-output-ordering/partitioning hazard, which has existed since the `multiTransform` path was introduced in SPARK-42049. ### Does this PR introduce _any_ user-facing change? No. ### How was this patch tested? New test in `ProjectedOrderingAndPartitioningSuite` asserting `sameOrderExpressions` is strict (not a `LazyList`); it fails before this change and passes after. Existing `ProjectedOrderingAndPartitioningSuite` / `BroadcastJoinSuite` / `BroadcastJoinSuiteAE` pass. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: Claude Opus 4.8 Closes #57531 from peter-toth/SPARK-58323-aliasaware-strict-ordering-partitioning-4.1. Authored-by: Peter Toth <peter.toth@gmail.com> Signed-off-by: Peter Toth <peter.toth@gmail.com>
peter-toth
added a commit
that referenced
this pull request
Jul 26, 2026
…rtitioning to avoid StackOverflowError ### What changes were proposed in this pull request? Backport of SPARK-58323 (#57502) to branch-4.0. `AliasAwareQueryOutputOrdering.outputOrdering` and `BroadcastHashJoinExec`'s output-partitioning expansion build their results with `multiTransform`, which returns a `LazyList`, and store the bounded result unforced. This forces them into strict collections with `.toList`, right after the existing `.take(...)` so the lazy short-circuit during candidate generation is preserved. Tailored for branch-4.0 (differs from the master PR): - The `PartitioningPreservingUnaryExecNode.outputPartitioning` change is omitted: on branch-4.0 that method already materializes a strict collection (`...iterator.flatMap(...).take(...).toSeq` over an `Iterator`), so there is no unforced `LazyList` to fix. - The `BroadcastJoinSuite` single-element assertion change is omitted: on branch-4.0 `expandOutputPartitioning` always returns a `PartitioningCollection` (no `case p :: Nil` unwrap), so the single-element output shape is unchanged. ### Why are the changes needed? Each plan node re-wraps the child ordering/partitioning's unforced `LazyList`, so across a deep projection chain the deferred nesting grows unbounded and overflows the stack when the ordering/partitioning is later serialized or deeply traversed. On master this was observed as a driver `StackOverflowError` at `DAGScheduler.submitMissingTasks` during task serialization of a `SortedMergeCoalescedRDD` (SPARK-55715) on a large bucketed `MERGE`. That RDD and code path do not exist on branch-4.0, so this backport is defensive hardening of the same latent lazy-collection-in-output-ordering/partitioning hazard, which has existed since the `multiTransform` path was introduced in SPARK-42049. ### Does this PR introduce _any_ user-facing change? No. ### How was this patch tested? New test in `ProjectedOrderingAndPartitioningSuite` asserting `sameOrderExpressions` is strict (not a `LazyList`); it fails before this change and passes after. Existing `ProjectedOrderingAndPartitioningSuite` / `BroadcastJoinSuite` / `BroadcastJoinSuiteAE` pass. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: Claude Opus 4.8 Closes #57532 from peter-toth/SPARK-58323-aliasaware-strict-ordering-partitioning-4.0. Authored-by: Peter Toth <peter.toth@gmail.com> Signed-off-by: Peter Toth <peter.toth@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes were proposed in this pull request?
AliasAwareQueryOutputOrdering.outputOrderingandPartitioningPreservingUnaryExecNode.outputPartitioning(andBroadcastHashJoinExec's output-partitioning expansion) build their results withmultiTransform, which returns aLazyList, and store the bounded result unforced. This forces them into strict collections with.toList, right after the existing.take(...)so the lazy short-circuit during candidate generation is preserved.Note: for
BroadcastHashJoinExec, using a strictListalso makes the existingcase p :: Nil => punwrap inoutputPartitioningreachable — so a single-element expansion now returns theHashPartitioningdirectly instead of a one-elementPartitioningCollection(that branch was previously dead because the expansion was an unforcedLazyList, which aList-cons pattern never matches). This is the cleaner, intended form; the correspondingBroadcastJoinSuiteassertion is updated accordingly.Why are the changes needed?
Each plan node re-wraps the child ordering/partitioning's unforced
LazyList, so across a deep projection chain the deferred nesting grows unbounded and overflows the stack when the ordering/partitioning is later serialized or deeply traversed. Concretely, task serialization of aSortedMergeCoalescedRDD(SPARK-55715) that captures the childoutputOrderingwas observed to fail on the driver with aStackOverflowErroratDAGScheduler.submitMissingTaskson a large bucketedMERGE. The lazyLazyListinSortOrder.sameOrderExpressions(and in the projectedPartitioningCollection) is the root cause; this has existed since themultiTransformpath was introduced in SPARK-42049.Does this PR introduce any user-facing change?
No.
How was this patch tested?
New test in
ProjectedOrderingAndPartitioningSuiteassertingsameOrderExpressionsand the projectedPartitioningCollection.partitioningsare strict (notLazyList); it fails before this change (they wereLazyList) and passes after.BroadcastJoinSuite/BroadcastJoinSuiteAEupdated for the single-element output-partitioning form and pass. ExistingProjectedOrderingAndPartitioningSuite/PlannerSuite/EnsureRequirementsSuitepass.Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Opus 4.8