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