vortex_file/segments/
source.rs1use std::pin::Pin;
5use std::sync::Arc;
6use std::sync::atomic::AtomicUsize;
7use std::sync::atomic::Ordering;
8use std::task::Context;
9use std::task::Poll;
10
11use futures::FutureExt;
12use futures::StreamExt;
13use futures::channel::mpsc;
14use futures::future;
15use vortex_array::buffer::BufferHandle;
16use vortex_buffer::Alignment;
17use vortex_buffer::ByteBuffer;
18use vortex_error::VortexResult;
19use vortex_error::vortex_bail;
20use vortex_error::vortex_err;
21use vortex_error::vortex_panic;
22use vortex_io::VortexReadAt;
23use vortex_io::runtime::Handle;
24use vortex_layout::segments::SegmentFuture;
25use vortex_layout::segments::SegmentId;
26use vortex_layout::segments::SegmentSource;
27use vortex_metrics::Counter;
28use vortex_metrics::Histogram;
29use vortex_metrics::Label;
30use vortex_metrics::MetricBuilder;
31use vortex_metrics::MetricsRegistry;
32
33use crate::SegmentSpec;
34use crate::read::IoRequestStream;
35use crate::read::ReadRequest;
36use crate::read::RequestId;
37
38#[derive(Debug)]
39pub enum ReadEvent {
41 Request(ReadRequest),
43 Polled(RequestId),
45 Dropped(RequestId),
47}
48
49pub struct FileSegmentSource {
71 segments: Arc<[SegmentSpec]>,
72 events: mpsc::UnboundedSender<ReadEvent>,
74 next_id: Arc<AtomicUsize>,
76}
77
78impl FileSegmentSource {
79 pub fn open<R: VortexReadAt + Clone>(
84 segments: Arc<[SegmentSpec]>,
85 reader: R,
86 handle: Handle,
87 metrics: RequestMetrics,
88 ) -> Self {
89 let (send, recv) = mpsc::unbounded();
90
91 let max_alignment = segments
92 .iter()
93 .map(|segment| segment.alignment)
94 .max()
95 .unwrap_or_else(Alignment::none);
96 let coalesce_config = reader.coalesce_config().map(|mut config| {
97 let extra = (*max_alignment as u64).saturating_sub(1);
100 config.max_size = config.max_size.saturating_add(extra);
101 config
102 });
103 let concurrency = reader.concurrency();
104 if concurrency == 0 {
105 vortex_panic!(
106 "VortexReadAt::concurrency returned 0 (uri={:?}); this would stall I/O",
107 reader.uri()
108 );
109 }
110
111 let stream = IoRequestStream::new(
112 StreamExt::boxed(recv),
113 coalesce_config,
114 max_alignment,
115 metrics,
116 )
117 .boxed();
118
119 let drive_fut = async move {
120 stream
121 .map(move |req| {
122 let reader = reader.clone();
123 async move {
124 let result = reader
125 .read_at(req.offset(), req.len(), req.alignment())
126 .await;
127 let result = result.and_then(|buffer| {
128 if req.len() != buffer.len() {
129 vortex_bail!(
130 "FileSegmentSource: expected buffer of length {} but received {}. {:?}",
131 req.len(),
132 buffer.len(),
133 req
134 )
135 }
136 Ok(buffer)
137 });
138
139 req.resolve(result);
140 }
141 })
142 .buffer_unordered(concurrency)
143 .collect::<()>()
144 .await
145 };
146
147 handle.spawn(drive_fut).detach();
148
149 Self {
150 segments,
151 events: send,
152 next_id: Arc::new(AtomicUsize::new(0)),
153 }
154 }
155}
156
157impl SegmentSource for FileSegmentSource {
158 fn request(&self, id: SegmentId) -> SegmentFuture {
159 let spec = *match self.segments.get(*id as usize) {
162 Some(spec) => spec,
163 None => {
164 return future::ready(Err(vortex_err!("Missing segment: {}", id))).boxed();
165 }
166 };
167
168 let SegmentSpec {
169 offset,
170 length,
171 alignment,
172 } = spec;
173
174 let (send, recv) = oneshot::channel();
175 let id = self.next_id.fetch_add(1, Ordering::Relaxed);
176 let event = ReadEvent::Request(ReadRequest {
177 id,
178 offset,
179 length: length as usize,
180 alignment,
181 callback: send,
182 });
183
184 if let Err(e) = self.events.unbounded_send(event) {
186 return future::ready(Err(vortex_err!("Failed to submit read request: {e}"))).boxed();
187 }
188
189 let fut = ReadFuture {
190 id,
191 recv: recv.into_future(),
192 polled: false,
193 finished: false,
194 events: self.events.clone(),
195 };
196
197 fut.boxed()
199 }
200}
201
202struct ReadFuture {
207 id: usize,
208 recv: oneshot::AsyncReceiver<VortexResult<BufferHandle>>,
209 polled: bool,
210 finished: bool,
211 events: mpsc::UnboundedSender<ReadEvent>,
212}
213
214impl Future for ReadFuture {
215 type Output = VortexResult<BufferHandle>;
216
217 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
218 match self.recv.poll_unpin(cx) {
219 Poll::Ready(result) => {
220 self.finished = true;
221 Poll::Ready(
225 result.unwrap_or_else(|e| {
226 Err(vortex_err!("ReadRequest dropped by runtime: {e}"))
227 }),
228 )
229 }
230 Poll::Pending if !self.polled => {
231 self.polled = true;
232 match self.events.unbounded_send(ReadEvent::Polled(self.id)) {
234 Ok(()) => Poll::Pending,
235 Err(e) => Poll::Ready(Err(vortex_err!("ReadRequest dropped by runtime: {e}"))),
236 }
237 }
238 _ => Poll::Pending,
239 }
240 }
241}
242
243impl Drop for ReadFuture {
244 fn drop(&mut self) {
245 if self.finished {
247 return;
248 }
249
250 drop(self.events.unbounded_send(ReadEvent::Dropped(self.id)));
252 }
253}
254
255pub struct RequestMetrics {
257 pub individual_requests: Counter,
259 pub coalesced_requests: Counter,
261 pub num_requests_coalesced: Histogram,
263}
264
265impl RequestMetrics {
266 pub fn new(metrics_registry: &dyn MetricsRegistry, labels: Vec<Label>) -> Self {
268 Self {
269 individual_requests: MetricBuilder::new(metrics_registry)
270 .add_labels(labels.clone())
271 .counter("io.requests.individual"),
272 coalesced_requests: MetricBuilder::new(metrics_registry)
273 .add_labels(labels.clone())
274 .counter("io.requests.coalesced"),
275 num_requests_coalesced: MetricBuilder::new(metrics_registry)
276 .add_labels(labels)
277 .histogram("io.requests.coalesced.num_coalesced"),
278 }
279 }
280}
281
282pub(crate) struct BufferSegmentSource {
287 buffer: ByteBuffer,
288 segments: Arc<[SegmentSpec]>,
289}
290
291impl BufferSegmentSource {
292 pub fn new(buffer: ByteBuffer, segments: Arc<[SegmentSpec]>) -> Self {
294 Self { buffer, segments }
295 }
296}
297
298impl SegmentSource for BufferSegmentSource {
299 fn request(&self, id: SegmentId) -> SegmentFuture {
300 let spec = match self.segments.get(*id as usize) {
301 Some(spec) => spec,
302 None => {
303 return future::ready(Err(vortex_err!("Missing segment: {}", id))).boxed();
304 }
305 };
306
307 let start = spec.offset as usize;
308 let end = start + spec.length as usize;
309 if end > self.buffer.len() {
310 return future::ready(Err(vortex_err!(
311 "Segment {} range {}..{} out of bounds for buffer of length {}",
312 *id,
313 start,
314 end,
315 self.buffer.len()
316 )))
317 .boxed();
318 }
319
320 let slice = self.buffer.slice(start..end).aligned(spec.alignment);
321 future::ready(Ok(BufferHandle::new_host(slice))).boxed()
322 }
323}