1
+ use std:: collections:: VecDeque ;
1
2
use std:: fmt;
2
3
use std:: sync:: Arc ;
3
4
@@ -11,7 +12,9 @@ use datafusion_physical_expr::{EquivalenceProperties, Partitioning, PhysicalExpr
11
12
use datafusion_physical_plan:: execution_plan:: { Boundedness , EmissionType } ;
12
13
use datafusion_physical_plan:: metrics:: MetricsSet ;
13
14
use datafusion_physical_plan:: { DisplayAs , DisplayFormatType , ExecutionPlan , PlanProperties } ;
15
+ use itertools:: Itertools ;
14
16
use object_store:: ObjectStoreScheme ;
17
+ use vortex_error:: VortexExpect ;
15
18
use vortex_expr:: datafusion:: convert_expr_to_vortex;
16
19
use vortex_expr:: { VortexExpr , and} ;
17
20
@@ -212,18 +215,56 @@ fn repartition_by_size(
212
215
) -> Vec < Vec < PartitionedFile > > {
213
216
let total_file_count = all_files. len ( ) ;
214
217
let total_size = all_files. iter ( ) . map ( |f| f. object_meta . size ) . sum :: < usize > ( ) ;
215
- let target_partition_size = total_size / ( desired_partitions + 1 ) ;
218
+ let target_partition_size = total_size / desired_partitions;
216
219
217
220
let mut partitions = Vec :: with_capacity ( desired_partitions) ;
218
221
219
222
let mut curr_partition_size = 0 ;
220
223
let mut curr_partition = Vec :: default ( ) ;
221
224
222
- for file in all_files. into_iter ( ) {
223
- curr_partition_size += file. object_meta . size ;
224
- curr_partition. push ( file. clone ( ) ) ;
225
+ let mut all_files = VecDeque :: from_iter (
226
+ all_files
227
+ . into_iter ( )
228
+ . sorted_unstable_by_key ( |f| f. object_meta . size ) ,
229
+ ) ;
230
+
231
+ while !all_files. is_empty ( ) && partitions. len ( ) < desired_partitions {
232
+ // If the current partition is empty, we want to bootstrap it with the biggest file we have leftover.
233
+ let file = if curr_partition. is_empty ( ) {
234
+ all_files. pop_back ( )
235
+ // If we already have files in the partition, we try and fill it up.
236
+ } else {
237
+ // Peak at the biggest file left
238
+ let biggest_file_size = all_files
239
+ . back ( )
240
+ . vortex_expect ( "We must have at least one item" )
241
+ . object_meta
242
+ . size ;
243
+
244
+ let smallest_file_size = all_files
245
+ . front ( )
246
+ . vortex_expect ( "We must have at least one item" )
247
+ . object_meta
248
+ . size ;
249
+
250
+ // We try and find a file on either end that fits in the partition
251
+ if curr_partition_size + biggest_file_size >= target_partition_size {
252
+ all_files. pop_front ( )
253
+ } else if curr_partition_size + smallest_file_size >= target_partition_size {
254
+ all_files. pop_back ( )
255
+ } else {
256
+ None
257
+ }
258
+ } ;
259
+
260
+ // Add a file to the partition
261
+ if let Some ( file) = file {
262
+ curr_partition_size += file. object_meta . size ;
263
+ curr_partition. push ( file. clone ( ) ) ;
264
+ }
225
265
226
- if curr_partition_size >= target_partition_size {
266
+ // If the partition is full, move on to the next one
267
+ if curr_partition_size >= target_partition_size || file. is_none ( ) {
227
268
curr_partition_size = 0 ;
228
269
partitions. push ( std:: mem:: take ( & mut curr_partition) ) ;
229
270
}
@@ -232,19 +273,19 @@ fn repartition_by_size(
232
273
// If we we're still missing the last partition
233
274
if !curr_partition. is_empty ( ) && partitions. len ( ) != desired_partitions {
234
275
partitions. push ( std:: mem:: take ( & mut curr_partition) ) ;
235
- // If we already have enough partitions
236
276
} else if !curr_partition. is_empty ( ) {
237
277
for ( idx, file) in curr_partition. into_iter ( ) . enumerate ( ) {
238
278
let new_part_idx = idx % partitions. len ( ) ;
239
279
partitions[ new_part_idx] . push ( file. clone ( ) ) ;
240
280
}
241
281
}
242
282
283
+ for ( idx, file) in all_files. into_iter ( ) . enumerate ( ) {
284
+ let new_part_idx = idx % partitions. len ( ) ;
285
+ partitions[ new_part_idx] . push ( file. clone ( ) ) ;
286
+ }
287
+
243
288
// Assert that we have the correct number of partitions and that the total number of files is right
244
- assert_eq ! (
245
- partitions. len( ) ,
246
- usize :: min( desired_partitions, total_file_count)
247
- ) ;
248
289
assert_eq ! ( total_file_count, partitions. iter( ) . flatten( ) . count( ) ) ;
249
290
250
291
partitions
0 commit comments