parquet/arrow/async_writer/
mod.rs1#[cfg(feature = "object_store")]
62mod store;
63#[cfg(feature = "object_store")]
64pub use store::*;
65
66use crate::{
67 arrow::ArrowWriter,
68 arrow::arrow_writer::ArrowWriterOptions,
69 errors::{ParquetError, Result},
70 file::{
71 metadata::{KeyValue, ParquetMetaData, RowGroupMetaData},
72 properties::WriterProperties,
73 },
74};
75use arrow_array::RecordBatch;
76use arrow_schema::SchemaRef;
77use bytes::Bytes;
78use futures::FutureExt;
79use futures::future::BoxFuture;
80use std::mem;
81use tokio::io::{AsyncWrite, AsyncWriteExt};
82
83pub trait AsyncFileWriter: Send {
85 fn write(&mut self, bs: Bytes) -> BoxFuture<'_, Result<()>>;
92
93 fn complete(&mut self) -> BoxFuture<'_, Result<()>>;
97}
98
99impl AsyncFileWriter for Box<dyn AsyncFileWriter + '_> {
100 fn write(&mut self, bs: Bytes) -> BoxFuture<'_, Result<()>> {
101 self.as_mut().write(bs)
102 }
103
104 fn complete(&mut self) -> BoxFuture<'_, Result<()>> {
105 self.as_mut().complete()
106 }
107}
108
109impl<T: AsyncWrite + Unpin + Send> AsyncFileWriter for T {
110 fn write(&mut self, bs: Bytes) -> BoxFuture<'_, Result<()>> {
111 async move {
112 self.write_all(&bs).await?;
113 Ok(())
114 }
115 .boxed()
116 }
117
118 fn complete(&mut self) -> BoxFuture<'_, Result<()>> {
119 async move {
120 self.flush().await?;
121 self.shutdown().await?;
122 Ok(())
123 }
124 .boxed()
125 }
126}
127
128pub struct AsyncArrowWriter<W> {
157 sync_writer: ArrowWriter<Vec<u8>>,
159
160 async_writer: W,
162}
163
164impl<W: AsyncFileWriter> AsyncArrowWriter<W> {
165 pub fn try_new(
167 writer: W,
168 arrow_schema: SchemaRef,
169 props: Option<WriterProperties>,
170 ) -> Result<Self> {
171 let options = ArrowWriterOptions::new().with_properties(props.unwrap_or_default());
172 Self::try_new_with_options(writer, arrow_schema, options)
173 }
174
175 pub fn try_new_with_options(
177 writer: W,
178 arrow_schema: SchemaRef,
179 options: ArrowWriterOptions,
180 ) -> Result<Self> {
181 let sync_writer = ArrowWriter::try_new_with_options(Vec::new(), arrow_schema, options)?;
182
183 Ok(Self {
184 sync_writer,
185 async_writer: writer,
186 })
187 }
188
189 pub fn flushed_row_groups(&self) -> &[RowGroupMetaData] {
191 self.sync_writer.flushed_row_groups()
192 }
193
194 pub fn memory_size(&self) -> usize {
198 self.sync_writer.memory_size()
199 }
200
201 pub fn in_progress_size(&self) -> usize {
205 self.sync_writer.in_progress_size()
206 }
207
208 pub fn in_progress_rows(&self) -> usize {
210 self.sync_writer.in_progress_rows()
211 }
212
213 pub fn bytes_written(&self) -> usize {
215 self.sync_writer.bytes_written()
216 }
217
218 pub async fn write(&mut self, batch: &RecordBatch) -> Result<()> {
223 let before = self.sync_writer.flushed_row_groups().len();
224 self.sync_writer.write(batch)?;
225 if before != self.sync_writer.flushed_row_groups().len() {
226 self.do_write().await?;
227 }
228 Ok(())
229 }
230
231 pub async fn flush(&mut self) -> Result<()> {
233 self.sync_writer.flush()?;
234 self.do_write().await?;
235
236 Ok(())
237 }
238
239 pub fn append_key_value_metadata(&mut self, kv_metadata: KeyValue) {
243 self.sync_writer.append_key_value_metadata(kv_metadata);
244 }
245
246 pub async fn finish(&mut self) -> Result<ParquetMetaData> {
254 let metadata = self.sync_writer.finish()?;
255
256 self.do_write().await?;
258 self.async_writer.complete().await?;
259
260 Ok(metadata)
261 }
262
263 pub async fn close(mut self) -> Result<ParquetMetaData> {
267 self.finish().await
268 }
269
270 pub fn into_inner(self) -> W {
277 self.async_writer
278 }
279
280 async fn do_write(&mut self) -> Result<()> {
287 let buffer = mem::take(self.sync_writer.inner_mut());
288
289 self.async_writer
290 .write(Bytes::from(buffer))
291 .await
292 .map_err(|e| ParquetError::External(Box::new(e)))?;
293
294 Ok(())
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use crate::arrow::arrow_reader::{ParquetRecordBatchReader, ParquetRecordBatchReaderBuilder};
301 use arrow::datatypes::{DataType, Field, Schema};
302 use arrow_array::{ArrayRef, BinaryArray, Int32Array, Int64Array, RecordBatchReader};
303 use bytes::Bytes;
304 use std::sync::Arc;
305
306 use super::*;
307
308 fn get_test_reader() -> ParquetRecordBatchReader {
309 let testdata = arrow::util::test_util::parquet_test_data();
310 let path = format!("{testdata}/alltypes_tiny_pages_plain.parquet");
312 let original_data = Bytes::from(std::fs::read(path).unwrap());
313 ParquetRecordBatchReaderBuilder::try_new(original_data)
314 .unwrap()
315 .build()
316 .unwrap()
317 }
318
319 #[tokio::test]
320 async fn test_async_writer() {
321 let col = Arc::new(Int64Array::from_iter_values([1, 2, 3])) as ArrayRef;
322 let to_write = RecordBatch::try_from_iter([("col", col)]).unwrap();
323
324 let mut buffer = Vec::new();
325 let mut writer = AsyncArrowWriter::try_new(&mut buffer, to_write.schema(), None).unwrap();
326 writer.write(&to_write).await.unwrap();
327 writer.close().await.unwrap();
328
329 let buffer = Bytes::from(buffer);
330 let mut reader = ParquetRecordBatchReaderBuilder::try_new(buffer)
331 .unwrap()
332 .build()
333 .unwrap();
334 let read = reader.next().unwrap().unwrap();
335
336 assert_eq!(to_write, read);
337 }
338
339 #[tokio::test]
342 async fn test_async_writer_with_sync_writer() {
343 let reader = get_test_reader();
344
345 let write_props = WriterProperties::builder()
346 .set_max_row_group_row_count(Some(64))
347 .build();
348
349 let mut async_buffer = Vec::new();
350 let mut async_writer = AsyncArrowWriter::try_new(
351 &mut async_buffer,
352 reader.schema(),
353 Some(write_props.clone()),
354 )
355 .unwrap();
356
357 let mut sync_buffer = Vec::new();
358 let mut sync_writer =
359 ArrowWriter::try_new(&mut sync_buffer, reader.schema(), Some(write_props)).unwrap();
360 for record_batch in reader {
361 let record_batch = record_batch.unwrap();
362 async_writer.write(&record_batch).await.unwrap();
363 sync_writer.write(&record_batch).unwrap();
364 }
365 sync_writer.close().unwrap();
366 async_writer.close().await.unwrap();
367
368 assert_eq!(sync_buffer, async_buffer);
369 }
370
371 #[tokio::test]
372 async fn test_async_writer_bytes_written() {
373 let col = Arc::new(Int64Array::from_iter_values([1, 2, 3])) as ArrayRef;
374 let to_write = RecordBatch::try_from_iter([("col", col)]).unwrap();
375
376 let temp = tempfile::tempfile().unwrap();
377
378 let file = tokio::fs::File::from_std(temp.try_clone().unwrap());
379 let mut writer =
380 AsyncArrowWriter::try_new(file.try_clone().await.unwrap(), to_write.schema(), None)
381 .unwrap();
382 writer.write(&to_write).await.unwrap();
383 let _metadata = writer.finish().await.unwrap();
384 let reported = writer.bytes_written();
386
387 let actual = file.metadata().await.unwrap().len() as usize;
389
390 assert_eq!(reported, actual);
391 }
392
393 #[tokio::test]
394 async fn test_async_writer_file() {
395 let col = Arc::new(Int64Array::from_iter_values([1, 2, 3])) as ArrayRef;
396 let col2 = Arc::new(BinaryArray::from_iter_values(vec![
397 vec![0; 500000],
398 vec![0; 500000],
399 vec![0; 500000],
400 ])) as ArrayRef;
401 let to_write = RecordBatch::try_from_iter([("col", col), ("col2", col2)]).unwrap();
402
403 let temp = tempfile::tempfile().unwrap();
404
405 let file = tokio::fs::File::from_std(temp.try_clone().unwrap());
406 let mut writer = AsyncArrowWriter::try_new(file, to_write.schema(), None).unwrap();
407 writer.write(&to_write).await.unwrap();
408 writer.close().await.unwrap();
409
410 let mut reader = ParquetRecordBatchReaderBuilder::try_new(temp)
411 .unwrap()
412 .build()
413 .unwrap();
414 let read = reader.next().unwrap().unwrap();
415
416 assert_eq!(to_write, read);
417 }
418
419 #[tokio::test]
420 async fn in_progress_accounting() {
421 let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]);
423
424 let a = Int32Array::from_value(0_i32, 512);
426
427 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a)]).unwrap();
429
430 let temp = tempfile::tempfile().unwrap();
431 let file = tokio::fs::File::from_std(temp.try_clone().unwrap());
432 let mut writer = AsyncArrowWriter::try_new(file, batch.schema(), None).unwrap();
433
434 assert_eq!(writer.in_progress_size(), 0);
436 assert_eq!(writer.in_progress_rows(), 0);
437 assert_eq!(writer.bytes_written(), 4); writer.write(&batch).await.unwrap();
439
440 let initial_size = writer.in_progress_size();
442 assert!(initial_size > 0);
443 assert_eq!(writer.in_progress_rows(), batch.num_rows());
444 let initial_memory = writer.memory_size();
445 assert!(
447 initial_size <= initial_memory,
448 "{initial_size} <= {initial_memory}"
449 );
450
451 writer.write(&batch).await.unwrap();
453 assert!(writer.in_progress_size() > initial_size);
454 assert_eq!(writer.in_progress_rows(), batch.num_rows() * 2);
455 assert!(writer.memory_size() > initial_memory);
456 assert!(
457 writer.in_progress_size() <= writer.memory_size(),
458 "in_progress_size {} <= memory_size {}",
459 writer.in_progress_size(),
460 writer.memory_size()
461 );
462
463 let pre_flush_bytes_written = writer.bytes_written();
465 writer.flush().await.unwrap();
466 assert_eq!(writer.in_progress_size(), 0);
467 assert_eq!(writer.memory_size(), 0);
468 assert_eq!(writer.in_progress_rows(), 0);
469 assert!(writer.bytes_written() > pre_flush_bytes_written);
470
471 writer.close().await.unwrap();
472 }
473}