1use std::any::Any;
18use std::sync::Arc;
19use std::sync::atomic::{AtomicBool, Ordering};
20use crate::{state::At, state::Position, storage::Cursor};
24
25#[derive(Clone, Copy, Debug)]
34pub struct ReadLimits {
35 pub rows: usize,
36 pub row_bytes: usize,
37 pub batch_bytes: usize,
38 pub batch_rows: usize,
39 pub segments: usize,
40 pub segment_bytes: usize,
41 pub decoded_bytes: usize,
42 pub pinned_bytes: usize,
43}
44
45#[derive(Clone, Copy, Debug)]
46pub struct PageLimits {
47 pub max_rows: usize,
48 pub max_encoded_bytes: usize,
49}
50
51#[derive(Debug)]
52pub struct RowPage<S> {
53 pub rows: Vec<At<Arc<S>>>,
54 pub encoded_bytes: usize,
55 pub complete: bool,
56}
57
58struct Ready<S> {
59 row: At<Arc<S>>,
60 encoded_bytes: usize,
61}
62
63struct Owned {
64 source: Box<dyn RowSource>,
65 _permit: tokio::sync::OwnedSemaphorePermit,
66}
67
68pub struct RowReader<S> {
69 owned: Option<Owned>,
70 cut: ReadCut,
71 limits: ReadLimits,
72 returned: usize,
73 done: bool,
74 partition: Option<super::Partition<S>>,
75 pending: Option<Ready<S>>,
76}
77
78#[derive(Clone, Copy, Debug, PartialEq, Eq)]
81pub enum ReadCut {
82 Window { delivered: Cursor },
83 Series { epoch: u64, delivered: Cursor, ordinal: u64 },
84}
85
86#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
87pub enum ReadError {
88 #[error("the active row reader budget is exhausted")]
89 Busy,
90
91 #[error("row read exceeds its {0} limit")]
92 Limit(&'static str),
93
94 #[error("the attached series adapter does not support bounded reads")]
95 Unsupported,
96
97 #[error("bounded row decoding does not support {0}")]
98 EncodingUnsupported(&'static str),
99
100 #[error("no series adapter is attached")]
101 Unserved,
102
103 #[error("row history is retained only above {floor:?}")]
104 Retained { floor: Position },
105
106 #[error("selected stream capture is incomplete or uncertified")]
107 IncompleteCapture,
108
109 #[error("row source is unreadable: {0}")]
110 Unreadable(String),
111
112 #[error("row reader is closed")]
113 Closed,
114}
115
116pub trait RowSource: Send {
119 fn cut(&self) -> ReadCut;
120
121 #[allow(clippy::type_complexity)]
122 fn next(&mut self, cancelled: &AtomicBool) -> Result<Option<At<Arc<dyn Any + Send + Sync>>>, ReadError>;
123}
124
125impl Default for ReadLimits {
128 fn default() -> Self {
129 Self {
130 rows: 100_000,
131 row_bytes: 1024 * 1024,
132 batch_bytes: 8 * 1024 * 1024,
133 batch_rows: 65_536,
134 segments: 256,
135 segment_bytes: 64 * 1024 * 1024,
136 decoded_bytes: 32 * 1024 * 1024,
137 pinned_bytes: 16 * 1024 * 1024,
138 }
139 }
140}
141
142impl Default for PageLimits {
143 fn default() -> Self {
144 Self { max_rows: 256, max_encoded_bytes: 256 * 1024 }
145 }
146}
147
148impl<S: serde::Serialize + Send + Sync + 'static> RowReader<S> {
149 pub fn cut(&self) -> ReadCut {
150 self.cut
151 }
152
153 pub async fn next_page(&mut self, page_limits: PageLimits) -> Result<RowPage<S>, ReadError> {
154 if page_limits.max_rows == 0 || page_limits.max_encoded_bytes == 0 {
155 return Err(ReadError::Limit("positive page limits"));
156 }
157 let Some(mut owned) = self.owned.take() else {
158 return if self.done { Ok(RowPage { rows: Vec::new(), encoded_bytes: 0, complete: true }) } else { Err(ReadError::Closed) };
159 };
160 let partition = self.partition.take();
161 let mut pending = self.pending.take();
162 let returned = self.returned;
163 let limits = self.limits;
164 struct Cancel(Arc<AtomicBool>);
165 impl Drop for Cancel {
166 fn drop(&mut self) {
167 self.0.store(true, Ordering::Release);
168 }
169 }
170 let cancelled = Arc::new(AtomicBool::new(false));
171 let _cancel = Cancel(cancelled.clone());
172 let result = tokio::task::spawn_blocking(move || {
173 let mut page = RowPage { rows: Vec::new(), encoded_bytes: 0usize, complete: false };
174 loop {
175 if cancelled.load(Ordering::Acquire) {
176 return Err(ReadError::Closed);
177 }
178 let next = match pending.take() {
179 Some(ready) => Some(ready),
180 None => loop {
181 if cancelled.load(Ordering::Acquire) {
182 return Err(ReadError::Closed);
183 }
184 let Some(row) = owned.source.next(&cancelled)? else {
185 break None;
186 };
187 let typed = super::row_value::<S>(row.value.as_ref())
188 .ok_or_else(|| ReadError::Unreadable("source row type mismatch".into()))?;
189 if partition.as_ref().is_some_and(|p| !(p.matches)(typed)) {
190 continue;
191 }
192 let encoded_bytes = encoded_size(typed, limits.row_bytes)?;
194 let row_value = if let Some(managed) = row.value.downcast_ref::<super::ManagedPublication>() {
195 Arc::new(managed.row().clone()) as Arc<dyn std::any::Any + Send + Sync>
196 } else {
197 row.value
198 };
199 let value = row_value.downcast::<S>().map_err(|_| ReadError::Unreadable("source row type mismatch".into()))?;
200 break Some(Ready { row: At { value, position: row.position }, encoded_bytes });
201 },
202 };
203 let Some(ready) = next else {
204 page.complete = true;
205 break;
206 };
207 if returned + page.rows.len() == limits.rows {
208 return Err(ReadError::Limit("rows"));
209 }
210 let bytes = page.encoded_bytes.checked_add(ready.encoded_bytes);
211 if page.rows.len() == page_limits.max_rows || bytes.is_none_or(|bytes| bytes > page_limits.max_encoded_bytes) {
212 if page.rows.is_empty() {
213 return Err(ReadError::Limit("page row bytes"));
214 }
215 pending = Some(ready);
219 break;
220 }
221 page.encoded_bytes = bytes.expect("within the checked page budget");
222 page.rows.push(ready.row);
223 }
224 Ok((owned, partition, pending, page))
225 })
226 .await
227 .map_err(|_| ReadError::Closed)?;
228 let (owned, partition, pending, page) = result?;
229 self.partition = partition;
230 self.returned += page.rows.len();
231 self.done = page.complete;
232 if !page.complete {
233 self.owned = Some(owned);
234 self.pending = pending;
235 }
236 Ok(page)
237 }
238}
239
240impl<S: Clone + serde::Serialize + Send + Sync + 'static> super::Query<S> {
241 pub async fn reader(self, limits: ReadLimits) -> Result<RowReader<S>, ReadError> {
242 if [
243 limits.rows,
244 limits.row_bytes,
245 limits.batch_bytes,
246 limits.batch_rows,
247 limits.segments,
248 limits.segment_bytes,
249 limits.decoded_bytes,
250 limits.pinned_bytes,
251 ]
252 .contains(&0)
253 {
254 return Err(ReadError::Limit("positive limits"));
255 }
256 let reserved = limits.row_bytes.checked_add(std::mem::size_of::<Ready<S>>()).ok_or(ReadError::Limit("pending row reservation"))?;
257 let mut source_limits = limits;
258 source_limits.pinned_bytes = limits.pinned_bytes.checked_sub(reserved).ok_or(ReadError::Limit("pending row reservation"))?;
259 let permit = self.tier.reader_permit()?;
260 let end = match self.as_of {
261 Some(cut) => match self.end {
262 std::ops::Bound::Unbounded => std::ops::Bound::Included(cut),
263 std::ops::Bound::Included(end) if end > cut => std::ops::Bound::Included(cut),
264 std::ops::Bound::Excluded(end) if end > cut => std::ops::Bound::Included(cut),
265 end => end,
266 },
267 None => self.end,
268 };
269 let label_bytes = self
270 .partition
271 .as_ref()
272 .map_or(Some(0usize), |partition| partition.labels.iter().try_fold(0usize, |total, label| total.checked_add(label.len() + 32)));
273 if label_bytes.is_none_or(|bytes| bytes > limits.pinned_bytes) {
274 return Err(ReadError::Limit("track bytes"));
275 }
276 let track = self.partition.as_ref().map(|p| p.labels.clone()).unwrap_or_default();
277 let opened = self.tier.reader(self.definition, track, self.start, end, source_limits, permit).await?;
278 let source = match opened.source {
279 Err(ReadError::Unserved) => self.window.reader::<S>(self.start, end, source_limits)?,
280 result => result?,
281 };
282 Ok(RowReader {
283 cut: source.cut(),
284 owned: Some(Owned { source, _permit: opened.permit }),
285 limits,
286 returned: 0,
287 done: false,
288 partition: self.partition,
289 pending: None,
290 })
291 }
292}
293
294pub(crate) fn encoded_size<S: serde::Serialize>(row: &S, limit: usize) -> Result<usize, ReadError> {
297 struct Count {
298 size: usize,
299 limit: usize,
300 }
301 impl std::io::Write for Count {
302 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
303 self.size = self
304 .size
305 .checked_add(bytes.len())
306 .filter(|size| *size <= self.limit)
307 .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::FileTooLarge, "row byte limit"))?;
308 Ok(bytes.len())
309 }
310 fn flush(&mut self) -> std::io::Result<()> {
311 Ok(())
312 }
313 }
314 let mut count = Count { size: 0, limit };
315 serde_json::to_writer(&mut count, row).map_err(|error| {
316 if error.io_error_kind() == Some(std::io::ErrorKind::FileTooLarge) {
317 ReadError::Limit("row bytes")
318 } else {
319 ReadError::Unreadable(error.to_string())
320 }
321 })?;
322 Ok(count.size)
323}
324
325