laminar_core/streaming/source/
mod.rs1use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
4use std::sync::{Arc, OnceLock};
5use std::time::Duration;
6
7use arrow::array::RecordBatch;
8use arrow::datatypes::SchemaRef;
9
10use super::channel::{channel_with_config, Producer};
11use super::config::SourceConfig;
12use super::error::{StreamingError, TryPushError};
13use super::sink::Sink;
14
15pub trait Record: Clone + Send + Sized + 'static {
17 fn schema() -> SchemaRef;
19
20 fn to_record_batch(&self) -> RecordBatch;
24
25 fn event_time(&self) -> Option<i64> {
30 None
31 }
32
33 fn to_record_batch_from_iter<I>(records: I) -> RecordBatch
38 where
39 I: IntoIterator<Item = Self>,
40 {
41 let batches: Vec<RecordBatch> = records.into_iter().map(|r| r.to_record_batch()).collect();
42 if batches.is_empty() {
43 return RecordBatch::new_empty(Self::schema());
44 }
45 arrow::compute::concat_batches(&Self::schema(), &batches)
46 .unwrap_or_else(|_| RecordBatch::new_empty(Self::schema()))
47 }
48}
49
50#[derive(Clone)]
52pub(crate) enum SourceMessage<T> {
53 Record(T),
55
56 Batch(RecordBatch),
58}
59
60struct SourceWatermark {
62 current: Arc<AtomicI64>,
66}
67
68impl SourceWatermark {
69 fn new() -> Self {
70 Self {
71 current: Arc::new(AtomicI64::new(i64::MIN)),
72 }
73 }
74
75 fn from_arc(arc: Arc<AtomicI64>) -> Self {
76 Self { current: arc }
77 }
78
79 fn update(&self, timestamp: i64) {
80 let mut current = self.current.load(Ordering::Acquire);
82 while timestamp > current {
83 match self.current.compare_exchange_weak(
84 current,
85 timestamp,
86 Ordering::AcqRel,
87 Ordering::Acquire,
88 ) {
89 Ok(_) => break,
90 Err(actual) => current = actual,
91 }
92 }
93 }
94
95 fn restore_for_recovery(&self, timestamp: i64) {
96 self.current.store(timestamp, Ordering::Release);
97 }
98
99 fn get(&self) -> i64 {
100 self.current.load(Ordering::Acquire)
101 }
102
103 fn arc(&self) -> Arc<AtomicI64> {
104 Arc::clone(&self.current)
105 }
106}
107
108struct SourceInner<T: Record> {
110 producer: Producer<SourceMessage<T>>,
112
113 watermark: SourceWatermark,
115
116 schema: SchemaRef,
118
119 name: Option<String>,
121
122 sequence: Arc<AtomicU64>,
125
126 event_time_column: OnceLock<String>,
129
130 max_out_of_orderness: OnceLock<Duration>,
133}
134
135pub struct Source<T: Record> {
137 inner: Arc<SourceInner<T>>,
138}
139
140impl<T: Record> Source<T> {
141 pub(crate) fn new(config: SourceConfig) -> (Self, Sink<T>) {
143 let channel_config = config.channel;
144 let (producer, consumer) = channel_with_config::<SourceMessage<T>>(&channel_config);
145
146 let schema = T::schema();
147
148 let inner = Arc::new(SourceInner {
149 producer,
150 watermark: SourceWatermark::new(),
151 schema: schema.clone(),
152 name: config.name,
153 sequence: Arc::new(AtomicU64::new(0)),
154 event_time_column: OnceLock::new(),
155 max_out_of_orderness: OnceLock::new(),
156 });
157
158 let source = Self { inner };
159 let sink = Sink::new(consumer, schema);
160
161 (source, sink)
162 }
163
164 pub fn push(&self, record: T) -> Result<(), StreamingError> {
170 if let Some(event_time) = record.event_time() {
171 self.inner.watermark.update(event_time);
172 }
173
174 self.inner
175 .producer
176 .push(SourceMessage::Record(record))
177 .map_err(|_| StreamingError::ChannelFull)?;
178
179 self.inner.sequence.fetch_add(1, Ordering::Relaxed);
180 Ok(())
181 }
182
183 pub fn try_push(&self, record: T) -> Result<(), TryPushError<T>> {
189 if let Some(event_time) = record.event_time() {
190 self.inner.watermark.update(event_time);
191 }
192
193 self.inner
194 .producer
195 .push(SourceMessage::Record(record))
196 .map_err(|msg| match msg {
197 SourceMessage::Record(r) => TryPushError {
198 value: r,
199 error: StreamingError::ChannelFull,
200 },
201 SourceMessage::Batch(_) => unreachable!("only Record is pushed here"),
202 })?;
203
204 self.inner.sequence.fetch_add(1, Ordering::Relaxed);
205 Ok(())
206 }
207
208 pub fn push_batch(&self, records: &[T]) -> usize
210 where
211 T: Clone,
212 {
213 self.push_batch_drain(records.iter().cloned())
214 }
215
216 pub fn push_batch_drain<I>(&self, records: I) -> usize
219 where
220 I: IntoIterator<Item = T>,
221 {
222 let mut count = 0;
223 for record in records {
224 if self.push(record).is_err() {
225 break;
226 }
227 count += 1;
228 }
229 count
230 }
231
232 pub fn push_arrow(&self, batch: RecordBatch) -> Result<(), StreamingError> {
242 if !self.inner.schema.fields().is_empty() && batch.schema() != self.inner.schema {
244 return Err(StreamingError::SchemaMismatch {
245 expected: self
246 .inner
247 .schema
248 .fields()
249 .iter()
250 .map(|f| f.name().clone())
251 .collect(),
252 actual: batch
253 .schema()
254 .fields()
255 .iter()
256 .map(|f| f.name().clone())
257 .collect(),
258 });
259 }
260
261 self.inner
262 .producer
263 .push(SourceMessage::Batch(batch))
264 .map_err(|_| StreamingError::ChannelFull)?;
265
266 self.inner.sequence.fetch_add(1, Ordering::Relaxed);
267 Ok(())
268 }
269
270 pub fn watermark(&self, timestamp: i64) {
279 self.inner.watermark.update(timestamp);
284 }
285
286 pub fn restore_watermark_for_recovery(&self, timestamp: i64) {
293 self.inner.watermark.restore_for_recovery(timestamp);
294 }
295
296 #[must_use]
298 pub fn current_watermark(&self) -> i64 {
299 self.inner.watermark.get()
300 }
301
302 #[must_use]
304 pub fn schema(&self) -> SchemaRef {
305 Arc::clone(&self.inner.schema)
306 }
307
308 #[must_use]
310 pub fn name(&self) -> Option<&str> {
311 self.inner.name.as_deref()
312 }
313
314 #[must_use]
316 pub fn is_closed(&self) -> bool {
317 self.inner.producer.is_closed()
318 }
319
320 #[must_use]
322 pub fn pending(&self) -> usize {
323 self.inner.producer.len()
324 }
325
326 #[must_use]
328 pub fn capacity(&self) -> usize {
329 self.inner.producer.capacity()
330 }
331
332 #[must_use]
334 pub fn sequence(&self) -> u64 {
335 self.inner.sequence.load(Ordering::Acquire)
336 }
337
338 #[must_use]
340 pub fn sequence_counter(&self) -> Arc<AtomicU64> {
341 Arc::clone(&self.inner.sequence)
342 }
343
344 #[must_use]
346 pub fn watermark_atomic(&self) -> Arc<AtomicI64> {
347 self.inner.watermark.arc()
348 }
349
350 pub fn set_event_time_column(&self, column: &str) {
357 let _ = self.inner.event_time_column.set(column.to_owned());
358 }
359
360 #[must_use]
362 pub fn event_time_column(&self) -> Option<String> {
363 self.inner.event_time_column.get().cloned()
364 }
365
366 pub fn set_max_out_of_orderness(&self, dur: Duration) {
370 let _ = self.inner.max_out_of_orderness.set(dur);
371 }
372
373 #[must_use]
375 pub fn max_out_of_orderness(&self) -> Option<Duration> {
376 self.inner.max_out_of_orderness.get().copied()
377 }
378}
379
380impl<T: Record> Clone for Source<T> {
381 fn clone(&self) -> Self {
382 let producer = self.inner.producer.clone();
383 let event_time_col = self.inner.event_time_column.get().cloned();
384 let event_time_column = OnceLock::new();
385 if let Some(col) = event_time_col {
386 let _ = event_time_column.set(col);
387 }
388 let max_ooo = self.inner.max_out_of_orderness.get().copied();
389 let max_out_of_orderness = OnceLock::new();
390 if let Some(dur) = max_ooo {
391 let _ = max_out_of_orderness.set(dur);
392 }
393 Self {
394 inner: Arc::new(SourceInner {
395 producer,
396 watermark: SourceWatermark::from_arc(self.inner.watermark.arc()),
397 schema: Arc::clone(&self.inner.schema),
398 name: self.inner.name.clone(),
399 sequence: Arc::clone(&self.inner.sequence),
400 event_time_column,
401 max_out_of_orderness,
402 }),
403 }
404 }
405}
406
407impl<T: Record + std::fmt::Debug> std::fmt::Debug for Source<T> {
408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409 f.debug_struct("Source")
410 .field("name", &self.inner.name)
411 .field("pending", &self.pending())
412 .field("capacity", &self.capacity())
413 .field("watermark", &self.current_watermark())
414 .finish()
415 }
416}
417
418#[must_use]
420pub fn create<T: Record>(buffer_size: usize) -> (Source<T>, Sink<T>) {
421 Source::new(SourceConfig::with_buffer_size(buffer_size))
422}
423
424#[must_use]
426pub fn create_with_config<T: Record>(config: SourceConfig) -> (Source<T>, Sink<T>) {
427 Source::new(config)
428}
429
430#[cfg(test)]
431mod tests;