1use std::io;
5use std::io::Write;
6use std::sync::Arc;
7use std::sync::atomic::AtomicU64;
8
9use futures::FutureExt;
10use futures::StreamExt;
11use futures::TryStreamExt;
12use futures::future::Fuse;
13use futures::future::LocalBoxFuture;
14use futures::future::ready;
15use futures::pin_mut;
16use futures::select;
17use itertools::Itertools;
18use vortex_array::ArrayContext;
19use vortex_array::ArrayRef;
20use vortex_array::dtype::DType;
21use vortex_array::expr::stats::Stat;
22use vortex_array::iter::ArrayIterator;
23use vortex_array::iter::ArrayIteratorExt;
24use vortex_array::session::ArraySessionExt;
25use vortex_array::stats::PRUNING_STATS;
26use vortex_array::stream::ArrayStream;
27use vortex_array::stream::ArrayStreamAdapter;
28use vortex_array::stream::ArrayStreamExt;
29use vortex_array::stream::SendableArrayStream;
30use vortex_buffer::ByteBuffer;
31use vortex_error::VortexError;
32use vortex_error::VortexExpect;
33use vortex_error::VortexResult;
34use vortex_error::vortex_bail;
35use vortex_error::vortex_err;
36use vortex_io::IoBuf;
37use vortex_io::VortexWrite;
38use vortex_io::kanal_ext::KanalExt;
39use vortex_io::runtime::BlockingRuntime;
40use vortex_io::session::RuntimeSessionExt;
41use vortex_layout::LayoutStrategy;
42use vortex_layout::layouts::file_stats::accumulate_stats;
43use vortex_layout::sequence::SequenceId;
44use vortex_layout::sequence::SequentialStreamAdapter;
45use vortex_layout::sequence::SequentialStreamExt;
46use vortex_session::SessionExt;
47use vortex_session::VortexSession;
48use vortex_session::registry::ReadContext;
49
50use crate::Footer;
51use crate::MAGIC_BYTES;
52use crate::WriteStrategyBuilder;
53use crate::counting::CountingVortexWrite;
54use crate::footer::FileStatistics;
55use crate::segments::writer::BufferedSegmentSink;
56
57pub struct VortexWriteOptions {
63 session: VortexSession,
64 strategy: Arc<dyn LayoutStrategy>,
65 exclude_dtype: bool,
66 max_variable_length_statistics_size: usize,
67 file_statistics: Vec<Stat>,
68}
69
70pub trait WriteOptionsSessionExt: SessionExt {
71 fn write_options(&self) -> VortexWriteOptions {
73 let session = self.session();
74 VortexWriteOptions {
75 strategy: WriteStrategyBuilder::default().build(),
76 session,
77 exclude_dtype: false,
78 file_statistics: PRUNING_STATS.to_vec(),
79 max_variable_length_statistics_size: 64,
80 }
81 }
82}
83impl<S: SessionExt> WriteOptionsSessionExt for S {}
84
85impl VortexWriteOptions {
86 pub fn new(session: VortexSession) -> Self {
88 VortexWriteOptions {
89 strategy: WriteStrategyBuilder::default().build(),
90 session,
91 exclude_dtype: false,
92 file_statistics: PRUNING_STATS.to_vec(),
93 max_variable_length_statistics_size: 64,
94 }
95 }
96
97 pub fn with_strategy(mut self, strategy: Arc<dyn LayoutStrategy>) -> Self {
99 self.strategy = strategy;
100 self
101 }
102
103 pub fn exclude_dtype(mut self) -> Self {
107 self.exclude_dtype = true;
108 self
109 }
110
111 pub fn with_file_statistics(mut self, file_statistics: Vec<Stat>) -> Self {
113 self.file_statistics = file_statistics;
114 self
115 }
116}
117
118impl VortexWriteOptions {
119 pub fn blocking<B: BlockingRuntime>(self, runtime: &B) -> BlockingWrite<'_, B> {
121 BlockingWrite {
122 options: self,
123 runtime,
124 }
125 }
126
127 pub async fn write<W: VortexWrite + Unpin, S: ArrayStream + Send + 'static>(
132 self,
133 write: W,
134 stream: S,
135 ) -> VortexResult<WriteSummary> {
136 self.write_internal(write, ArrayStreamExt::boxed(stream))
137 .await
138 }
139
140 async fn write_internal<W: VortexWrite + Unpin>(
141 self,
142 mut write: W,
143 stream: SendableArrayStream,
144 ) -> VortexResult<WriteSummary> {
145 let ctx = ArrayContext::new(self.session.arrays().registry().ids().sorted().collect())
151 .with_registry(self.session.arrays().registry().clone());
153 let dtype = stream.dtype().clone();
154
155 let (mut ptr, eof) = SequenceId::root().split();
156
157 let stream = SequentialStreamAdapter::new(
158 dtype.clone(),
159 stream
160 .try_filter(|chunk| ready(!chunk.is_empty()))
161 .map(move |result| result.map(|chunk| (ptr.advance(), chunk))),
162 )
163 .sendable();
164 let (file_stats, stream) = accumulate_stats(
165 stream,
166 self.file_statistics.clone().into(),
167 self.max_variable_length_statistics_size,
168 );
169
170 write.write_all(ByteBuffer::copy_from(MAGIC_BYTES)).await?;
172 let mut position = MAGIC_BYTES.len() as u64;
173
174 let (send, recv) = kanal::bounded_async(1);
176
177 let segments = Arc::new(BufferedSegmentSink::new(send, position));
178
179 let ctx2 = ctx.clone();
182 let session = self.session.clone();
183 let layout_fut = self.session.handle().spawn_nested(move |h| async move {
184 let session = session.with_handle(h);
185 let layout = self
186 .strategy
187 .write_stream(
188 ctx2,
189 Arc::<BufferedSegmentSink>::clone(&segments),
190 stream,
191 eof,
192 &session,
193 )
194 .await?;
195 Ok::<_, VortexError>((layout, segments.segment_specs()))
196 });
197
198 let recv_stream = recv.into_stream();
200 pin_mut!(recv_stream);
201 while let Some(buffer) = recv_stream.next().await {
202 if buffer.is_empty() {
203 continue;
204 }
205 position += buffer.len() as u64;
206 write.write_all(buffer).await?;
207 }
208
209 let (layout, segment_specs) = layout_fut.await?;
210
211 let mut footer = Footer::new(
213 Arc::clone(&layout),
214 segment_specs,
215 if self.file_statistics.is_empty() {
216 None
217 } else {
218 Some(FileStatistics::new_with_dtype(
219 file_stats.stats_sets().into(),
220 &dtype,
221 ))
222 },
223 ReadContext::new(ctx.to_ids()),
224 );
225
226 let footer_buffers = footer
228 .clone()
229 .into_serializer()
230 .with_offset(position)
231 .with_exclude_dtype(self.exclude_dtype)
232 .serialize()?;
233
234 footer = footer.with_approx_byte_size(footer_buffers.iter().map(|b| b.len()).sum());
237
238 for buffer in footer_buffers {
239 position += buffer.len() as u64;
240 write.write_all(buffer).await?;
241 }
242
243 write.flush().await?;
244
245 Ok(WriteSummary {
246 footer,
247 size: position,
248 })
249 }
250
251 pub fn writer<'w, W: VortexWrite + Unpin + 'w>(self, write: W, dtype: DType) -> Writer<'w> {
253 let (arrays_send, arrays_recv) = kanal::bounded_async(1);
255
256 let arrays =
257 ArrayStreamExt::boxed(ArrayStreamAdapter::new(dtype, arrays_recv.into_stream()));
258
259 let write = CountingVortexWrite::new(write);
260 let bytes_written = write.counter();
261 let strategy = Arc::clone(&self.strategy);
262 let future = self.write(write, arrays).boxed_local().fuse();
263
264 Writer {
265 arrays: Some(arrays_send),
266 future,
267 bytes_written,
268 strategy,
269 }
270 }
271}
272
273pub struct Writer<'w> {
275 arrays: Option<kanal::AsyncSender<VortexResult<ArrayRef>>>,
277 future: Fuse<LocalBoxFuture<'w, VortexResult<WriteSummary>>>,
279 bytes_written: Arc<AtomicU64>,
281 strategy: Arc<dyn LayoutStrategy>,
283}
284
285impl Writer<'_> {
286 pub async fn push(&mut self, chunk: ArrayRef) -> VortexResult<()> {
288 let arrays = self.arrays.clone().vortex_expect("missing arrays sender");
289 let send_fut = async move { arrays.send(Ok(chunk)).await }.fuse();
290 pin_mut!(send_fut);
291
292 select! {
295 result = send_fut => {
296 if result.is_err() {
298 return Err(self.handle_failed_task().await);
299 }
300 },
301 result = &mut self.future => {
302 match result {
306 Ok(_) => vortex_bail!("Internal error: writer future completed early"),
307 Err(e) => return Err(e),
308 }
309 }
310 }
311
312 Ok(())
313 }
314
315 pub async fn push_stream(&mut self, mut stream: SendableArrayStream) -> VortexResult<()> {
320 let arrays = self.arrays.clone().vortex_expect("missing arrays sender");
321 let stream_fut = async move {
322 while let Some(chunk) = stream.next().await {
323 arrays.send(chunk).await?;
324 }
325 Ok::<_, kanal::SendError>(())
326 }
327 .fuse();
328 pin_mut!(stream_fut);
329
330 select! {
333 result = stream_fut => {
334 if let Err(_send_err) = result {
335 return Err(self.handle_failed_task().await);
337 }
338 }
339
340 result = &mut self.future => {
341 match result {
345 Ok(_) => vortex_bail!("Internal error: writer future completed early"),
346 Err(e) => return Err(e),
347 }
348 }
349 }
350
351 Ok(())
352 }
353
354 pub fn bytes_written(&self) -> u64 {
356 self.bytes_written
357 .load(std::sync::atomic::Ordering::Relaxed)
358 }
359
360 pub fn buffered_bytes(&self) -> u64 {
362 self.strategy.buffered_bytes()
363 }
364
365 pub async fn finish(mut self) -> VortexResult<WriteSummary> {
368 drop(self.arrays.take());
370
371 self.future.await
373 }
374
375 async fn handle_failed_task(&mut self) -> VortexError {
377 match (&mut self.future).await {
378 Ok(_) => vortex_err!(
379 "Internal error: writer task completed successfully but write future finished early"
380 ),
381 Err(e) => e,
382 }
383 }
384}
385
386pub struct BlockingWrite<'rt, B: BlockingRuntime> {
388 options: VortexWriteOptions,
389 runtime: &'rt B,
390}
391
392impl<'rt, B: BlockingRuntime> BlockingWrite<'rt, B> {
393 pub fn write<W: Write + Unpin>(
395 self,
396 write: W,
397 iter: impl ArrayIterator + Send + 'static,
398 ) -> VortexResult<WriteSummary> {
399 self.runtime.block_on(async move {
400 self.options
401 .write(BlockingWriteAdapter(write), iter.into_array_stream())
402 .await
403 })
404 }
405
406 pub fn writer<'w, W: Write + Unpin + 'w>(
407 self,
408 write: W,
409 dtype: DType,
410 ) -> BlockingWriter<'rt, 'w, B> {
411 BlockingWriter {
412 writer: self.options.writer(BlockingWriteAdapter(write), dtype),
413 runtime: self.runtime,
414 }
415 }
416}
417
418pub struct BlockingWriter<'rt, 'w, B: BlockingRuntime> {
420 runtime: &'rt B,
421 writer: Writer<'w>,
422}
423
424impl<B: BlockingRuntime> BlockingWriter<'_, '_, B> {
425 pub fn push(&mut self, chunk: ArrayRef) -> VortexResult<()> {
426 self.runtime.block_on(self.writer.push(chunk))
427 }
428
429 pub fn bytes_written(&self) -> u64 {
430 self.writer.bytes_written()
431 }
432
433 pub fn buffered_bytes(&self) -> u64 {
434 self.writer.buffered_bytes()
435 }
436
437 pub fn finish(self) -> VortexResult<WriteSummary> {
438 self.runtime.block_on(self.writer.finish())
439 }
440}
441
442struct BlockingWriteAdapter<W>(W);
444
445impl<W: Write + Unpin> VortexWrite for BlockingWriteAdapter<W> {
446 async fn write_all<B: IoBuf>(&mut self, buffer: B) -> io::Result<B> {
447 self.0.write_all(buffer.as_slice())?;
448 Ok(buffer)
449 }
450
451 fn flush(&mut self) -> impl Future<Output = io::Result<()>> {
452 ready(self.0.flush())
453 }
454
455 fn shutdown(&mut self) -> impl Future<Output = io::Result<()>> {
456 ready(Ok(()))
457 }
458}
459
460pub struct WriteSummary {
461 footer: Footer,
462 size: u64,
463 }
465
466impl WriteSummary {
467 pub fn footer(&self) -> &Footer {
469 &self.footer
470 }
471
472 pub fn size(&self) -> u64 {
474 self.size
475 }
476
477 pub fn row_count(&self) -> u64 {
479 self.footer.row_count()
480 }
481}