@@ -72,8 +72,8 @@ use object_store::path::Path;
72
72
use object_store:: { ObjectMeta , ObjectStore } ;
73
73
use parquet:: arrow:: arrow_reader:: statistics:: StatisticsConverter ;
74
74
use parquet:: arrow:: arrow_writer:: {
75
- compute_leaves, get_column_writers , ArrowColumnChunk , ArrowColumnWriter ,
76
- ArrowLeafColumn , ArrowWriterOptions ,
75
+ compute_leaves, ArrowColumnChunk , ArrowColumnWriter , ArrowLeafColumn ,
76
+ ArrowRowGroupWriterFactory , ArrowWriterOptions ,
77
77
} ;
78
78
use parquet:: arrow:: async_reader:: MetadataFetch ;
79
79
use parquet:: arrow:: { parquet_to_arrow_schema, ArrowSchemaConverter , AsyncArrowWriter } ;
@@ -1306,14 +1306,6 @@ impl FileSink for ParquetSink {
1306
1306
object_store : Arc < dyn ObjectStore > ,
1307
1307
) -> Result < u64 > {
1308
1308
let parquet_opts = & self . parquet_options ;
1309
- let mut allow_single_file_parallelism =
1310
- parquet_opts. global . allow_single_file_parallelism ;
1311
-
1312
- if parquet_opts. crypto . file_encryption . is_some ( ) {
1313
- // For now, arrow-rs does not support parallel writes with encryption
1314
- // See https://github.com/apache/arrow-rs/issues/7359
1315
- allow_single_file_parallelism = false ;
1316
- }
1317
1309
1318
1310
let mut file_write_tasks: JoinSet <
1319
1311
std:: result:: Result < ( Path , FileMetaData ) , DataFusionError > ,
@@ -1330,7 +1322,7 @@ impl FileSink for ParquetSink {
1330
1322
} ;
1331
1323
1332
1324
while let Some ( ( path, mut rx) ) = file_stream_rx. recv ( ) . await {
1333
- if !allow_single_file_parallelism {
1325
+ if !parquet_opts . global . allow_single_file_parallelism {
1334
1326
let mut writer = self
1335
1327
. create_async_arrow_writer (
1336
1328
& path,
@@ -1458,13 +1450,13 @@ type ColSender = Sender<ArrowLeafColumn>;
1458
1450
/// Returns join handles for each columns serialization task along with a send channel
1459
1451
/// to send arrow arrays to each serialization task.
1460
1452
fn spawn_column_parallel_row_group_writer (
1461
- schema : Arc < Schema > ,
1462
- parquet_props : Arc < WriterProperties > ,
1453
+ arrow_row_group_writer_factory : Arc < ArrowRowGroupWriterFactory > ,
1463
1454
max_buffer_size : usize ,
1464
1455
pool : & Arc < dyn MemoryPool > ,
1465
1456
) -> Result < ( Vec < ColumnWriterTask > , Vec < ColSender > ) > {
1466
- let schema_desc = ArrowSchemaConverter :: new ( ) . convert ( & schema) ?;
1467
- let col_writers = get_column_writers ( & schema_desc, & parquet_props, & schema) ?;
1457
+ let arrow_row_group_writer =
1458
+ arrow_row_group_writer_factory. create_row_group_writer ( 0 ) ?;
1459
+ let col_writers = arrow_row_group_writer. into_column_writers ( ) ;
1468
1460
let num_columns = col_writers. len ( ) ;
1469
1461
1470
1462
let mut col_writer_tasks = Vec :: with_capacity ( num_columns) ;
@@ -1559,6 +1551,7 @@ fn spawn_rg_join_and_finalize_task(
1559
1551
/// across both columns and row_groups, with a theoretical max number of parallel tasks
1560
1552
/// given by n_columns * num_row_groups.
1561
1553
fn spawn_parquet_parallel_serialization_task (
1554
+ arrow_row_group_writer_factory : Arc < ArrowRowGroupWriterFactory > ,
1562
1555
mut data : Receiver < RecordBatch > ,
1563
1556
serialize_tx : Sender < SpawnedTask < RBStreamSerializeResult > > ,
1564
1557
schema : Arc < Schema > ,
@@ -1571,12 +1564,14 @@ fn spawn_parquet_parallel_serialization_task(
1571
1564
let max_row_group_rows = writer_props. max_row_group_size ( ) ;
1572
1565
let ( mut column_writer_handles, mut col_array_channels) =
1573
1566
spawn_column_parallel_row_group_writer (
1574
- Arc :: clone ( & schema) ,
1575
- Arc :: clone ( & writer_props) ,
1567
+ arrow_row_group_writer_factory. clone ( ) ,
1576
1568
max_buffer_rb,
1577
1569
& pool,
1578
1570
) ?;
1579
1571
let mut current_rg_rows = 0 ;
1572
+ // TODO: row_group_writer should use the correct row group index. Currently this would fail if
1573
+ // multiple row groups were written.
1574
+ // let mut rg_index = 0;
1580
1575
1581
1576
while let Some ( mut rb) = data. recv ( ) . await {
1582
1577
// This loop allows the "else" block to repeatedly split the RecordBatch to handle the case
@@ -1623,8 +1618,7 @@ fn spawn_parquet_parallel_serialization_task(
1623
1618
1624
1619
( column_writer_handles, col_array_channels) =
1625
1620
spawn_column_parallel_row_group_writer (
1626
- Arc :: clone ( & schema) ,
1627
- Arc :: clone ( & writer_props) ,
1621
+ arrow_row_group_writer_factory. clone ( ) ,
1628
1622
max_buffer_rb,
1629
1623
& pool,
1630
1624
) ?;
@@ -1655,24 +1649,15 @@ fn spawn_parquet_parallel_serialization_task(
1655
1649
/// Consume RowGroups serialized by other parallel tasks and concatenate them in
1656
1650
/// to the final parquet file, while flushing finalized bytes to an [ObjectStore]
1657
1651
async fn concatenate_parallel_row_groups (
1652
+ mut parquet_writer : SerializedFileWriter < SharedBuffer > ,
1653
+ merged_buff : SharedBuffer ,
1658
1654
mut serialize_rx : Receiver < SpawnedTask < RBStreamSerializeResult > > ,
1659
- schema : Arc < Schema > ,
1660
- writer_props : Arc < WriterProperties > ,
1661
1655
mut object_store_writer : Box < dyn AsyncWrite + Send + Unpin > ,
1662
1656
pool : Arc < dyn MemoryPool > ,
1663
1657
) -> Result < FileMetaData > {
1664
- let merged_buff = SharedBuffer :: new ( INITIAL_BUFFER_BYTES ) ;
1665
-
1666
1658
let mut file_reservation =
1667
1659
MemoryConsumer :: new ( "ParquetSink(SerializedFileWriter)" ) . register ( & pool) ;
1668
1660
1669
- let schema_desc = ArrowSchemaConverter :: new ( ) . convert ( schema. as_ref ( ) ) ?;
1670
- let mut parquet_writer = SerializedFileWriter :: new (
1671
- merged_buff. clone ( ) ,
1672
- schema_desc. root_schema_ptr ( ) ,
1673
- writer_props,
1674
- ) ?;
1675
-
1676
1661
while let Some ( task) = serialize_rx. recv ( ) . await {
1677
1662
let result = task. join_unwind ( ) . await ;
1678
1663
let mut rg_out = parquet_writer. next_row_group ( ) ?;
@@ -1723,28 +1708,47 @@ async fn output_single_parquet_file_parallelized(
1723
1708
let ( serialize_tx, serialize_rx) =
1724
1709
mpsc:: channel :: < SpawnedTask < RBStreamSerializeResult > > ( max_rowgroups) ;
1725
1710
1711
+ let parquet_schema = ArrowSchemaConverter :: new ( )
1712
+ . with_coerce_types ( parquet_props. coerce_types ( ) )
1713
+ . convert ( & output_schema) ?;
1714
+ let merged_buff = SharedBuffer :: new ( INITIAL_BUFFER_BYTES ) ;
1715
+ let parquet_writer = SerializedFileWriter :: new (
1716
+ merged_buff. clone ( ) ,
1717
+ parquet_schema. root_schema_ptr ( ) ,
1718
+ parquet_props. clone ( ) . into ( ) ,
1719
+ ) ?;
1720
+ let arrow_row_group_writer_factory = ArrowRowGroupWriterFactory :: new (
1721
+ & parquet_writer,
1722
+ parquet_schema,
1723
+ output_schema. clone ( ) ,
1724
+ parquet_props. clone ( ) . into ( ) ,
1725
+ ) ;
1726
+
1726
1727
let arc_props = Arc :: new ( parquet_props. clone ( ) ) ;
1727
1728
let launch_serialization_task = spawn_parquet_parallel_serialization_task (
1729
+ Arc :: new ( arrow_row_group_writer_factory) ,
1728
1730
data,
1729
1731
serialize_tx,
1730
1732
Arc :: clone ( & output_schema) ,
1731
1733
Arc :: clone ( & arc_props) ,
1732
1734
parallel_options,
1733
1735
Arc :: clone ( & pool) ,
1734
1736
) ;
1737
+
1738
+ launch_serialization_task
1739
+ . join_unwind ( )
1740
+ . await
1741
+ . map_err ( |e| DataFusionError :: ExecutionJoin ( Box :: new ( e) ) ) ??;
1742
+
1735
1743
let file_metadata = concatenate_parallel_row_groups (
1744
+ parquet_writer,
1745
+ merged_buff,
1736
1746
serialize_rx,
1737
- Arc :: clone ( & output_schema) ,
1738
- Arc :: clone ( & arc_props) ,
1739
1747
object_store_writer,
1740
1748
pool,
1741
1749
)
1742
1750
. await ?;
1743
1751
1744
- launch_serialization_task
1745
- . join_unwind ( )
1746
- . await
1747
- . map_err ( |e| DataFusionError :: ExecutionJoin ( Box :: new ( e) ) ) ??;
1748
1752
Ok ( file_metadata)
1749
1753
}
1750
1754
0 commit comments