[spark] Infer postpone bucket count from data size#8864
Conversation
leaves12138
left a comment
There was a problem hiding this comment.
I found two correctness issues in the new bucket inference path. The existing PostponeBucketTableTest passes (18/18), and targeted cache-reuse and mixed-RowKind tests also pass. The overwrite case below is reproducible with the PR source.
| collectDataStatsByPartition(df, collectSize = !targetRowNum.isPresent) | ||
| dataStats.map { | ||
| case (partition, stats) => | ||
| val postponeRowCount = postponeRowCounts.getOrDefault(partition, 0L) |
There was a problem hiding this comment.
Existing postpone rows must not be included for an overwrite. WriteIntoPaimonTable configures writeBuilder.withOverwrite(...) before calling write, so the postpone files in the overwritten partition are removed by this commit. Counting them inflates the bucket number for data that will no longer exist. I reproduced this with 1,000 postpone rows followed by INSERT OVERWRITE of 100 rows and postpone.target-row-num-per-bucket = 100: the final partition is written with 11 buckets instead of 1. Please pass the overwrite semantics into inference (including dynamic partition overwrite) and exclude postpone rows that the commit removes.
| val numBuckets = | ||
| if (targetRowNum.isPresent) { | ||
| Math.min( | ||
| PostponeUtils.computeBucketNumByRowCount( |
There was a problem hiding this comment.
The maximum is applied only after computeBucketNumByRowCount, but that helper throws when the computed value exceeds Integer.MAX_VALUE. For example, 2,147,483,648 rows with a target of 1 and a max parallelism of 2,048 fail instead of being capped at 2,048, contradicting the documented limit. Please compute/cap in Long or BigInt before converting to Int, analogous to the size-based path.
leaves12138
left a comment
There was a problem hiding this comment.
Re-reviewed commit c466234. Both previously reported correctness issues are fixed: overwrite inference now excludes postpone rows that will be removed, and row-count inference caps before Int conversion. I also verified all 20 PostponeBucketTableTest cases with Spark 3 and compiled the changed Spark common module with Spark 4 / Scala 2.13. No further blocking issues found.
What changed
BinaryRowsize of incoming rows.postpone.target-size-per-bucket, defaulting to1 GB.postpone.target-row-num-per-bucketas an explicit row-count override.postpone.batch-write-fixed-bucket.max-parallelismis1.postpone.batch-write-fixed-bucket.max-parallelismbefore converting row-based counts toInt.Why
Spark previously used the number of input writers as the bucket count for partitions without real bucket data. Highly parallel jobs could therefore initialize many mostly empty buckets and create excessive small files, regardless of the actual data volume.
The serialized size is measured before ORC or Parquet encoding and compression, so the target represents logical uncompressed data rather than exact storage bytes.
Validation
mvn -pl paimon-spark/paimon-spark-ut -am -Pfast-build -Pspark3 -DfailIfNoTests=false -DwildcardSuites=org.apache.paimon.spark.sql.PostponeBucketTableTest -Dtest=none test(20 tests passed)mvn -pl paimon-spark/paimon-spark-common,paimon-spark/paimon-spark-ut -Pspark3 -DskipTests validatemvn -pl paimon-spark/paimon-spark-common -am -Pspark3 -DskipTests compilemvn package -Pgenerate-docs -pl paimon-docs -nsu -DskipTests -am