Hash partitioning not falling back to Spark for high precision decimal partition keys #3079
Copy link
Copy link
Open
Labels
bugSomething isn't workingSomething isn't workingpriority:criticalData corruption, silent wrong results, security issuesData corruption, silent wrong results, security issues
Milestone
Description
Describe the bug
Comet's murmur3 hash implementation is not compatible with Spark for high precision types and we should fall back to Spark if the partitioning keys contain these types.
We just need to add a check like this:
271 - case dt: DecimalType =>
272 - // High precision decimals (> 18) fall back to Spark because Spark converts them
273 - // to Java BigDecimal before hashing, which has different behavior than native hashing
274 - dt.precision <= 18
Steps to reproduce
No response
Expected behavior
No response
Additional context
No response
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't workingpriority:criticalData corruption, silent wrong results, security issuesData corruption, silent wrong results, security issues
Activity
andygrove commentedon Jan 13, 2026
This is trivial to fix but requires updating some golden files.
andygrove commentedon Jan 13, 2026
Acutally, this is not a bug. We do not need to guarantee that we allocate the same partitions as Spark.
andygrove commentedon Sep 22, 2026
Reopening. I closed this saying we don't need to allocate the same partitions as Spark, and that was wrong.
We have two shuffle implementations and both can appear in the same plan.
applyCometShufflepicks native or columnar per exchange with no harmonization across the plan, so the two sides of a join can end up with different ones. The columnar path uses Spark'sHashPartitioning.partitionIdExpressionand the native path uses the Rust murmur3. On a decimal key with precision above 18 those disagree. So the native path does have to match Spark, not for its own sake, but to stay consistent with its sibling.On main at
9a4d5f283, joining a Parquet table to a JSON view on aDECIMAL(38,0)key:Comet returns zero rows where Spark returns four. Default
spark.comet.shuffle.mode=auto, nothing exotic set. It reproduces with AQE enabled too, as long as coalescing doesn't collapse the fixture into a single partition, which is what you'd get at any real data volume. That's most likely why the suites have never caught it.The mismatch itself is in
hash_array_decimal!, which hashesvalue.to_le_bytes(), a fixed sixteen-byte little-endian i128. Spark hashestoJavaBigDecimal.unscaledValue.toByteArray(), which is minimal-length big-endian two's complement. Byte order and length both differ, and murmur3 mixes length intofmix, so every value diverges. Unscaled1inDECIMAL(38,0)at seed 42 hashes to-386724586in Spark and-680163996in Comet, which lands in partition 4 against partition 6 out of 7.#6005 implements the guard described in the original report. #5994 tracks making the native encoding match Spark, which would remove the need for the guard and also unblock
hashandxxhash64for these types.andygrove commentedon Sep 24, 2026
A cached relation makes this reachable from a query that has Comet turned off. The cache keeps the partitions native shuffle produced, and the cache scan reports the cached plan's
hashpartitioning(k, 200)to every later query. A later join onkthen shuffles only the other side, so if that side goes through Spark's hash, the join silently drops rows. That happens with Comet off for the query, or with Comet on when the other side's shuffle is columnar.On
mainat 67803a7, default Spark 4.1 profile, withspark.sql.autoBroadcastJoinThreshold=-1:CometColumnarShuffle)CometNativeShuffle)It is the same with AQE on or off, and with
spark.comet.exec.inMemoryCache.enabledon or off, since the partitioning comes from the cached plan rather than the cache format.decimal(20,2)keys fail too. Decimal keys of precision 18 or less, and integer, string, binary, date, timestamp, timestamp_ntz, boolean, float and double keys, all join correctly after a native-shuffled cache.The first row is the new part: without a cache, a query with Comet off never sees a native shuffle. #6005 should fix this too, since it keeps wide-decimal keys off native shuffle, and a cached case might be worth adding to its tests.
fix: do not hash decimals above precision 18 in native shuffle (port of