1use 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 {
432 use super::*;
433 use arrow::array::{Float64Array, Int64Array, StringArray};
434 use arrow::datatypes::{DataType, Field, Schema};
435 use std::sync::Arc;
436
437 #[derive(Clone, Debug)]
439 struct TestEvent {
440 id: i64,
441 value: f64,
442 timestamp: i64,
443 }
444
445 impl Record for TestEvent {
446 fn schema() -> SchemaRef {
447 Arc::new(Schema::new(vec![
448 Field::new("id", DataType::Int64, false),
449 Field::new("value", DataType::Float64, false),
450 Field::new("timestamp", DataType::Int64, false),
451 ]))
452 }
453
454 fn to_record_batch(&self) -> RecordBatch {
455 RecordBatch::try_new(
456 Self::schema(),
457 vec![
458 Arc::new(Int64Array::from(vec![self.id])),
459 Arc::new(Float64Array::from(vec![self.value])),
460 Arc::new(Int64Array::from(vec![self.timestamp])),
461 ],
462 )
463 .unwrap()
464 }
465
466 fn event_time(&self) -> Option<i64> {
467 Some(self.timestamp)
468 }
469 }
470
471 #[tokio::test]
472 async fn test_create_source_sink() {
473 let (source, _sink) = create::<TestEvent>(1024);
474
475 assert!(!source.is_closed());
476 assert_eq!(source.pending(), 0);
477 }
478
479 #[tokio::test]
480 async fn test_push_single() {
481 let (source, _sink) = create::<TestEvent>(16);
482
483 let event = TestEvent {
484 id: 1,
485 value: 42.0,
486 timestamp: 1000,
487 };
488
489 assert!(source.push(event).is_ok());
490 assert_eq!(source.pending(), 1);
491 }
492
493 #[tokio::test]
494 async fn test_try_push() {
495 let (source, _sink) = create::<TestEvent>(16);
496
497 let event = TestEvent {
498 id: 1,
499 value: 42.0,
500 timestamp: 1000,
501 };
502
503 assert!(source.try_push(event).is_ok());
504 }
505
506 #[tokio::test]
507 async fn test_push_batch() {
508 let (source, _sink) = create::<TestEvent>(16);
509
510 let events = vec![
511 TestEvent {
512 id: 1,
513 value: 1.0,
514 timestamp: 1000,
515 },
516 TestEvent {
517 id: 2,
518 value: 2.0,
519 timestamp: 2000,
520 },
521 TestEvent {
522 id: 3,
523 value: 3.0,
524 timestamp: 3000,
525 },
526 ];
527
528 let count = source.push_batch(&events);
529 assert_eq!(count, 3);
530 assert_eq!(source.pending(), 3);
531 }
532
533 #[tokio::test]
534 async fn test_push_arrow() {
535 let (source, _sink) = create::<TestEvent>(16);
536
537 let batch = RecordBatch::try_new(
538 TestEvent::schema(),
539 vec![
540 Arc::new(Int64Array::from(vec![1, 2, 3])),
541 Arc::new(Float64Array::from(vec![1.0, 2.0, 3.0])),
542 Arc::new(Int64Array::from(vec![1000, 2000, 3000])),
543 ],
544 )
545 .unwrap();
546
547 assert!(source.push_arrow(batch).is_ok());
548 }
549
550 #[tokio::test]
551 async fn test_push_arrow_schema_mismatch() {
552 let (source, _sink) = create::<TestEvent>(16);
553
554 let wrong_schema = Arc::new(Schema::new(vec![Field::new(
556 "wrong",
557 DataType::Utf8,
558 false,
559 )]));
560
561 let batch = RecordBatch::try_new(
562 wrong_schema,
563 vec![Arc::new(StringArray::from(vec!["test"]))],
564 )
565 .unwrap();
566
567 let result = source.push_arrow(batch);
568 assert!(matches!(result, Err(StreamingError::SchemaMismatch { .. })));
569 }
570
571 #[tokio::test]
572 async fn test_watermark() {
573 let (source, _sink) = create::<TestEvent>(16);
574
575 assert_eq!(source.current_watermark(), i64::MIN);
576
577 source.watermark(1000);
578 assert_eq!(source.current_watermark(), 1000);
579
580 source.watermark(2000);
581 assert_eq!(source.current_watermark(), 2000);
582
583 source.watermark(1500);
585 assert_eq!(source.current_watermark(), 2000);
586 }
587
588 #[tokio::test]
589 async fn recovery_restore_lowers_shared_watermark_then_runtime_advances() {
590 let (source, _sink) = create::<TestEvent>(16);
591 let clone = source.clone();
592 source.watermark(2_000);
593
594 source.restore_watermark_for_recovery(500);
595 assert_eq!(source.current_watermark(), 500);
596 assert_eq!(clone.current_watermark(), 500);
597
598 clone.watermark(600);
599 assert_eq!(source.current_watermark(), 600);
600 clone.watermark(550);
601 assert_eq!(source.current_watermark(), 600);
602 }
603
604 #[tokio::test]
605 async fn test_watermark_from_event_time() {
606 let (source, _sink) = create::<TestEvent>(16);
607
608 let event = TestEvent {
609 id: 1,
610 value: 42.0,
611 timestamp: 5000,
612 };
613
614 source.push(event).unwrap();
615
616 assert_eq!(source.current_watermark(), 5000);
618 }
619
620 #[tokio::test]
621 async fn test_clone_multi_producer() {
622 let (source, sink) = create::<TestEvent>(16);
623 let source2 = source.clone();
624 let mut sub = sink.subscribe(); source
627 .push(TestEvent {
628 id: 1,
629 value: 1.0,
630 timestamp: 1000,
631 })
632 .unwrap();
633 source2
634 .push(TestEvent {
635 id: 2,
636 value: 2.0,
637 timestamp: 2000,
638 })
639 .unwrap();
640
641 tokio::time::sleep(std::time::Duration::from_millis(10)).await;
642 assert!(sub.poll().is_some());
643 assert!(sub.poll().is_some());
644 }
645
646 #[tokio::test]
647 async fn test_schema() {
648 let (source, _sink) = create::<TestEvent>(16);
649
650 let schema = source.schema();
651 assert_eq!(schema.fields().len(), 3);
652 assert_eq!(schema.field(0).name(), "id");
653 assert_eq!(schema.field(1).name(), "value");
654 assert_eq!(schema.field(2).name(), "timestamp");
655 }
656
657 #[tokio::test]
658 async fn test_named_source() {
659 let config = SourceConfig::named("my_source");
660 let (source, _sink) = create_with_config::<TestEvent>(config);
661
662 assert_eq!(source.name(), Some("my_source"));
663 }
664
665 #[tokio::test]
666 async fn test_debug_format() {
667 let (source, _sink) = create::<TestEvent>(16);
668
669 let debug = format!("{source:?}");
670 assert!(debug.contains("Source"));
671 }
672
673 #[tokio::test]
674 async fn test_set_event_time_column() {
675 let (source, _sink) = create::<TestEvent>(16);
676
677 assert!(source.event_time_column().is_none());
678
679 source.set_event_time_column("timestamp");
680 assert_eq!(source.event_time_column(), Some("timestamp".to_string()));
681 }
682
683 #[tokio::test]
684 async fn test_event_time_column_preserved_on_clone() {
685 let (source, _sink) = create::<TestEvent>(16);
686 source.set_event_time_column("ts");
687
688 let source2 = source.clone();
689 assert_eq!(source2.event_time_column(), Some("ts".to_string()));
690 }
691}