parquet/arrow/async_writer/
mod.rs1#[cfg(feature = "object_store")]
49mod store;
50#[cfg(feature = "object_store")]
51pub use store::*;
52
53use crate::{
54 arrow::ArrowWriter,
55 arrow::arrow_writer::ArrowWriterOptions,
56 errors::{ParquetError, Result},
57 file::{
58 metadata::{KeyValue, ParquetMetaData, RowGroupMetaData},
59 properties::WriterProperties,
60 },
61};
62use arrow_array::RecordBatch;
63use arrow_schema::SchemaRef;
64use bytes::Bytes;
65use futures::FutureExt;
66use futures::future::BoxFuture;
67use std::mem;
68use tokio::io::{AsyncWrite, AsyncWriteExt};
69
70pub trait AsyncFileWriter: Send {
72 fn write(&mut self, bs: Bytes) -> BoxFuture<'_, Result<()>>;
79
80 fn complete(&mut self) -> BoxFuture<'_, Result<()>>;
84}
85
86impl AsyncFileWriter for Box<dyn AsyncFileWriter + '_> {
87 fn write(&mut self, bs: Bytes) -> BoxFuture<'_, Result<()>> {
88 self.as_mut().write(bs)
89 }
90
91 fn complete(&mut self) -> BoxFuture<'_, Result<()>> {
92 self.as_mut().complete()
93 }
94}
95
96impl<T: AsyncWrite + Unpin + Send> AsyncFileWriter for T {
97 fn write(&mut self, bs: Bytes) -> BoxFuture<'_, Result<()>> {
98 async move {
99 self.write_all(&bs).await?;
100 Ok(())
101 }
102 .boxed()
103 }
104
105 fn complete(&mut self) -> BoxFuture<'_, Result<()>> {
106 async move {
107 self.flush().await?;
108 self.shutdown().await?;
109 Ok(())
110 }
111 .boxed()
112 }
113}
114
115pub struct AsyncArrowWriter<W> {
144 sync_writer: ArrowWriter<Vec<u8>>,
146
147 async_writer: W,
149}
150
151impl<W: AsyncFileWriter> AsyncArrowWriter<W> {
152 pub fn try_new(
154 writer: W,
155 arrow_schema: SchemaRef,
156 props: Option<WriterProperties>,
157 ) -> Result<Self> {
158 let options = ArrowWriterOptions::new().with_properties(props.unwrap_or_default());
159 Self::try_new_with_options(writer, arrow_schema, options)
160 }
161
162 pub fn try_new_with_options(
164 writer: W,
165 arrow_schema: SchemaRef,
166 options: ArrowWriterOptions,
167 ) -> Result<Self> {
168 let sync_writer = ArrowWriter::try_new_with_options(Vec::new(), arrow_schema, options)?;
169
170 Ok(Self {
171 sync_writer,
172 async_writer: writer,
173 })
174 }
175
176 pub fn flushed_row_groups(&self) -> &[RowGroupMetaData] {
178 self.sync_writer.flushed_row_groups()
179 }
180
181 pub fn memory_size(&self) -> usize {
185 self.sync_writer.memory_size()
186 }
187
188 pub fn in_progress_size(&self) -> usize {
192 self.sync_writer.in_progress_size()
193 }
194
195 pub fn in_progress_rows(&self) -> usize {
197 self.sync_writer.in_progress_rows()
198 }
199
200 pub fn bytes_written(&self) -> usize {
202 self.sync_writer.bytes_written()
203 }
204
205 pub async fn write(&mut self, batch: &RecordBatch) -> Result<()> {
210 let before = self.sync_writer.flushed_row_groups().len();
211 self.sync_writer.write(batch)?;
212 if before != self.sync_writer.flushed_row_groups().len() {
213 self.do_write().await?;
214 }
215 Ok(())
216 }
217
218 pub async fn flush(&mut self) -> Result<()> {
220 self.sync_writer.flush()?;
221 self.do_write().await?;
222
223 Ok(())
224 }
225
226 pub fn append_key_value_metadata(&mut self, kv_metadata: KeyValue) {
230 self.sync_writer.append_key_value_metadata(kv_metadata);
231 }
232
233 pub async fn finish(&mut self) -> Result<ParquetMetaData> {
241 let metadata = self.sync_writer.finish()?;
242
243 self.do_write().await?;
245 self.async_writer.complete().await?;
246
247 Ok(metadata)
248 }
249
250 pub async fn close(mut self) -> Result<ParquetMetaData> {
254 self.finish().await
255 }
256
257 pub fn into_inner(self) -> W {
264 self.async_writer
265 }
266
267 async fn do_write(&mut self) -> Result<()> {
274 let buffer = mem::take(self.sync_writer.inner_mut());
275
276 self.async_writer
277 .write(Bytes::from(buffer))
278 .await
279 .map_err(|e| ParquetError::External(Box::new(e)))?;
280
281 Ok(())
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use crate::arrow::arrow_reader::{ParquetRecordBatchReader, ParquetRecordBatchReaderBuilder};
288 use arrow::datatypes::{DataType, Field, Schema};
289 use arrow_array::{ArrayRef, BinaryArray, Int32Array, Int64Array, RecordBatchReader};
290 use bytes::Bytes;
291 use std::sync::Arc;
292
293 use super::*;
294
295 fn get_test_reader() -> ParquetRecordBatchReader {
296 let testdata = arrow::util::test_util::parquet_test_data();
297 let path = format!("{testdata}/alltypes_tiny_pages_plain.parquet");
299 let original_data = Bytes::from(std::fs::read(path).unwrap());
300 ParquetRecordBatchReaderBuilder::try_new(original_data)
301 .unwrap()
302 .build()
303 .unwrap()
304 }
305
306 #[tokio::test]
307 async fn test_async_writer() {
308 let col = Arc::new(Int64Array::from_iter_values([1, 2, 3])) as ArrayRef;
309 let to_write = RecordBatch::try_from_iter([("col", col)]).unwrap();
310
311 let mut buffer = Vec::new();
312 let mut writer = AsyncArrowWriter::try_new(&mut buffer, to_write.schema(), None).unwrap();
313 writer.write(&to_write).await.unwrap();
314 writer.close().await.unwrap();
315
316 let buffer = Bytes::from(buffer);
317 let mut reader = ParquetRecordBatchReaderBuilder::try_new(buffer)
318 .unwrap()
319 .build()
320 .unwrap();
321 let read = reader.next().unwrap().unwrap();
322
323 assert_eq!(to_write, read);
324 }
325
326 #[tokio::test]
329 async fn test_async_writer_with_sync_writer() {
330 let reader = get_test_reader();
331
332 let write_props = WriterProperties::builder()
333 .set_max_row_group_row_count(Some(64))
334 .build();
335
336 let mut async_buffer = Vec::new();
337 let mut async_writer = AsyncArrowWriter::try_new(
338 &mut async_buffer,
339 reader.schema(),
340 Some(write_props.clone()),
341 )
342 .unwrap();
343
344 let mut sync_buffer = Vec::new();
345 let mut sync_writer =
346 ArrowWriter::try_new(&mut sync_buffer, reader.schema(), Some(write_props)).unwrap();
347 for record_batch in reader {
348 let record_batch = record_batch.unwrap();
349 async_writer.write(&record_batch).await.unwrap();
350 sync_writer.write(&record_batch).unwrap();
351 }
352 sync_writer.close().unwrap();
353 async_writer.close().await.unwrap();
354
355 assert_eq!(sync_buffer, async_buffer);
356 }
357
358 #[tokio::test]
359 async fn test_async_writer_bytes_written() {
360 let col = Arc::new(Int64Array::from_iter_values([1, 2, 3])) as ArrayRef;
361 let to_write = RecordBatch::try_from_iter([("col", col)]).unwrap();
362
363 let temp = tempfile::tempfile().unwrap();
364
365 let file = tokio::fs::File::from_std(temp.try_clone().unwrap());
366 let mut writer =
367 AsyncArrowWriter::try_new(file.try_clone().await.unwrap(), to_write.schema(), None)
368 .unwrap();
369 writer.write(&to_write).await.unwrap();
370 let _metadata = writer.finish().await.unwrap();
371 let reported = writer.bytes_written();
373
374 let actual = file.metadata().await.unwrap().len() as usize;
376
377 assert_eq!(reported, actual);
378 }
379
380 #[tokio::test]
381 async fn test_async_writer_file() {
382 let col = Arc::new(Int64Array::from_iter_values([1, 2, 3])) as ArrayRef;
383 let col2 = Arc::new(BinaryArray::from_iter_values(vec![
384 vec![0; 500000],
385 vec![0; 500000],
386 vec![0; 500000],
387 ])) as ArrayRef;
388 let to_write = RecordBatch::try_from_iter([("col", col), ("col2", col2)]).unwrap();
389
390 let temp = tempfile::tempfile().unwrap();
391
392 let file = tokio::fs::File::from_std(temp.try_clone().unwrap());
393 let mut writer = AsyncArrowWriter::try_new(file, to_write.schema(), None).unwrap();
394 writer.write(&to_write).await.unwrap();
395 writer.close().await.unwrap();
396
397 let mut reader = ParquetRecordBatchReaderBuilder::try_new(temp)
398 .unwrap()
399 .build()
400 .unwrap();
401 let read = reader.next().unwrap().unwrap();
402
403 assert_eq!(to_write, read);
404 }
405
406 #[tokio::test]
407 async fn in_progress_accounting() {
408 let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]);
410
411 let a = Int32Array::from_value(0_i32, 512);
413
414 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a)]).unwrap();
416
417 let temp = tempfile::tempfile().unwrap();
418 let file = tokio::fs::File::from_std(temp.try_clone().unwrap());
419 let mut writer = AsyncArrowWriter::try_new(file, batch.schema(), None).unwrap();
420
421 assert_eq!(writer.in_progress_size(), 0);
423 assert_eq!(writer.in_progress_rows(), 0);
424 assert_eq!(writer.bytes_written(), 4); writer.write(&batch).await.unwrap();
426
427 let initial_size = writer.in_progress_size();
429 assert!(initial_size > 0);
430 assert_eq!(writer.in_progress_rows(), batch.num_rows());
431 let initial_memory = writer.memory_size();
432 assert!(
434 initial_size <= initial_memory,
435 "{initial_size} <= {initial_memory}"
436 );
437
438 writer.write(&batch).await.unwrap();
440 assert!(writer.in_progress_size() > initial_size);
441 assert_eq!(writer.in_progress_rows(), batch.num_rows() * 2);
442 assert!(writer.memory_size() > initial_memory);
443 assert!(
444 writer.in_progress_size() <= writer.memory_size(),
445 "in_progress_size {} <= memory_size {}",
446 writer.in_progress_size(),
447 writer.memory_size()
448 );
449
450 let pre_flush_bytes_written = writer.bytes_written();
452 writer.flush().await.unwrap();
453 assert_eq!(writer.in_progress_size(), 0);
454 assert_eq!(writer.memory_size(), 0);
455 assert_eq!(writer.in_progress_rows(), 0);
456 assert!(writer.bytes_written() > pre_flush_bytes_written);
457
458 writer.close().await.unwrap();
459 }
460}