andygrove opened a new issue, #5510:
URL: https://github.com/apache/datafusion-comet/issues/5510

   ## Describe the bug
   
   `CometBroadcastExchangeExec` fails outright when 
`spark.kryo.registrationRequired=true`, which makes Kryo reject any class it 
has not been told about. This has nothing to do with the in-memory cache work; 
it reproduces on `main` with only Comet's broadcast exchange in play.
   
   `CometBroadcastExchangeExec.relationFuture` broadcasts the result of 
`Utils.coalesceBroadcastBatches`, which is an `Array[ChunkedByteBuffer]`. Spark 
registers `ChunkedByteBuffer` in `KryoSerializer.toRegister`, but not an array 
of them, and Comet has no Kryo registrator of its own, so the broadcast throws:
   
   ```
   java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException:
     Class is not registered: org.apache.spark.util.io.ChunkedByteBuffer[]
   Note: To register this class use: 
kryo.register(org.apache.spark.util.io.ChunkedByteBuffer[].class);
        at com.esotericsoftware.kryo.Kryo.getRegistration(Kryo.java:503)
        at 
com.esotericsoftware.kryo.util.DefaultClassResolver.writeClass(DefaultClassResolver.java:97)
        at com.esotericsoftware.kryo.Kryo.writeClassAndObject(Kryo.java:645)
        at 
org.apache.spark.serializer.KryoSerializationStream.writeObject(KryoSerializer.scala:285)
        ...
        at 
org.apache.spark.sql.comet.CometBroadcastExchangeExec.doExecuteBroadcast(CometBroadcastExchangeExec.scala:232)
   ```
   
   Because the message names a Spark utility class rather than anything 
Comet-shaped, it is hard to attribute to Comet at all.
   
   ## Steps to reproduce
   
   With `spark.serializer=org.apache.spark.serializer.KryoSerializer` and 
`spark.kryo.registrationRequired=true`, run any query that produces a 
`CometBroadcastExchange`:
   
   ```sql
   SELECT /*+ BROADCAST(b) */ a._1, b._2 FROM tbl_a a JOIN tbl_b b ON a._1 = 
b._1
   ```
   
   The plan contains `CometBroadcastHashJoin` over `CometBroadcastExchange`, 
and `collect()` fails as above. Disabling 
`spark.comet.exec.broadcastExchange.enabled` avoids it.
   
   ## Expected behavior
   
   A native broadcast should work under `spark.kryo.registrationRequired=true`, 
as Spark's own broadcast does.
   
   ## Additional context
   
   A fix is currently carried inside 
https://github.com/apache/datafusion-comet/pull/5051, which needed a Kryo 
registrator anyway for its cached batch format and covers this case because the 
cache write path hands back the same `ChunkedByteBuffer` type. @viirya raised 
the cache half of that in review; this half fell out of checking whether the 
registrator needed anything beyond the cached batch.
   
   This issue exists so that fix can be split out and land on its own, since it 
is a pre-existing bug rather than one that PR introduces, and it should not 
have to wait on an experimental default-off feature.
   
   What a standalone fix needs:
   
   - Register `ChunkedByteBuffer`, `Array[ChunkedByteBuffer]`, 
`Array[ByteBuffer]` and the heap `ByteBuffer` class. In PR 5051 these live in 
`Utils.arrowBytesKryoClasses`, next to 
`serializeBatches`/`serializeBatchColumns`, which are what produce them.
   - A `KryoRegistrator` implementation for users to point 
`spark.kryo.registrator` at.
   - Note that Comet **cannot** install this itself. `KryoSerializer` reads 
`spark.kryo.registrator` into a `val` in its constructor, and `SparkEnv.create` 
builds it at `SparkContext.scala:478`, whereas `PluginContainer` is constructed 
at line 574. Unlike `spark.sql.cache.serializer` — a `StaticSQLConf` read 
lazily from the session, which is why 
`CometDriverPlugin.maybeSetCacheSerializer` can inject it — this one is already 
captured before any plugin runs. Setting it from the driver plugin would reach 
executors but not the driver's own `SparkEnv`, so it would appear to work in 
cluster mode and fail in local mode. So it has to be documented, with a startup 
warning when Kryo, `registrationRequired`, and a missing registrator are 
combined.
   - A regression test. PR 5051 has one as `Comet broadcast exchange survives 
Kryo with registration required` in `CometInMemoryCacheKryoSuite`; standalone 
it belongs somewhere broadcast-shaped instead.
   
   Worth checking as part of this whether any other Comet path hands Spark's 
serializer an unregistered class. `getByteArrayRdd(child).collect()` in the 
same file returns `(Long, ChunkedByteBuffer)` tuples through the task-result 
serializer; `Tuple2` comes from Chill and `ChunkedByteBuffer` from Spark, so 
that one appears covered, but I have not swept the shuffle paths.
   


-- 
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