andygrove opened a new pull request, #5810:
URL: https://github.com/apache/datafusion-comet/pull/5810

   ## Which issue does this PR close?
   
   Closes #5776.
   
   ## Rationale for this change
   
   iceberg-rust's `FanoutWriter` keeps its per-partition writers in a `HashMap` 
and closes them by iterating it directly:
   
   ```rust
   async fn close(mut self) -> Result<O> {
       for (_, mut writer) in self.partition_writers {
           self.output.extend(writer.close().await?);
       }
       Ok(O::from_iter(self.output))
   }
   ```
   
   Under Rust's per-process `RandomState` that gives a different `DataFile` 
order on every run. That order becomes the manifest entry order, which becomes 
the scan-task order, which becomes the row order of an unordered `SELECT *`. 
Iceberg's own 
`TestMetadataTablesWithPartitionEvolution.testPartitionColumnNamedPartition` 
compares such a `SELECT *` positionally against what iceberg-java wrote, so it 
failed whenever the two disagreed.
   
   Measured on Spark 4.1 + Iceberg 1.11 by writing eight partitions through the 
fanout writer and printing the manifest order on three consecutive runs:
   
   ```
   run 1: partition=1, partition=2, partition=5, partition=0, partition=7, 
partition=4, partition=3, partition=6
   run 2: partition=6, partition=7, partition=4, partition=5, partition=0, 
partition=3, partition=2, partition=1
   run 3: partition=2, partition=0, partition=4, partition=5, partition=7, 
partition=3, partition=6, partition=1
   ```
   
   The clustered writer produced `partition=0 .. partition=7` on every run.
   
   Two notes on the issue as filed. The failing parameter set includes ORC and 
AVRO, which looked like evidence the Parquet writer could not be involved; it 
is not, because that test does not use the class's `createTable` helper and so 
writes Parquet whatever the `fileFormat` parameter says. And the test only has 
two partitions, so an unfixed writer already passes about half the time, which 
is why only a subset of the matrix reported failures.
   
   ## What changes are included in this PR?
   
   - Sort the fanout writer's output by file path in `InnerWriter::close`.
   
     It cannot be creation order instead: `RecordBatchPartitionSplitter` splits 
a batch through a map as well, so which partition is written first, and 
therefore which file-name counter it gets, is itself unstable. Path order sorts 
by partition directory and then by that counter within a partition, and needs 
nothing from the write order. The clustered and unpartitioned writers append in 
creation order and are already deterministic, so only the fanout arm sorts.
   
   - Record the behaviour under accepted divergences in `iceberg-writes.md`. 
This is determinism, not parity: iceberg-java's fanout writer iterates its own 
`StructLikeMap`, so the two agree only where that order and path order 
coincide, which they do for the ascending partition values the upstream test 
uses.
   
   Part of #5649, and one of the two remaining failure buckets blocking #5644.
   
   ## How are these changes tested?
   
   A Rust test (`fanout_write_returns_data_files_in_a_deterministic_order`) and 
a Scala test (`a fanout write lists its data files in a stable order`), both 
over eight partitions rather than the upstream test's two, so an unfixed writer 
lands in the right order by luck 1 time in 8! rather than half the time. Both 
were confirmed to fail three times out of three on the unfixed writer and pass 
three times out of three with the fix.
   
   The end-to-end scenario from the issue was also swept over 
`spark.sql.shuffle.partitions` in `{1, 2, 4, 200}` against both writers: 
`parts=2` with the native writer was the combination that reproduced the 
reordering, and all eight combinations now return the expected rows on three 
consecutive runs.
   
   `CometIcebergWriteActionSuite` (62), `CometIcebergWriteDetectionSuite` (48), 
`CometIcebergSystemFunctionSuite` (13), `CometIcebergRewriteActionSuite` (5) 
and the native `iceberg` tests (53) pass.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   
   https://claude.ai/code/session_018sqhRddDS2gFfJRT21QDsE


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