Skip to content

[GLUTEN-9671][VL] Fix broadcast exchange stackoverflow due to Kryo serialization#10541

Merged
FelixYBW merged 2 commits intoapache:mainfrom
felixloesing:fix_broadcast_kryo_cycle
Aug 29, 2025
Merged

[GLUTEN-9671][VL] Fix broadcast exchange stackoverflow due to Kryo serialization#10541
FelixYBW merged 2 commits intoapache:mainfrom
felixloesing:fix_broadcast_kryo_cycle

Conversation

@felixloesing
Copy link
Copy Markdown
Contributor

This pull request introduces a safer and more robust approach for handling Spark's BroadcastMode during serialization. The main improvement is the introduction of a new SafeBroadcastMode abstraction and related utilities, which help avoid serialization issues that caused a Stackoverflow exception during broadcast exchanges. BroadcastMode was introduced in this PR that caused the issue we observed. HashedRelationBroadcastMode embeds Catalyst expression trees, which are not safe to Kryo-serialize when running with spark.kryo.referenceTracking=false (default internally).

With this change, the broadcast payload now contains only primitives and byte arrays (no Catalyst trees). For bound keys, we serialize just column ordinals (+ null-aware flag) and for computed keys (e.g., upper(col)), we serialize the key expressions once as Java bytes and deserialize only where needed to build projections.

Test Plan

Ran internal test set (50 queries) and ran other query specifically checking if
spark.gluten.velox.offHeapBroadcastBuildRelation.enabled=true; works.

This pull request introduces a safer and more robust approach for
handling Spark's `BroadcastMode` during serialization. The main
improvement is the introduction of a new `SafeBroadcastMode` abstraction
and related utilities, which help avoid serialization issues that caused
a Stackoverflow exception during broadcast exchanges.
BroadcastMode was introduced in the
[PR](apache#8116) that caused
the issues observed. HashedRelationBroadcastMode embeds Catalyst
expression trees, which are not safe to Kryo-serialize when running with
`spark.kryo.referenceTracking=false` (default internally).

With this change, the broadcast payload now contains only primitives and
byte arrays (no Catalyst trees). For bound keys, we serialize just
column ordinals (+ null-aware flag) and for computed keys (e.g.,
upper(col)), we serialize the key expressions once as Java bytes and
deserialize only where needed to build projections.

Ran internal test set (50 queries) and ran other query specifically
checking if
`spark.gluten.velox.offHeapBroadcastBuildRelation.enabled=true;` works.
@github-actions github-actions bot added the VELOX label Aug 26, 2025
@github-actions
Copy link
Copy Markdown

#9671

@FelixYBW
Copy link
Copy Markdown
Contributor

@JkSelf It fixed the odd issue #9671. Can you take a look?

Spark set spark.kryo.referenceTracking=true. The fix can set it to false which boosts performance.

@JkSelf
Copy link
Copy Markdown
Contributor

JkSelf commented Aug 27, 2025

@felixloesing Thanks for your fix. Just curious — why does Gluten require this change, while vanilla Spark doesn't?

@felixloesing
Copy link
Copy Markdown
Contributor Author

felixloesing commented Aug 27, 2025

@felixloesing Thanks for your fix. Just curious — why does Gluten require this change, while vanilla Spark doesn't?

@JkSelf This is also described in the PR that introduced the BroadcastMode to BuildSideRelation but let me summarize. Vanilla Spark doesn’t have this because it computes the transform inside BroadcastExchangeExec, so there’s no post‑reuse mismatch to reconcile. In Gluten, if exchange re-use, the buildKeys of ColumnarSubqueryBroadcastExec and the buildKeys of BroadcastExchangeExec may not be the same, so it is difficult to construct the projection of the transform. That's why Gluten contains the BroadcastMode in the BuildSideRelation and that introduces Catalyst trees in the exchange that cannot be serialized in Kryo so it causes a StackOverflow exception.

Copy link
Copy Markdown
Contributor

@JkSelf JkSelf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your fix. LGTM except two minor comments.

oos.writeObject(keys)
oos.flush()
bos.toByteArray
} finally {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can we add a catch clause to log the exception and make debugging easier?

try {
ois = new ObjectInputStream(bis)
ois.readObject().asInstanceOf[Seq[Expression]]
} finally {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto.

@felixloesing
Copy link
Copy Markdown
Contributor Author

@JkSelf Thanks for the review! Added a catch clause and logged the exception.

Copy link
Copy Markdown
Contributor

@JkSelf JkSelf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for the fix.

@FelixYBW FelixYBW merged commit 91c52e1 into apache:main Aug 29, 2025
53 checks passed
wForget pushed a commit to wForget/gluten that referenced this pull request Sep 17, 2025
…rialization (apache#10541)

This pull request introduces a safer and more robust approach for handling Spark's BroadcastMode during serialization. The main improvement is the introduction of a new SafeBroadcastMode abstraction and related utilities, which help avoid serialization issues that caused a Stackoverflow exception during broadcast exchanges. BroadcastMode was introduced in this PR that caused the issue we observed. HashedRelationBroadcastMode embeds Catalyst expression trees, which are not safe to Kryo-serialize when running with spark.kryo.referenceTracking=false (default internally).

With this change, the broadcast payload now contains only primitives and byte arrays (no Catalyst trees). For bound keys, we serialize just column ordinals (+ null-aware flag) and for computed keys (e.g., upper(col)), we serialize the key expressions once as Java bytes and deserialize only where needed to build projections.

(cherry picked from commit 91c52e1)
wForget added a commit that referenced this pull request Sep 17, 2025
…ckoverflow due to Kryo serialization #10733

This pull request introduces a safer and more robust approach for handling Spark's BroadcastMode during serialization. The main improvement is the introduction of a new SafeBroadcastMode abstraction and related utilities, which help avoid serialization issues that caused a Stackoverflow exception during broadcast exchanges. BroadcastMode was introduced in this PR that caused the issue we observed. HashedRelationBroadcastMode embeds Catalyst expression trees, which are not safe to Kryo-serialize when running with spark.kryo.referenceTracking=false (default internally).

With this change, the broadcast payload now contains only primitives and byte arrays (no Catalyst trees). For bound keys, we serialize just column ordinals (+ null-aware flag) and for computed keys (e.g., upper(col)), we serialize the key expressions once as Java bytes and deserialize only where needed to build projections.

(cherry picked from commit 91c52e1)

Co-authored-by: Felix Loesing <felix.loesing@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants