@@ -138,7 +138,7 @@ impl ExternalSorterMetrics {
138138///
139139/// When memory is exhausted, sorted runs are spilled directly to disk
140140/// (one spill file per run — no merge needed since runs are already
141- /// sorted). [`MultiLevelMerge`] handles the final merge from disk
141+ /// sorted). `MultiLevelMergeBuilder` handles the final merge from disk
142142/// with dynamic fan-in.
143143///
144144/// ```text
@@ -169,7 +169,6 @@ impl ExternalSorterMetrics {
169169/// pressure, chunk sizes shrink and radix sort amortizes less — at
170170/// `batch_size` or below, the pipeline falls back to lexsort,
171171/// matching the old per-batch sort behavior.
172- /// ```
173172struct ExternalSorter {
174173 // ========================================================================
175174 // PROPERTIES:
@@ -451,20 +450,10 @@ impl ExternalSorter {
451450
452451 // Determine if we must take the spill path.
453452 //
454- // We must spill if:
455- // 1. We already spilled during the insert phase, OR
456- // 2. We have multiple sorted runs but merge_reservation is 0.
457- //
458- // Case 2 matters because the in-memory merge needs to allocate
459- // cursor infrastructure (RowCursorStream / FieldCursorStream)
460- // at build time, before any run data is consumed. The cursor
461- // allocation comes from merge_reservation. If that's 0, the
462- // pool is fully occupied by sorted run data and the cursor
463- // can't allocate. Spilling to disk frees pool memory, and
464- // MultiLevelMerge handles the merge with dynamic fan-in —
465- // reading from spill files that don't hold pool memory.
466- let must_spill = self . spilled_before ( )
467- || ( self . sorted_runs . len ( ) > 1 && self . merge_reservation . size ( ) == 0 ) ;
453+ // We must spill if we already spilled during the insert phase.
454+ // The merge-from-disk path handles combining spill files with
455+ // any remaining in-memory runs.
456+ let must_spill = self . spilled_before ( ) ;
468457
469458 if must_spill {
470459 // Spill remaining sorted runs. Since runs are already sorted,
@@ -484,13 +473,10 @@ impl ExternalSorter {
484473 . with_reservation ( self . merge_reservation . take ( ) )
485474 . build ( )
486475 } else {
487- // In-memory path: we have 0 runs, 1 run (no merge needed),
488- // or multiple runs with merge_reservation > 0 providing
489- // headroom for cursor allocation.
490- //
491- // Release merge_reservation back to the pool — in the
492- // non-spill path, merge_sorted_runs allocates cursor memory
493- // from the pool directly (freed merge_reservation bytes).
476+ // In-memory path: no prior spills. We have 0, 1, or multiple
477+ // sorted runs. Release merge_reservation (if any) back to the
478+ // pool — merge_sorted_runs allocates cursor memory from pool
479+ // headroom directly.
494480 self . merge_reservation . free ( ) ;
495481 self . merge_sorted_runs ( self . metrics . baseline . clone ( ) )
496482 }
0 commit comments