andygrove commented on code in PR #7234:
URL: https://github.com/apache/datafusion-comet/pull/7234#discussion_r4404671549


##########
native/shuffle/src/writers/buf_batch_writer.rs:
##########
@@ -118,7 +154,15 @@ impl<S: Borrow<ShuffleBlockWriter>, W: Write> 
BufBatchWriter<S, W> {
         write_time: &Time,
     ) -> datafusion::common::Result<usize> {
         self.check_scratch(scratch);
-        let batch_size = self.batch_size;
+        let Some(batch_size) = self.coalesce_batch_size else {
+            return self.write_batch_to_buffer(
+                batch,
+                scratch,
+                codec_context,
+                encode_time,
+                write_time,
+            );
+        };
         let coalescer = self.coalescer.get_or_insert_with(|| {
             // Enable BatchCoalescer's zero-copy passthrough for batches that 
are already big

Review Comment:
   You're right, the old wording described `PartitionedBatchIterator` output, 
which no longer reaches this path. I rewrote the comment around the only 
remaining caller, the single-partition path: it streams its input batches 
straight in and routes `>= batch_size` batches here directly, so those are 
written as one oversized block rather than split at `batch_size` (6fe4add2d0).



##########
native/shuffle/src/writers/partition_writer.rs:
##########
@@ -54,6 +54,10 @@ pub(crate) trait PartitionWriter: Send {
     ///
     /// Must be called exactly once per partition, in ascending id order, so 
the
     /// writer can lay partitions out contiguously and record their offsets.
+    ///
+    /// `iter` should yield maximal `batch_size` chunks plus at most one 
smaller tail, as
+    /// `PartitionedBatchIterator` does. [`LocalPartitionWriter`] writes each 
batch as its own
+    /// block in multi-partition mode, so many small batches would become many 
small blocks.

Review Comment:
   Agreed, `write` has the same property, since `PartitionedSpill::write` also 
writes each batch as its own block and spilled blocks are copied to the output 
as they are. I moved the note to the `PartitionWriter` trait docs so it covers 
both `write` and `finish_partition`, and it now says why: many small batches 
become many small blocks (6fe4add2d0).



##########
native/shuffle/src/writers/local/local_partition_writer.rs:
##########
@@ -502,6 +527,52 @@ mod tests {
         }
     }
 
+    /// Partition offsets are tracked arithmetically instead of read back from 
the output
+    /// file, so the index must still describe the data file exactly: every 
offset lands on a
+    /// block boundary and each partition's blocks decode to the rows written 
for it, with a
+    /// spilled prefix and in-memory tail both accounted for.
+    #[test]
+    fn offsets_match_data_file_with_spilled_and_in_memory_batches() {

Review Comment:
   You're right that `spilled_partitions_read_back_in_write_order` already 
covers the spilled prefix plus in-memory tail with arithmetic offsets, so my 
test added nothing. I replaced it with 
`offsets_match_data_file_for_spill_only_and_empty_partitions`. It uses four 
partitions: a spill-only partition, an empty one, an in-memory-only one, and an 
empty last one. It asserts that empty partitions have zero-length ranges, that 
the final offset equals the file length, and that every non-empty range decodes 
to the expected row count (6fe4add2d0).



##########
native/shuffle/src/writers/local/local_partition_writer.rs:
##########
@@ -345,11 +358,23 @@ impl PartitionWriter for LocalPartitionWriter {
                 )?;
                 writer.writer_stream_position()?
             }
-            DataOutput::Multi { output_writer, .. } => {
+            DataOutput::Multi {
+                output_writer,
+                bytes_written,
+                ..
+            } => {
                 let mut write_timer = metrics.write_time.timer();
                 output_writer.flush()?;
                 let pos = output_writer.stream_position()?;
                 write_timer.stop();
+                // The offsets were derived from the running total; the file 
must agree,
+                // or the index would point readers at the wrong bytes.
+                if pos != *bytes_written {
+                    return Err(DataFusionError::Execution(format!(
+                        "shuffle write error: data file holds {pos} bytes but 
the partition \
+                         offsets account for {bytes_written}"
+                    )));
+                }

Review Comment:
   Good call, nothing exercised it. I added 
`finish_all_fails_when_data_file_disagrees_with_offsets`, following your 
recipe. It uses a 64-byte write buffer and fails partition 0 after its first 
block reached the output. It then finishes partition 1 and checks that 
`finish_all` returns the "partition offsets account for" error and that no 
offsets are published (6fe4add2d0).



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