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

   ### Apache Iceberg version
   
   1.5.0
   
   ### Query engine
   
   Spark
   
   ### Please describe the bug 🐞
   
   # Summary 
   Doing the following should not leak any significant amount of memory.
   ```java
   sparkSession.sql("select * from 
icebergcatalog.db.table").coalesce(4).foreachPartition( (iterator) -> {
    while (iterator.hasNext()) iterator.next();
   });
   ```
   
   A workaround is to use repartition() instead however this requires more 
resources to handle spilling shuffling etc..
   
   Spark version: Spark 3.4.X
   
   # Details
   
   The Below code can be run on a sufficiently large iceberg table.
   
   ```java
     static AtomicInteger partitionCounter = new AtomicInteger(0);
   
       static void reproduceBug(SparkSession sparkSession, String table) {
   
           sparkSession.sql("select * from 
"+table).coalesce(4).foreachPartition( (iterator) -> {
               int partition = partitionCounter.getAndIncrement();
               AtomicLong rowCounter = ThreadLocal.withInitial(() -> new 
AtomicLong(0)).get();
   
               while (iterator.hasNext()) {
                   iterator.next();
                   if (rowCounter.getAndIncrement() % 100000 == 0) {
                       System.out.println(partition + " " + rowCounter.get());
                   }
               }
           });
       }
   ```
   
   The following image is me running the reproduceBug method over sufficiently 
large table that we have in our environment with ~500 columns. 
   
   <img width="1344" alt="Image" 
src="https://github.com/user-attachments/assets/eec2f689-7d2a-4c95-a295-4678ddbdf948";
 />
   
   The following image shows the "Dominators" report in VisualVM 
org.apache.spark.TaskContextImpl 
   
   <img width="1353" alt="Image" 
src="https://github.com/user-attachments/assets/559b8827-6969-4e4d-a486-741244361fce";
 />
   
   Digging deeper we see that the onCallbacks is keeping an anonymous class 
inside ```org.apache.spark.sql.execution.datasources.v2.DataSourceRDD```  and 
that is holding a reference to 
```org.apache.iceberg.spark.source.RowDataReader``` 
   
   I believe this callback is added here 
https://github.com/apache/spark/blob/master/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/v2/DataSourceRDD.scala#L90
   We also see the ```org.apache.iceberg.util.Filter``` iterator holding a 
heavy reference.
   <img width="1317" alt="Image" 
src="https://github.com/user-attachments/assets/23b2a235-2263-423a-b614-b8d1e1e82ed4";
 />
   
   # Exploring the problem 
   Is this inherently a bug in 
```org.apache.spark.sql.execution.datasources.v2.DataSourceRDD```? Or should 
iterators not hold onto state no longer needed once advanced to the end? Is the 
iterator even exhausted?  Once an iterator is exhausted there is no longer a 
need for referencing. However this kind of breaks the concept of a 
CloseableIterator which has an explicit close vs an implicit close where you 
could detect hasNext() is false and auto-close. then even ignore a duplicate 
close() as it was handled by an implicit close() of iterator exhaustion. I 
believe an iterator accumulating hundreds of megabytes of state kind of breaks 
the implicit "expected" contract of an iterator being a streaming set of 
objects. There might even be a distinction between closing and simply holding 
onto large object references.
   
   
   Digging deeper I see ```items = 
org.apache.iceberg.parquet.ParquetReader$FileIterator#6```  holding onto a 
model reference. It might be possible to null out the model references when 
hasNext() is false. 
   
   ```java
      @Override
       public boolean hasNext() {
         boolean hasNext = valuesRead < totalValues; 
         if (!hasNext) {
           this.model = null;
         }
         return hasNext;
       }
   ```
   
   <img width="1347" alt="Image" 
src="https://github.com/user-attachments/assets/68f781b0-6f57-4a86-884c-63d4e0aca0b4";
 />
   
   
   
   ### Willingness to contribute
   
   - [x] I can contribute a fix for this bug independently
   - [x] 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