edgarRd opened a new issue, #18901:
URL: https://github.com/apache/iceberg/issues/18901

   ### Apache Iceberg version
   
   1.11.0
   
   ### Query engine
   
   Spark
   
   ### Please describe the bug 🐞
   
   ### Problem
   
   `SparkBatch.planInputPartitions()` creates a new table broadcast and a new 
`FileIO` broadcast every time it is called:
   
   ```java
   Broadcast<Table> tableBroadcast =
       sparkContext.broadcast(SerializableTableWithSize.copyOf(table));
   Broadcast<FileIO> fileIOBroadcast =
       sparkContext.broadcast(SerializableFileIOWithSize.wrap(fileIO.get()));
   ```
   
   `SparkScan.toBatch()` returns a new `SparkBatch` on each call, so nothing is 
reused between calls, even though the table and the `FileIO` do not change 
during the lifetime of a scan.
   
   Spark calls `planInputPartitions()` once per copy of a scan's physical node, 
while it is still planning the query. When dynamic partition pruning filters 
nest over a chain of joins on a partition column - something similar to 
([SPARK-48486](https://issues.apache.org/jira/browse/SPARK-48486)) - Spark 
plans an exponential number of copies, and each one adds two broadcasts to the 
driver. Iceberg never destroys these broadcasts, so they stay in the driver's 
block manager until the plans that reference them are garbage collected.
   
   ### What we saw
   
   A production query on Spark 4.1.1 with Iceberg 1.11.0, with 17 left joins of 
which 10 are on the partition column:
   
   - the driver created 264,382 broadcasts, exactly half from each of the two 
`sparkContext.broadcast` calls above and none from anywhere else
   - the driver's `MemoryStore` went from 14.2 GiB free to about 1 MiB in 
roughly 6.5 minutes and never recovered
   - the driver failed without starting a job or a stage
   
   The same query succeeds with 
`spark.sql.optimizer.dynamicPartitionPruning.enabled=false`.
   
   The Spark 3.5, 4.0, 4.1 and 4.2 modules on `main` have the same code.
   
   ### Expected
   
   Planning input partitions again for the same scan should reuse the 
broadcasts it already created. The number of broadcasts should grow with the 
number of scans in the query, not with the number of times Spark plans them.
   
   ### How to reproduce
   
   Create seven small partitioned tables and join them on the partition column, 
with a selective filter on the first one:
   
   ```sql
   CREATE TABLE t0 (id BIGINT, ds STRING) USING iceberg PARTITIONED BY (ds);
   INSERT INTO t0 VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd');
   -- repeat for t1 .. t6
   
   SELECT t0.id
   FROM t0
   LEFT JOIN t1 ON t0.ds = t1.ds AND t0.id = t1.id
   LEFT JOIN t2 ON t0.ds = t2.ds AND t0.id = t2.id
   LEFT JOIN t3 ON t0.ds = t3.ds AND t0.id = t3.id
   LEFT JOIN t4 ON t0.ds = t4.ds AND t0.id = t4.id
   LEFT JOIN t5 ON t0.ds = t5.ds AND t0.id = t5.id
   LEFT JOIN t6 ON t0.ds = t6.ds AND t0.id = t6.id
   WHERE t0.id < 3;
   ```
   
   With default settings this query creates 415 to 429 broadcasts on `main`, 
depending on the Spark version, for seven tables of four rows each. The count 
can be read from the `Created broadcast N from broadcast at SparkBatch.java` 
lines in the driver log, or from the ID of a broadcast created before and after 
the query. Each additional join on `ds` roughly doubles it.
   
   Calling `scan.toBatch().planInputPartitions()` repeatedly on a single scan 
shows the same thing in isolation: every call creates two new broadcasts.
   
   ### Scope
   
   The exponential number of DPP subqueries is a Spark planning problem and is 
tracked in SPARK-48486 (it is unverified if this spark issue is exactly the 
nesting root cause) . This issue is only about Iceberg creating new broadcasts 
for every one of those planning calls, which turns slow planning into a driver 
out-of-memory failure.
   
   ### Willingness to contribute
   
   - [x] I can contribute a fix for this bug independently
   - [ ] I would be willing to contribute a fix for this bug with guidance from 
the Iceberg community
   - [ ] I cannot contribute a fix for this bug at this time


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