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 {
40 Request(ReadRequest),
41 Polled(RequestId),
42 Dropped(RequestId),
43}
44
45pub struct FileSegmentSource {
67 segments: Arc<[SegmentSpec]>,
68 events: mpsc::UnboundedSender<ReadEvent>,
70 next_id: Arc<AtomicUsize>,
72}
73
74impl FileSegmentSource {
75 pub fn open<R: VortexReadAt + Clone>(
76 segments: Arc<[SegmentSpec]>,
77 reader: R,
78 handle: Handle,
79 metrics: RequestMetrics,
80 ) -> Self {
81 let (send, recv) = mpsc::unbounded();
82
83 let max_alignment = segments
84 .iter()
85 .map(|segment| segment.alignment)
86 .max()
87 .unwrap_or_else(Alignment::none);
88 let coalesce_config = reader.coalesce_config().map(|mut config| {
89 let extra = (*max_alignment as u64).saturating_sub(1);
92 config.max_size = config.max_size.saturating_add(extra);
93 config
94 });
95 let concurrency = reader.concurrency();
96 if concurrency == 0 {
97 vortex_panic!(
98 "VortexReadAt::concurrency returned 0 (uri={:?}); this would stall I/O",
99 reader.uri()
100 );
101 }
102
103 let stream = IoRequestStream::new(
104 StreamExt::boxed(recv),
105 coalesce_config,
106 max_alignment,
107 metrics,
108 )
109 .boxed();
110
111 let drive_fut = async move {
112 stream
113 .map(move |req| {
114 let reader = reader.clone();
115 async move {
116 let result = reader
117 .read_at(req.offset(), req.len(), req.alignment())
118 .await;
119 let result = result.and_then(|buffer| {
120 if req.len() != buffer.len() {
121 vortex_bail!(
122 "FileSegmentSource: expected buffer of length {} but received {}. {:?}",
123 req.len(),
124 buffer.len(),
125 req
126 )
127 }
128 Ok(buffer)
129 });
130
131 req.resolve(result);
132 }
133 })
134 .buffer_unordered(concurrency)
135 .collect::<()>()
136 .await
137 };
138
139 handle.spawn(drive_fut).detach();
140
141 Self {
142 segments,
143 events: send,
144 next_id: Arc::new(AtomicUsize::new(0)),
145 }
146 }
147}
148
149impl SegmentSource for FileSegmentSource {
150 fn request(&self, id: SegmentId) -> SegmentFuture {
151 let spec = *match self.segments.get(*id as usize) {
154 Some(spec) => spec,
155 None => {
156 return future::ready(Err(vortex_err!("Missing segment: {}", id))).boxed();
157 }
158 };
159
160 let SegmentSpec {
161 offset,
162 length,
163 alignment,
164 } = spec;
165
166 let (send, recv) = oneshot::channel();
167 let id = self.next_id.fetch_add(1, Ordering::Relaxed);
168 let event = ReadEvent::Request(ReadRequest {
169 id,
170 offset,
171 length: length as usize,
172 alignment,
173 callback: send,
174 });
175
176 if let Err(e) = self.events.unbounded_send(event) {
178 return future::ready(Err(vortex_err!("Failed to submit read request: {e}"))).boxed();
179 }
180
181 let fut = ReadFuture {
182 id,
183 recv: recv.into_future(),
184 polled: false,
185 finished: false,
186 events: self.events.clone(),
187 };
188
189 fut.boxed()
191 }
192}
193
194struct ReadFuture {
199 id: usize,
200 recv: oneshot::AsyncReceiver<VortexResult<BufferHandle>>,
201 polled: bool,
202 finished: bool,
203 events: mpsc::UnboundedSender<ReadEvent>,
204}
205
206impl Future for ReadFuture {
207 type Output = VortexResult<BufferHandle>;
208
209 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
210 match self.recv.poll_unpin(cx) {
211 Poll::Ready(result) => {
212 self.finished = true;
213 Poll::Ready(
217 result.unwrap_or_else(|e| {
218 Err(vortex_err!("ReadRequest dropped by runtime: {e}"))
219 }),
220 )
221 }
222 Poll::Pending if !self.polled => {
223 self.polled = true;
224 match self.events.unbounded_send(ReadEvent::Polled(self.id)) {
226 Ok(()) => Poll::Pending,
227 Err(e) => Poll::Ready(Err(vortex_err!("ReadRequest dropped by runtime: {e}"))),
228 }
229 }
230 _ => Poll::Pending,
231 }
232 }
233}
234
235impl Drop for ReadFuture {
236 fn drop(&mut self) {
237 if self.finished {
239 return;
240 }
241
242 drop(self.events.unbounded_send(ReadEvent::Dropped(self.id)));
244 }
245}
246
247pub struct RequestMetrics {
248 pub individual_requests: Counter,
249 pub coalesced_requests: Counter,
250 pub num_requests_coalesced: Histogram,
251}
252
253impl RequestMetrics {
254 pub fn new(metrics_registry: &dyn MetricsRegistry, labels: Vec<Label>) -> Self {
255 Self {
256 individual_requests: MetricBuilder::new(metrics_registry)
257 .add_labels(labels.clone())
258 .counter("io.requests.individual"),
259 coalesced_requests: MetricBuilder::new(metrics_registry)
260 .add_labels(labels.clone())
261 .counter("io.requests.coalesced"),
262 num_requests_coalesced: MetricBuilder::new(metrics_registry)
263 .add_labels(labels)
264 .histogram("io.requests.coalesced.num_coalesced"),
265 }
266 }
267}
268
269pub(crate) struct BufferSegmentSource {
274 buffer: ByteBuffer,
275 segments: Arc<[SegmentSpec]>,
276}
277
278impl BufferSegmentSource {
279 pub fn new(buffer: ByteBuffer, segments: Arc<[SegmentSpec]>) -> Self {
281 Self { buffer, segments }
282 }
283}
284
285impl SegmentSource for BufferSegmentSource {
286 fn request(&self, id: SegmentId) -> SegmentFuture {
287 let spec = match self.segments.get(*id as usize) {
288 Some(spec) => spec,
289 None => {
290 return future::ready(Err(vortex_err!("Missing segment: {}", id))).boxed();
291 }
292 };
293
294 let start = spec.offset as usize;
295 let end = start + spec.length as usize;
296 if end > self.buffer.len() {
297 return future::ready(Err(vortex_err!(
298 "Segment {} range {}..{} out of bounds for buffer of length {}",
299 *id,
300 start,
301 end,
302 self.buffer.len()
303 )))
304 .boxed();
305 }
306
307 let slice = self.buffer.slice(start..end).aligned(spec.alignment);
308 future::ready(Ok(BufferHandle::new_host(slice))).boxed()
309 }
310}