1use std::{
13 any::Any,
14 cell::RefCell,
15 marker::PhantomData,
16 rc::Rc,
17 sync::{Arc, Mutex},
18};
19
20use bytes::Bytes;
21use krabka_units::prelude::*;
22
23use crate::{
24 dsl::{
25 builder::InternalStreamsBuilder,
26 config::{Grouped, Joined, Materialized, StreamJoined},
27 graph::{GraphNodeKind, LowerState, NodeId},
28 kgrouped::KGroupedStream,
29 ktable::KTable,
30 names,
31 processors::{
32 change::Change, global_join::KStreamGlobalTableJoinProcessor,
33 join::KStreamKTableJoinProcessor, ktable_join::JoinKind, outer_join_store::TimeTracker,
34 stateless, stream_join::KStreamKStreamJoinProcessor, table::KStreamToTableProcessor,
35 tuple_forwarder::TupleForwarder,
36 },
37 windows::JoinWindows,
38 },
39 processor::serde::{BytesSerde, DefaultSerde, Produced, Serde, SerdeAssociate},
40 topology::NodeHandle,
41};
42
43type SharedOuterJoiner<V, V2, VO> = Arc<dyn Fn(Option<&V>, Option<&V2>) -> VO + Send + Sync>;
48
49struct StreamJoinGraph {
50 join_this: String,
51 join_other: String,
52 this_store: String,
53 other_store: String,
54 outer_store: Option<String>,
55 this_id: NodeId,
56 other_id: NodeId,
57 merge: String,
58}
59
60fn allocate_stream_join_graph(
61 builder: &mut InternalStreamsBuilder,
62 parents: (NodeId, NodeId),
63 required: (bool, bool),
64) -> StreamJoinGraph {
65 builder.new_processor_name(names::KSTREAM_WINDOWED);
66 builder.new_processor_name(names::KSTREAM_WINDOWED);
67 let this_prefix = if required.0 {
68 names::KSTREAM_JOINTHIS
69 } else {
70 names::KSTREAM_OUTERTHIS
71 };
72 let other_prefix = if required.1 {
73 names::KSTREAM_JOINOTHER
74 } else {
75 names::KSTREAM_OUTEROTHER
76 };
77 let join_this = builder.new_processor_name(this_prefix);
78 let join_other = builder.new_processor_name(other_prefix);
79 let merge = builder.new_processor_name(names::MERGE);
80 let this_store = format!("{join_this}-store");
81 let other_store = format!("{join_other}-store");
82 let outer_store = (!required.0 || !required.1).then(|| {
83 let this_index = &join_this[this_prefix.len()..];
84 format!("{}{this_index}-store", names::KSTREAM_OUTERSHARED)
85 });
86 let this_id = builder.graph.add(
87 join_this.clone(),
88 GraphNodeKind::StatelessProcessor {
89 repartition_required: false,
90 },
91 vec![parents.0],
92 );
93 let other_id = builder.graph.add(
94 join_other.clone(),
95 GraphNodeKind::StatelessProcessor {
96 repartition_required: false,
97 },
98 vec![parents.1],
99 );
100 StreamJoinGraph {
101 join_this,
102 join_other,
103 this_store,
104 other_store,
105 outer_store,
106 this_id,
107 other_id,
108 merge,
109 }
110}
111
112type GraceLowering<K, VO> =
126 Box<dyn FnOnce(&mut LowerState, NodeId, String, String) -> NodeHandle<K, VO> + Send>;
127
128pub struct KStream<K, V, KS = <K as DefaultSerde>::Serde, VS = <V as DefaultSerde>::Serde> {
129 #[allow(dead_code)]
130 pub(crate) builder: Rc<RefCell<InternalStreamsBuilder>>,
131 #[allow(dead_code)]
132 pub(crate) node: NodeId,
133 pub(crate) key_changing: bool,
140 pub(crate) source_topic: Option<String>,
155 pub(crate) key_serde: KS,
156 pub(crate) value_serde: VS,
157 pub(crate) _pd: std::marker::PhantomData<fn() -> (K, V)>,
158}
159
160impl<K, V, KS, VS> KStream<K, V, KS, VS> {
161 pub(crate) fn new(
162 builder: Rc<RefCell<InternalStreamsBuilder>>,
163 node: NodeId,
164 key_serde: KS,
165 value_serde: VS,
166 ) -> Self {
167 Self::new_with_key_changing(builder, node, false, key_serde, value_serde)
168 }
169
170 pub(crate) fn new_with_key_changing(
171 builder: Rc<RefCell<InternalStreamsBuilder>>,
172 node: NodeId,
173 key_changing: bool,
174 key_serde: KS,
175 value_serde: VS,
176 ) -> Self {
177 Self {
178 builder,
179 node,
180 key_changing,
181 source_topic: None,
182 key_serde,
183 value_serde,
184 _pd: std::marker::PhantomData,
185 }
186 }
187
188 #[must_use]
190 pub(crate) fn with_source_topic(mut self, topic: Option<String>) -> Self {
191 self.source_topic = topic;
192 self
193 }
194
195 #[must_use]
196 pub fn with_key_serde<NewKS>(self, serde: NewKS) -> KStream<K, V, NewKS, VS> {
197 KStream {
198 builder: self.builder,
199 node: self.node,
200 key_changing: self.key_changing,
201 source_topic: self.source_topic,
202 key_serde: serde,
203 value_serde: self.value_serde,
204 _pd: std::marker::PhantomData,
205 }
206 }
207
208 #[must_use]
209 pub fn with_value_serde<NewVS>(self, serde: NewVS) -> KStream<K, V, KS, NewVS> {
210 KStream {
211 builder: self.builder,
212 node: self.node,
213 key_changing: self.key_changing,
214 source_topic: self.source_topic,
215 key_serde: self.key_serde,
216 value_serde: serde,
217 _pd: std::marker::PhantomData,
218 }
219 }
220}
221
222impl<K, V, KS, VS> KStream<K, V, KS, VS>
223where
224 K: Any + Send + Sync + Clone,
225 V: Any + Send + Clone,
226 KS: Clone,
227 VS: Clone,
228{
229 pub fn map_values<V2, F>(&self, f: F) -> KStream<K, V2, KS, <V2 as DefaultSerde>::Serde>
231 where
232 V2: DefaultSerde + Any + Send + Clone,
233 F: Fn(&V) -> V2 + Clone + Send + Sync + 'static,
234 {
235 let parent_id = self.node;
236 let mut g = self.builder.borrow_mut();
237 let name = g.new_processor_name(names::MAPVALUES);
238 let id = g.graph.add(
239 name.clone(),
240 GraphNodeKind::StatelessProcessor {
241 repartition_required: false,
242 },
243 vec![parent_id],
244 );
245 let f2 = f.clone();
246 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
247 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
248 let h = state.topology.add_processor::<K, V, K, V2, _, _, _>(
249 name.clone(),
250 move || stateless::MapValuesProcessor {
251 f: f2.clone(),
252 _pd: PhantomData,
253 },
254 [parent],
255 );
256 state.handle_name.insert(id, h.name().to_string());
257 }));
258 drop(g);
259 KStream::new_with_key_changing(
261 Rc::clone(&self.builder),
262 id,
263 self.key_changing,
264 self.key_serde.clone(),
265 <V2 as DefaultSerde>::Serde::default(),
266 )
267 .with_source_topic(self.source_topic.clone())
268 }
269
270 #[must_use]
272 pub fn filter<F>(&self, predicate: F) -> KStream<K, V, KS, VS>
273 where
274 K: Default,
275 F: Fn(&K, &V) -> bool + Clone + Send + Sync + 'static,
276 {
277 self.filter_inner(predicate, false)
278 }
279
280 #[must_use]
282 pub fn filter_not<F>(&self, predicate: F) -> KStream<K, V, KS, VS>
283 where
284 K: Default,
285 F: Fn(&K, &V) -> bool + Clone + Send + Sync + 'static,
286 {
287 self.filter_inner(predicate, true)
288 }
289
290 fn filter_inner<F>(&self, predicate: F, negate: bool) -> KStream<K, V, KS, VS>
291 where
292 K: Default,
293 F: Fn(&K, &V) -> bool + Clone + Send + Sync + 'static,
294 {
295 let parent_id = self.node;
296 let mut g = self.builder.borrow_mut();
297 let name = g.new_processor_name(names::FILTER);
298 let id = g.graph.add(
299 name.clone(),
300 GraphNodeKind::StatelessProcessor {
301 repartition_required: false,
302 },
303 vec![parent_id],
304 );
305 let p2 = predicate.clone();
306 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
307 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
308 let h = state.topology.add_processor::<K, V, K, V, _, _, _>(
309 name.clone(),
310 move || stateless::FilterProcessor {
311 predicate: p2.clone(),
312 negate,
313 _pd: PhantomData,
314 },
315 [parent],
316 );
317 state.handle_name.insert(id, h.name().to_string());
318 }));
319 drop(g);
320 KStream::new_with_key_changing(
322 Rc::clone(&self.builder),
323 id,
324 self.key_changing,
325 self.key_serde.clone(),
326 self.value_serde.clone(),
327 )
328 .with_source_topic(self.source_topic.clone())
329 }
330
331 pub fn map<K2, V2, F>(
333 &self,
334 f: F,
335 ) -> KStream<K2, V2, <K2 as DefaultSerde>::Serde, <V2 as DefaultSerde>::Serde>
336 where
337 K: Default,
338 K2: DefaultSerde + Any + Send + Clone,
339 V2: DefaultSerde + Any + Send + Clone,
340 F: Fn(&K, &V) -> (K2, V2) + Clone + Send + Sync + 'static,
341 {
342 let parent_id = self.node;
343 let mut g = self.builder.borrow_mut();
344 let name = g.new_processor_name(names::MAP);
345 let id = g.graph.add(
346 name.clone(),
347 GraphNodeKind::StatelessProcessor {
348 repartition_required: false,
349 },
350 vec![parent_id],
351 );
352 g.graph.nodes[id].key_changing_operation = true;
353 let f2 = f.clone();
354 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
355 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
356 let h = state.topology.add_processor::<K, V, K2, V2, _, _, _>(
357 name.clone(),
358 move || stateless::MapProcessor {
359 f: f2.clone(),
360 _pd: PhantomData,
361 },
362 [parent],
363 );
364 state.handle_name.insert(id, h.name().to_string());
365 }));
366 drop(g);
367 KStream::new_with_key_changing(
369 Rc::clone(&self.builder),
370 id,
371 true,
372 <K2 as DefaultSerde>::Serde::default(),
373 <V2 as DefaultSerde>::Serde::default(),
374 )
375 }
376
377 pub fn select_key<K2, F>(&self, f: F) -> KStream<K2, V, <K2 as DefaultSerde>::Serde, VS>
379 where
380 K: Default,
381 K2: DefaultSerde + Any + Send + Clone,
382 F: Fn(&K, &V) -> K2 + Clone + Send + Sync + 'static,
383 {
384 self.select_key_with_serde(f, <K2 as DefaultSerde>::Serde::default())
385 }
386
387 pub fn select_key_with_serde<K2, GKS, F>(&self, f: F, key_serde: GKS) -> KStream<K2, V, GKS, VS>
389 where
390 K: Default,
391 K2: Any + Send + Clone,
392 GKS: Serde<K2> + Clone + 'static,
393 F: Fn(&K, &V) -> K2 + Clone + Send + Sync + 'static,
394 {
395 let parent_id = self.node;
396 let mut g = self.builder.borrow_mut();
397 let name = g.new_processor_name(names::KEY_SELECT);
398 let id = g.graph.add(
399 name.clone(),
400 GraphNodeKind::StatelessProcessor {
401 repartition_required: false,
402 },
403 vec![parent_id],
404 );
405 g.graph.nodes[id].key_changing_operation = true;
406 let f2 = f.clone();
407 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
408 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
409 let h = state.topology.add_processor::<K, V, K2, V, _, _, _>(
410 name.clone(),
411 move || stateless::SelectKeyProcessor {
412 f: f2.clone(),
413 _pd: PhantomData,
414 },
415 [parent],
416 );
417 state.handle_name.insert(id, h.name().to_string());
418 }));
419 drop(g);
420 KStream::new_with_key_changing(
422 Rc::clone(&self.builder),
423 id,
424 true,
425 key_serde,
426 self.value_serde.clone(),
427 )
428 }
429
430 pub fn flat_map<K2, V2, IT, F>(
432 &self,
433 f: F,
434 ) -> KStream<K2, V2, <K2 as DefaultSerde>::Serde, <V2 as DefaultSerde>::Serde>
435 where
436 K: Default,
437 K2: DefaultSerde + Any + Send + Clone,
438 V2: DefaultSerde + Any + Send + Clone,
439 IT: IntoIterator<Item = (K2, V2)> + 'static,
440 F: Fn(&K, &V) -> IT + Clone + Send + Sync + 'static,
441 {
442 let parent_id = self.node;
443 let mut g = self.builder.borrow_mut();
444 let name = g.new_processor_name(names::FLATMAP);
445 let id = g.graph.add(
446 name.clone(),
447 GraphNodeKind::StatelessProcessor {
448 repartition_required: false,
449 },
450 vec![parent_id],
451 );
452 g.graph.nodes[id].key_changing_operation = true;
453 let f2 = f.clone();
454 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
455 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
456 let h = state.topology.add_processor::<K, V, K2, V2, _, _, _>(
457 name.clone(),
458 move || stateless::FlatMapProcessor {
459 f: f2.clone(),
460 _pd: PhantomData,
461 },
462 [parent],
463 );
464 state.handle_name.insert(id, h.name().to_string());
465 }));
466 drop(g);
467 KStream::new_with_key_changing(
469 Rc::clone(&self.builder),
470 id,
471 true,
472 <K2 as DefaultSerde>::Serde::default(),
473 <V2 as DefaultSerde>::Serde::default(),
474 )
475 }
476
477 pub fn flat_map_values<V2, IT, F>(
479 &self,
480 f: F,
481 ) -> KStream<K, V2, KS, <V2 as DefaultSerde>::Serde>
482 where
483 V2: DefaultSerde + Any + Send + Clone,
484 IT: IntoIterator<Item = V2> + 'static,
485 F: Fn(&V) -> IT + Clone + Send + Sync + 'static,
486 {
487 let parent_id = self.node;
488 let mut g = self.builder.borrow_mut();
489 let name = g.new_processor_name(names::FLATMAPVALUES);
490 let id = g.graph.add(
491 name.clone(),
492 GraphNodeKind::StatelessProcessor {
493 repartition_required: false,
494 },
495 vec![parent_id],
496 );
497 let f2 = f.clone();
498 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
499 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
500 let h = state.topology.add_processor::<K, V, K, V2, _, _, _>(
501 name.clone(),
502 move || stateless::FlatMapValuesProcessor {
503 f: f2.clone(),
504 _pd: PhantomData,
505 },
506 [parent],
507 );
508 state.handle_name.insert(id, h.name().to_string());
509 }));
510 drop(g);
511 KStream::new_with_key_changing(
513 Rc::clone(&self.builder),
514 id,
515 self.key_changing,
516 self.key_serde.clone(),
517 <V2 as DefaultSerde>::Serde::default(),
518 )
519 .with_source_topic(self.source_topic.clone())
520 }
521
522 #[must_use]
524 pub fn peek<F>(&self, f: F) -> KStream<K, V, KS, VS>
525 where
526 K: Default,
527 F: Fn(&K, &V) + Clone + Send + Sync + 'static,
528 {
529 let parent_id = self.node;
530 let mut g = self.builder.borrow_mut();
531 let name = g.new_processor_name(names::PEEK);
532 let id = g.graph.add(
533 name.clone(),
534 GraphNodeKind::StatelessProcessor {
535 repartition_required: false,
536 },
537 vec![parent_id],
538 );
539 let f2 = f.clone();
540 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
541 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
542 let h = state.topology.add_processor::<K, V, K, V, _, _, _>(
543 name.clone(),
544 move || stateless::PeekProcessor {
545 f: f2.clone(),
546 _pd: PhantomData,
547 },
548 [parent],
549 );
550 state.handle_name.insert(id, h.name().to_string());
551 }));
552 drop(g);
553 KStream::new_with_key_changing(
555 Rc::clone(&self.builder),
556 id,
557 self.key_changing,
558 self.key_serde.clone(),
559 self.value_serde.clone(),
560 )
561 .with_source_topic(self.source_topic.clone())
562 }
563
564 pub fn foreach<F>(self, f: F)
566 where
567 K: Default,
568 F: Fn(&K, &V) + Clone + Send + Sync + 'static,
569 {
570 let parent_id = self.node;
571 let mut g = self.builder.borrow_mut();
572 let name = g.new_processor_name(names::FOREACH);
573 let id = g.graph.add(
574 name.clone(),
575 GraphNodeKind::StatelessProcessor {
576 repartition_required: false,
577 },
578 vec![parent_id],
579 );
580 let f2 = f.clone();
581 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
582 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
583 let h = state.topology.add_processor::<K, V, K, V, _, _, _>(
584 name.clone(),
585 move || stateless::ForeachProcessor {
586 f: f2.clone(),
587 _pd: PhantomData,
588 },
589 [parent],
590 );
591 state.handle_name.insert(id, h.name().to_string());
592 }));
593 }
594
595 pub fn to_explicit<KS2, VS2>(
597 self,
598 topic: impl Into<String>,
599 produced: impl Into<Produced<KS2, VS2>>,
600 ) where
601 KS2: SerdeAssociate + Serde<K> + Clone,
602 VS2: SerdeAssociate + Serde<V> + Clone,
603 {
604 let produced = produced.into();
605 let topic: String = topic.into();
606 let parent_id = self.node;
607 let mut g = self.builder.borrow_mut();
608 let name = g.new_processor_name(names::SINK);
609 let id = g.graph.add(
610 name.clone(),
611 GraphNodeKind::StreamSink {
612 topic: topic.clone(),
613 },
614 vec![parent_id],
615 );
616 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
617 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
618 state.topology.add_sink_explicit::<K, V, KS2, VS2, _, _>(
619 name.clone(),
620 topic,
621 [parent],
622 produced,
623 );
624 state.handle_name.insert(id, name.clone());
626 }));
627 }
628
629 pub fn to(self, topic: impl Into<String>)
631 where
632 KS: SerdeAssociate + Serde<K> + Clone,
633 VS: SerdeAssociate + Serde<V> + Clone,
634 {
635 let key_serde = self.key_serde.clone();
636 let value_serde = self.value_serde.clone();
637 self.to_explicit(topic, Produced::with(key_serde, value_serde));
638 }
639
640 #[must_use]
642 pub fn merge(&self, other: &KStream<K, V, KS, VS>) -> KStream<K, V, KS, VS> {
643 let left_id = self.node;
644 let right_id = other.node;
645 let mut g = self.builder.borrow_mut();
646 let name = g.new_processor_name(names::MERGE);
647 let id = g.graph.add(
648 name.clone(),
649 GraphNodeKind::StatelessProcessor {
650 repartition_required: false,
651 },
652 vec![left_id, right_id],
653 );
654 g.graph.nodes[id].merge_node = true;
655 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
656 let left = NodeHandle::<K, V>::from_name(state.handle_name[&left_id].clone());
657 let right = NodeHandle::<K, V>::from_name(state.handle_name[&right_id].clone());
658 let h = state.topology.add_processor::<K, V, K, V, _, _, _>(
659 name.clone(),
660 || stateless::MergeProcessor { _pd: PhantomData },
661 [left, right],
662 );
663 state.handle_name.insert(id, h.name().to_string());
664 }));
665 drop(g);
666 KStream::new_with_key_changing(
668 Rc::clone(&self.builder),
669 id,
670 self.key_changing || other.key_changing,
671 self.key_serde.clone(),
672 self.value_serde.clone(),
673 )
674 }
675
676 #[must_use]
704 pub fn join_table<VT, VO, F, VTS>(
707 &self,
708 table: &KTable<K, VT, KS, VTS>,
709 joiner: F,
710 ) -> KStream<K, VO, KS, <VO as DefaultSerde>::Serde>
711 where
712 VT: Any + Send + Clone,
713 VO: DefaultSerde + Any + Send + Clone,
714 VTS: Clone,
715 F: Fn(&V, &VT) -> VO + Clone + Send + Sync + 'static,
716 {
717 let lf = move |v: &V, opt: Option<&VT>| joiner(v, opt.expect("inner join hit"));
720 self.join_table_impl::<VT, VO, _, VTS>(table, lf, false, None)
721 }
722
723 #[must_use]
728 pub fn left_join_table<VT, VO, F, VTS>(
729 &self,
730 table: &KTable<K, VT, KS, VTS>,
731 joiner: F,
732 ) -> KStream<K, VO, KS, <VO as DefaultSerde>::Serde>
733 where
734 VT: Any + Send + Clone,
735 VO: DefaultSerde + Any + Send + Clone,
736 VTS: Clone,
737 F: Fn(&V, Option<&VT>) -> VO + Clone + Send + Sync + 'static,
738 {
739 self.join_table_impl::<VT, VO, _, VTS>(table, joiner, true, None)
740 }
741
742 #[must_use]
757 pub fn join_table_with<VT, VO, F, VTS>(
760 &self,
761 table: &KTable<K, VT, KS, VTS>,
762 joiner: F,
763 join_config: Joined,
764 ) -> KStream<K, VO, KS, <VO as DefaultSerde>::Serde>
765 where
766 VT: Any + Send + Sync + Clone,
767 VO: DefaultSerde + Any + Send + Clone,
768 VTS: Clone,
769 KS: Serde<K> + Clone + 'static,
770 VS: Serde<V> + Clone + 'static,
771 V: Sync,
772 F: Fn(&V, &VT) -> VO + Clone + Send + Sync + 'static,
773 {
774 let lf = move |v: &V, opt: Option<&VT>| joiner(v, opt.expect("inner join hit"));
775 let configured_grace = join_config.grace;
776 drop(join_config);
777 let grace =
778 self.build_grace_lowering::<VT, VO, _, VTS>(table, lf.clone(), false, configured_grace);
779 self.join_table_impl::<VT, VO, _, VTS>(table, lf, false, grace)
780 }
781
782 #[must_use]
788 pub fn left_join_table_with<VT, VO, F, VTS>(
789 &self,
790 table: &KTable<K, VT, KS, VTS>,
791 joiner: F,
792 join_config: Joined,
793 ) -> KStream<K, VO, KS, <VO as DefaultSerde>::Serde>
794 where
795 VT: Any + Send + Sync + Clone,
796 VO: DefaultSerde + Any + Send + Clone,
797 VTS: Clone,
798 KS: Serde<K> + Clone + 'static,
799 VS: Serde<V> + Clone + 'static,
800 V: Sync,
801 F: Fn(&V, Option<&VT>) -> VO + Clone + Send + Sync + 'static,
802 {
803 let configured_grace = join_config.grace;
804 drop(join_config);
805 let grace = self.build_grace_lowering::<VT, VO, _, VTS>(
806 table,
807 joiner.clone(),
808 true,
809 configured_grace,
810 );
811 self.join_table_impl::<VT, VO, _, VTS>(table, joiner, true, grace)
812 }
813
814 fn build_grace_lowering<VT, VO, LF, VTS>(
828 &self,
829 table: &KTable<K, VT, KS, VTS>,
830 left_form: LF,
831 emit_on_miss: bool,
832 grace: Option<Time>,
833 ) -> Option<GraceLowering<K, VO>>
834 where
835 VT: Any + Send + Sync + Clone,
836 VO: DefaultSerde + Any + Send + Clone,
837 VTS: Clone,
838 KS: Serde<K> + Clone + 'static,
839 VS: Serde<V> + Clone + 'static,
840 V: Sync,
841 LF: Fn(&V, Option<&VT>) -> VO + Clone + Send + Sync + 'static,
842 {
843 let grace = grace?;
844 let retention = table
847 .versioned_retention
848 .expect("grace requires a versioned table");
849 assert!(grace < retention, "grace must be < history_retention");
850
851 let key_serde = self.key_serde.clone();
852 let value_serde = self.value_serde.clone();
853 Some(Box::new(
854 move |state: &mut LowerState,
855 parent_id: NodeId,
856 join_name: String,
857 table_store: String| {
858 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
859 let buffer_name = format!("{join_name}-Buffer");
863 let store_for_proc = table_store.clone();
864 let buffer_for_proc = buffer_name.clone();
865 let lf = left_form.clone();
866 let h = state.topology.add_processor::<K, V, K, VO, _, _, _>(
867 join_name.clone(),
868 move || crate::dsl::processors::join_grace::KStreamKTableJoinGraceProcessor {
869 table_store: store_for_proc.clone(),
870 buffer_store: buffer_for_proc.clone(),
871 grace,
872 joiner: lf.clone(),
873 emit_on_miss,
874 observed_stream_time: i64::MIN,
875 _pd: PhantomData,
876 },
877 [parent],
878 );
879 state.topology.add_join_grace_store::<K, V, KS, VS>(
882 buffer_name.clone(),
883 key_serde.clone(),
884 value_serde.clone(),
885 true,
886 [h.name().to_string()],
887 );
888 state
889 .topology
890 .connect_processor_store(h.name(), &buffer_name);
891 h
892 },
893 ))
894 }
895
896 fn join_table_impl<VT, VO, LF, VTS>(
909 &self,
910 table: &KTable<K, VT, KS, VTS>,
911 left_form: LF,
912 emit_on_miss: bool,
913 grace: Option<GraceLowering<K, VO>>,
914 ) -> KStream<K, VO, KS, <VO as DefaultSerde>::Serde>
915 where
916 VT: Any + Send + Clone,
917 VO: DefaultSerde + Any + Send + Clone,
918 VTS: Clone,
919 LF: Fn(&V, Option<&VT>) -> VO + Clone + Send + Sync + 'static,
920 {
921 assert!(
922 !self.key_changing,
923 "join: the stream key was changed upstream (map/select_key/flat_map/group_by); \
924 call `.repartition(..)` before joining to re-partition by the new key"
925 );
926 let table_store = table
927 .store_name()
928 .expect("join requires a materialized table (a store-backed KTable)")
929 .to_string();
930 let table_src = table.source_topic().map(str::to_string);
931 let table_versioned = table.versioned_retention.is_some();
936 let stream_member = self.source_topic.clone();
940
941 let parent_id = self.node;
942 let mut g = self.builder.borrow_mut();
943 let join_name = g.new_processor_name(names::JOIN);
944 let join_id = g.graph.add(
945 join_name.clone(),
946 GraphNodeKind::StatelessProcessor {
951 repartition_required: false,
952 },
953 vec![parent_id],
954 );
955 let store_for_thunk = table_store.clone();
956 g.graph.nodes[join_id].lower = Some(Box::new(move |state: &mut LowerState| {
957 let h = if let Some(grace_lower) = grace {
965 grace_lower(state, parent_id, join_name.clone(), store_for_thunk.clone())
969 } else if table_versioned {
970 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
971 let store_for_proc = store_for_thunk.clone();
972 let lf = left_form.clone();
973 state.topology.add_processor::<K, V, K, VO, _, _, _>(
974 join_name.clone(),
975 move || crate::dsl::processors::join::KStreamKTableJoinAsOfProcessor {
976 table_store: store_for_proc.clone(),
977 joiner: lf.clone(),
978 emit_on_miss,
979 _pd: PhantomData,
980 },
981 [parent],
982 )
983 } else {
984 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
985 let store_for_proc = store_for_thunk.clone();
986 let lf = left_form.clone();
987 state.topology.add_processor::<K, V, K, VO, _, _, _>(
988 join_name.clone(),
989 move || KStreamKTableJoinProcessor {
990 table_store: store_for_proc.clone(),
991 joiner: lf.clone(),
992 emit_on_miss,
993 _pd: PhantomData,
994 },
995 [parent],
996 )
997 };
998 state
1002 .topology
1003 .connect_processor_store(h.name(), &store_for_thunk);
1004 if let (Some(sm), Some(ts)) = (&stream_member, &table_src) {
1008 state
1009 .topology
1010 .add_copartition_group([sm.clone(), ts.clone()]);
1011 }
1012 state.handle_name.insert(join_id, h.name().to_string());
1013 }));
1014 drop(g);
1015 KStream::new_with_key_changing(
1018 Rc::clone(&self.builder),
1019 join_id,
1020 false,
1021 self.key_serde.clone(),
1022 <VO as DefaultSerde>::Serde::default(),
1023 )
1024 }
1025
1026 #[must_use]
1042 pub fn join_global<GK, VG, VR, KM, J, GKS, VGS>(
1045 &self,
1046 global: &crate::dsl::global_table::GlobalKTable<GK, VG, GKS, VGS>,
1047 key_mapper: KM,
1048 joiner: J,
1049 ) -> KStream<K, VR, KS, <VR as DefaultSerde>::Serde>
1050 where
1051 GK: Any + Send + Sync + 'static,
1052 VG: Any + Send + Clone,
1053 VR: DefaultSerde + Any + Send + Clone,
1054 GKS: Clone,
1055 VGS: Clone,
1056 KM: Fn(&K, &V) -> GK + Clone + Send + Sync + 'static,
1057 J: Fn(&V, &VG) -> VR + Clone + Send + Sync + 'static,
1058 {
1059 let jf = move |v: &V, opt: Option<&VG>| joiner(v, opt.expect("inner global join hit"));
1062 self.join_global_impl::<GK, VG, VR, KM, _, GKS, VGS>(global, key_mapper, jf, false)
1063 }
1064
1065 #[must_use]
1070 pub fn left_join_global<GK, VG, VR, KM, J, GKS, VGS>(
1071 &self,
1072 global: &crate::dsl::global_table::GlobalKTable<GK, VG, GKS, VGS>,
1073 key_mapper: KM,
1074 joiner: J,
1075 ) -> KStream<K, VR, KS, <VR as DefaultSerde>::Serde>
1076 where
1077 GK: Any + Send + Sync + 'static,
1078 VG: Any + Send + Clone,
1079 VR: DefaultSerde + Any + Send + Clone,
1080 GKS: Clone,
1081 VGS: Clone,
1082 KM: Fn(&K, &V) -> GK + Clone + Send + Sync + 'static,
1083 J: Fn(&V, Option<&VG>) -> VR + Clone + Send + Sync + 'static,
1084 {
1085 self.join_global_impl::<GK, VG, VR, KM, _, GKS, VGS>(global, key_mapper, joiner, true)
1086 }
1087
1088 fn join_global_impl<GK, VG, VR, KM, LF, GKS, VGS>(
1103 &self,
1104 global: &crate::dsl::global_table::GlobalKTable<GK, VG, GKS, VGS>,
1105 key_mapper: KM,
1106 left_form: LF,
1107 emit_on_miss: bool,
1108 ) -> KStream<K, VR, KS, <VR as DefaultSerde>::Serde>
1109 where
1110 GK: Any + Send + Sync + 'static,
1111 VG: Any + Send + Clone,
1112 VR: DefaultSerde + Any + Send + Clone,
1113 GKS: Clone,
1114 VGS: Clone,
1115 KM: Fn(&K, &V) -> GK + Clone + Send + Sync + 'static,
1116 LF: Fn(&V, Option<&VG>) -> VR + Clone + Send + Sync + 'static,
1117 {
1118 let store_name = global.store_name().to_string();
1119
1120 let parent_id = self.node;
1121 let mut g = self.builder.borrow_mut();
1122 let join_name = g.new_processor_name(names::GLOBALTABLE_JOIN);
1123 let join_id = g.graph.add(
1124 join_name.clone(),
1125 GraphNodeKind::StatelessProcessor {
1129 repartition_required: false,
1130 },
1131 vec![parent_id],
1132 );
1133 g.graph.nodes[join_id].lower = Some(Box::new(move |state: &mut LowerState| {
1134 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
1135 let store_for_proc = store_name.clone();
1136 let km = key_mapper.clone();
1137 let jf = left_form.clone();
1138 let h = state.topology.add_processor::<K, V, K, VR, _, _, _>(
1139 join_name.clone(),
1140 move || KStreamGlobalTableJoinProcessor {
1141 store_name: store_for_proc.clone(),
1142 key_mapper: km.clone(),
1143 joiner: jf.clone(),
1144 emit_on_miss,
1145 _pd: PhantomData,
1146 },
1147 [parent],
1148 );
1149 state.handle_name.insert(join_id, h.name().to_string());
1150 }));
1151 drop(g);
1152 KStream::new_with_key_changing(
1155 Rc::clone(&self.builder),
1156 join_id,
1157 false,
1158 self.key_serde.clone(),
1159 <VR as DefaultSerde>::Serde::default(),
1160 )
1161 }
1162
1163 #[must_use]
1195 pub fn join<V2, VO, F, V2S>(
1198 &self,
1199 other: &KStream<K, V2, KS, V2S>,
1200 joiner: F,
1201 windows: JoinWindows,
1202 stream_joined: StreamJoined<KS, VS, V2S>,
1203 ) -> KStream<K, VO, KS, <VO as DefaultSerde>::Serde>
1204 where
1205 V2: Any + Send + Sync + Clone,
1206 VO: DefaultSerde + Any + Send + Clone,
1207 F: Fn(&V, &V2) -> VO + Clone + Send + Sync + 'static,
1208 KS: Serde<K> + Clone + 'static,
1209 VS: Serde<V> + Clone + 'static,
1210 V2S: Serde<V2> + Clone + 'static,
1211 V: Send + Sync,
1212 {
1213 let j =
1216 move |a: Option<&V>, b: Option<&V2>| joiner(a.expect("inner a"), b.expect("inner b"));
1217 let j: SharedOuterJoiner<V, V2, VO> = Arc::new(j);
1218 self.join_impl::<V2, VO, V2S>(other, &j, windows, stream_joined, JoinKind::inner())
1219 }
1220
1221 #[must_use]
1229 pub fn left_join<V2, VO, F, V2S>(
1232 &self,
1233 other: &KStream<K, V2, KS, V2S>,
1234 joiner: F,
1235 windows: JoinWindows,
1236 stream_joined: StreamJoined<KS, VS, V2S>,
1237 ) -> KStream<K, VO, KS, <VO as DefaultSerde>::Serde>
1238 where
1239 V2: Any + Send + Sync + Clone,
1240 VO: DefaultSerde + Any + Send + Clone,
1241 F: Fn(&V, Option<&V2>) -> VO + Clone + Send + Sync + 'static,
1242 KS: Serde<K> + Clone + 'static,
1243 VS: Serde<V> + Clone + 'static,
1244 V2S: Serde<V2> + Clone + 'static,
1245 V: Send + Sync,
1246 {
1247 let j = move |a: Option<&V>, b: Option<&V2>| joiner(a.expect("left a"), b);
1250 let j: SharedOuterJoiner<V, V2, VO> = Arc::new(j);
1251 self.join_impl::<V2, VO, V2S>(other, &j, windows, stream_joined, JoinKind::left())
1252 }
1253
1254 #[must_use]
1260 pub fn outer_join<V2, VO, F, V2S>(
1261 &self,
1262 other: &KStream<K, V2, KS, V2S>,
1263 joiner: F,
1264 windows: JoinWindows,
1265 stream_joined: StreamJoined<KS, VS, V2S>,
1266 ) -> KStream<K, VO, KS, <VO as DefaultSerde>::Serde>
1267 where
1268 V2: Any + Send + Sync + Clone,
1269 VO: DefaultSerde + Any + Send + Clone,
1270 F: Fn(Option<&V>, Option<&V2>) -> VO + Clone + Send + Sync + 'static,
1271 KS: Serde<K> + Clone + 'static,
1272 VS: Serde<V> + Clone + 'static,
1273 V2S: Serde<V2> + Clone + 'static,
1274 V: Send + Sync,
1275 {
1276 let joiner: SharedOuterJoiner<V, V2, VO> = Arc::new(joiner);
1278 self.join_impl::<V2, VO, V2S>(other, &joiner, windows, stream_joined, JoinKind::outer())
1279 }
1280
1281 fn join_impl<V2, VO, V2S>(
1306 &self,
1307 other: &KStream<K, V2, KS, V2S>,
1308 outer_joiner: &SharedOuterJoiner<V, V2, VO>,
1309 windows: JoinWindows,
1310 stream_joined: StreamJoined<KS, VS, V2S>,
1311 kind: JoinKind,
1312 ) -> KStream<K, VO, KS, <VO as DefaultSerde>::Serde>
1313 where
1314 V2: Any + Send + Sync + Clone,
1315 VO: DefaultSerde + Any + Send + Clone,
1316 KS: Serde<K> + Clone + 'static,
1317 VS: Serde<V> + Clone + 'static,
1318 V2S: Serde<V2> + Clone + 'static,
1319 V: Send + Sync,
1320 {
1321 let a_src = self.source_topic.clone();
1322 let b_src = other.source_topic.clone();
1323
1324 let parent_id = self.node;
1325 let other_parent_id = other.node;
1326
1327 let (before, after, grace) = (windows.before, windows.after, windows.grace);
1328
1329 let mut g = self.builder.borrow_mut();
1330 let StreamJoinGraph {
1331 join_this,
1332 join_other,
1333 this_store,
1334 other_store,
1335 outer_store,
1336 this_id,
1337 other_id,
1338 merge,
1339 } = allocate_stream_join_graph(
1340 &mut g,
1341 (parent_id, other_parent_id),
1342 (kind.a_required, kind.b_required),
1343 );
1344
1345 let this_store_clone = this_store.clone();
1346 let other_store_clone = other_store.clone();
1347 let outer_store_clone = outer_store.clone();
1348
1349 let tracker = Arc::new(Mutex::new(TimeTracker::default()));
1352
1353 let StreamJoined {
1356 key: ks,
1357 left_value: vs1,
1358 right_value: vs2,
1359 } = stream_joined;
1360
1361 {
1363 let own = this_store_clone.clone();
1364 let other_s = other_store_clone.clone();
1365 let outer_store_this = outer_store_clone.clone();
1366 let tracker_this = Arc::clone(&tracker);
1367 let ks_for_proc = ks.clone();
1368 let vs_for_proc = vs1.clone();
1369 let oj = Arc::clone(outer_joiner);
1370 let this_emit = !kind.b_required; g.graph.nodes[this_id].lower = Some(Box::new(move |state: &mut LowerState| {
1373 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
1374 let own_for_proc = own.clone();
1375 let other_for_proc = other_s.clone();
1376 let outer_store_proc = outer_store_this.clone();
1377 let tracker_proc = Arc::clone(&tracker_this);
1378 let ks = ks_for_proc.clone();
1379 let vs = vs_for_proc.clone();
1380 let oj = Arc::clone(&oj);
1381
1382 let ks_for_proc_inner = ks.clone();
1383 let vs_for_proc_inner = vs.clone();
1384 let outer_store_for_proc = outer_store_proc.clone();
1385 let h = state.topology.add_processor::<K, V, K, VO, _, _, _>(
1386 join_this.clone(),
1387 move || KStreamKStreamJoinProcessor {
1388 own_store: own_for_proc.clone(),
1389 other_store: other_for_proc.clone(),
1390 fetch_before: before,
1391 fetch_after: after,
1392 joiner: {
1393 let oj = Arc::clone(&oj);
1394 move |a: &V, b: Option<&V2>| oj(Some(a), b)
1395 },
1396 side_left: true,
1397 emit_unmatched: this_emit,
1398 outer_store: outer_store_for_proc.clone(),
1399 tracker: outer_store_for_proc
1400 .as_ref()
1401 .map(|_| Arc::clone(&tracker_proc)),
1402 key_serde: outer_store_for_proc
1403 .as_ref()
1404 .map(|_| Box::new(ks_for_proc_inner.clone()) as Box<dyn Serde<K>>),
1405 value_serde: outer_store_for_proc
1406 .as_ref()
1407 .map(|_| Box::new(vs_for_proc_inner.clone()) as Box<dyn Serde<V>>),
1408 before,
1409 after,
1410 grace,
1411 _pd: PhantomData,
1412 },
1413 [parent],
1414 );
1415 state.topology.add_join_window_store::<K, V, KS, VS>(
1417 own.clone(),
1418 ks.clone(),
1419 vs.clone(),
1420 (before, after, grace),
1421 [h.name().to_string()],
1422 );
1423 state.topology.connect_processor_store(h.name(), &own);
1424 state.topology.connect_processor_store(h.name(), &other_s);
1425 if let Some(os) = &outer_store_proc {
1428 state
1429 .topology
1430 .add_state_store::<Bytes, Bytes, BytesSerde, BytesSerde>(
1431 os.clone(),
1432 BytesSerde,
1433 BytesSerde,
1434 [h.name().to_string()],
1435 );
1436 }
1437 state.handle_name.insert(this_id, h.name().to_string());
1438 }));
1439 }
1440
1441 {
1443 let own = other_store_clone.clone();
1444 let other_s = this_store_clone.clone();
1445 let outer_store_other = outer_store_clone.clone();
1446 let tracker_other = Arc::clone(&tracker);
1447 let ks_for_proc = ks.clone();
1448 let vs_for_proc = vs2.clone();
1449 let oj = Arc::clone(outer_joiner);
1450 let other_emit = !kind.a_required; g.graph.nodes[other_id].lower = Some(Box::new(move |state: &mut LowerState| {
1453 let parent =
1454 NodeHandle::<K, V2>::from_name(state.handle_name[&other_parent_id].clone());
1455 let own_for_proc = own.clone();
1456 let other_for_proc = other_s.clone();
1457 let outer_store_proc = outer_store_other.clone();
1458 let tracker_proc = Arc::clone(&tracker_other);
1459 let ks = ks_for_proc.clone();
1460 let vs = vs_for_proc.clone();
1461 let oj = Arc::clone(&oj);
1462
1463 let ks_for_proc_inner = ks.clone();
1464 let vs_for_proc_inner = vs.clone();
1465 let outer_store_for_proc = outer_store_proc.clone();
1466 let h = state.topology.add_processor::<K, V2, K, VO, _, _, _>(
1467 join_other.clone(),
1468 move || KStreamKStreamJoinProcessor {
1469 own_store: own_for_proc.clone(),
1470 other_store: other_for_proc.clone(),
1471 fetch_before: after,
1475 fetch_after: before,
1476 joiner: {
1477 let oj = Arc::clone(&oj);
1478 move |b: &V2, a: Option<&V>| oj(a, Some(b))
1479 },
1480 side_left: false,
1481 emit_unmatched: other_emit,
1482 outer_store: outer_store_for_proc.clone(),
1483 tracker: outer_store_for_proc
1484 .as_ref()
1485 .map(|_| Arc::clone(&tracker_proc)),
1486 key_serde: outer_store_for_proc
1487 .as_ref()
1488 .map(|_| Box::new(ks_for_proc_inner.clone()) as Box<dyn Serde<K>>),
1489 value_serde: outer_store_for_proc
1490 .as_ref()
1491 .map(|_| Box::new(vs_for_proc_inner.clone()) as Box<dyn Serde<V2>>),
1492 before,
1493 after,
1494 grace,
1495 _pd: PhantomData,
1496 },
1497 [parent],
1498 );
1499 state.topology.add_join_window_store::<K, V2, KS, V2S>(
1501 own.clone(),
1502 ks.clone(),
1503 vs.clone(),
1504 (before, after, grace),
1505 [h.name().to_string()],
1506 );
1507 state.topology.connect_processor_store(h.name(), &own);
1508 state.topology.connect_processor_store(h.name(), &other_s);
1509 if let Some(os) = &outer_store_proc {
1512 state.topology.connect_processor_store(h.name(), os);
1513 }
1514 state.handle_name.insert(other_id, h.name().to_string());
1515 }));
1516 }
1517
1518 let merge_id = g.graph.add(
1520 merge.clone(),
1521 GraphNodeKind::StatelessProcessor {
1522 repartition_required: false,
1523 },
1524 vec![this_id, other_id],
1525 );
1526 g.graph.nodes[merge_id].merge_node = true;
1527 g.graph.nodes[merge_id].lower = Some(Box::new(move |state: &mut LowerState| {
1528 let this_parent = NodeHandle::<K, VO>::from_name(state.handle_name[&this_id].clone());
1529 let other_parent = NodeHandle::<K, VO>::from_name(state.handle_name[&other_id].clone());
1530 let h = state.topology.add_processor::<K, VO, K, VO, _, _, _>(
1531 merge.clone(),
1532 || stateless::MergeProcessor::<K, VO> { _pd: PhantomData },
1533 [this_parent, other_parent],
1534 );
1535 if let (Some(a), Some(bb)) = (&a_src, &b_src) {
1537 state
1538 .topology
1539 .add_copartition_group([a.clone(), bb.clone()]);
1540 }
1541 state.handle_name.insert(merge_id, h.name().to_string());
1542 }));
1543 drop(g);
1544 KStream::new_with_key_changing(
1546 Rc::clone(&self.builder),
1547 merge_id,
1548 false,
1549 self.key_serde.clone(),
1550 <VO as DefaultSerde>::Serde::default(),
1551 )
1552 }
1553
1554 #[must_use]
1567 pub fn repartition<KS2, VS2>(
1568 &self,
1569 repartitioned: crate::dsl::config::Repartitioned<KS2, VS2>,
1570 ) -> KStream<K, V, KS2, VS2>
1571 where
1572 KS2: Serde<K> + Clone + 'static,
1573 VS2: Serde<V> + Clone + 'static,
1574 {
1575 let crate::dsl::config::Repartitioned {
1576 name: explicit_name,
1577 partitions,
1578 key_serde,
1579 value_serde,
1580 } = repartitioned;
1581 let parent_id = self.node;
1582 let mut g = self.builder.borrow_mut();
1583 let base = explicit_name.unwrap_or_else(|| g.new_processor_name(names::SOURCE));
1585 let sink_name = g.new_processor_name(names::SINK);
1588 let source_name = g.new_processor_name(names::SOURCE);
1589 let topic_base = base.clone();
1590 let id = g.graph.add(
1591 source_name.clone(),
1592 GraphNodeKind::Repartition {
1593 topic: format!("{topic_base}{}", names::REPARTITION_SUFFIX),
1594 partitions,
1595 },
1596 vec![parent_id],
1597 );
1598 let key_serde_clone = key_serde.clone();
1599 let value_serde_clone = value_serde.clone();
1600 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
1601 let parent_name = state.handle_name[&parent_id].clone();
1602 let parent = NodeHandle::<K, V>::from_name(parent_name);
1603 let topic = format!("{}-{topic_base}{}", state.app_id, names::REPARTITION_SUFFIX);
1604 state.topology.add_sink_explicit::<K, V, KS2, VS2, _, _>(
1606 sink_name.clone(),
1607 topic.clone(),
1608 [parent],
1609 crate::processor::serde::Produced::with(
1610 key_serde_clone.clone(),
1611 value_serde_clone.clone(),
1612 ),
1613 );
1614 state.topology.add_repartition_topic(topic.clone());
1616 state.topology.add_source_explicit::<K, V, KS2, VS2>(
1618 source_name.clone(),
1619 [topic],
1620 crate::processor::serde::Consumed::with(
1621 key_serde_clone.clone(),
1622 value_serde_clone.clone(),
1623 ),
1624 );
1625 state.handle_name.insert(id, source_name.clone());
1626 }));
1627 drop(g);
1628 KStream::new_with_key_changing(Rc::clone(&self.builder), id, false, key_serde, value_serde)
1631 }
1632
1633 #[must_use]
1647 pub fn split(&self) -> BranchedStream<K, V, KS, VS> {
1648 let _branch_name = self.builder.borrow_mut().new_processor_name(names::BRANCH);
1654 BranchedStream {
1655 builder: Rc::clone(&self.builder),
1656 parent: self.node,
1657 key_changing: self.key_changing,
1658 source_topic: self.source_topic.clone(),
1659 key_serde: self.key_serde.clone(),
1660 value_serde: self.value_serde.clone(),
1661 _pd: std::marker::PhantomData,
1662 }
1663 }
1664
1665 pub fn group_by_key_explicit<GKS, GVS>(
1674 &self,
1675 grouped: impl Into<Grouped<GKS, GVS>>,
1676 ) -> KGroupedStream<K, V>
1677 where
1678 GKS: Serde<K> + Clone + 'static,
1679 GVS: Serde<V> + Clone + 'static,
1680 {
1681 let grouped = grouped.into();
1682 KGroupedStream::new(
1683 Rc::clone(&self.builder),
1684 self.node,
1685 self.key_changing,
1686 grouped.name,
1687 crate::dsl::kgrouped::repartition_lower::<K, V, GKS, GVS>(
1688 grouped.key_serde,
1689 grouped.value_serde,
1690 ),
1691 )
1692 .with_source_topic(if self.key_changing {
1693 None
1694 } else {
1695 self.source_topic.clone()
1696 })
1697 }
1698
1699 pub fn group_by_key(&self) -> KGroupedStream<K, V>
1701 where
1702 KS: Serde<K> + Clone + 'static,
1703 VS: Serde<V> + Clone + 'static,
1704 {
1705 KGroupedStream::new(
1706 Rc::clone(&self.builder),
1707 self.node,
1708 self.key_changing,
1709 None,
1710 crate::dsl::kgrouped::repartition_lower::<K, V, KS, VS>(
1711 self.key_serde.clone(),
1712 self.value_serde.clone(),
1713 ),
1714 )
1715 .with_source_topic(if self.key_changing {
1716 None
1717 } else {
1718 self.source_topic.clone()
1719 })
1720 }
1721
1722 pub fn group_by<GKS, GVS, F>(
1727 &self,
1728 f: F,
1729 grouped: impl Into<Grouped<GKS, GVS>>,
1730 ) -> KGroupedStream<GKS::Target, V>
1731 where
1732 K: Default,
1733 GKS: SerdeAssociate + Serde<GKS::Target> + Clone + 'static,
1734 GVS: SerdeAssociate + Serde<V> + Clone + 'static,
1735 GKS::Target: Any + Send + Sync + Clone,
1736 F: Fn(&K, &V) -> GKS::Target + Clone + Send + Sync + 'static,
1737 {
1738 let grouped = grouped.into();
1739 self.select_key_with_serde(f, grouped.key_serde.clone())
1740 .group_by_key_explicit(grouped)
1741 }
1742
1743 pub fn process<KOut, VOut, PS>(
1767 &self,
1768 supplier: PS,
1769 store_names: impl IntoIterator<Item = impl Into<String>>,
1770 ) -> KStream<KOut, VOut, <KOut as DefaultSerde>::Serde, <VOut as DefaultSerde>::Serde>
1771 where
1772 KOut: DefaultSerde + Any + Send + Sync + Clone,
1773 VOut: DefaultSerde + Any + Send + Clone,
1774 PS: crate::processor::api::ProcessorSupplier<K, V, KOut, VOut> + Clone + 'static,
1775 {
1776 let stores: Vec<String> = store_names.into_iter().map(Into::into).collect();
1777 let thunks: Vec<crate::dsl::builder::StoreConnectThunk> = {
1780 let g = self.builder.borrow();
1781 stores
1782 .iter()
1783 .map(|s| {
1784 g.store_thunk(s).unwrap_or_else(|| {
1785 panic!(
1786 "process references store '{s}' that was not added via add_state_store"
1787 )
1788 })
1789 })
1790 .collect()
1791 };
1792 let parent_id = self.node;
1793 let mut g = self.builder.borrow_mut();
1794 let name = g.new_processor_name(names::KSTREAM_PROCESSOR);
1795 let id = g.graph.add(
1796 name.clone(),
1797 GraphNodeKind::StatelessProcessor {
1798 repartition_required: false,
1799 },
1800 vec![parent_id],
1801 );
1802 g.graph.nodes[id].key_changing_operation = true;
1803 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
1807 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
1808 let h = state.topology.add_processor::<K, V, KOut, VOut, _, _, _>(
1809 name.clone(),
1810 supplier.clone(),
1811 [parent],
1812 );
1813 let proc_name = h.name().to_string();
1814 for t in &thunks {
1817 t(state, &proc_name);
1818 }
1819 state.handle_name.insert(id, proc_name);
1820 }));
1821 drop(g);
1822 KStream::new_with_key_changing(
1824 Rc::clone(&self.builder),
1825 id,
1826 true,
1827 <KOut as DefaultSerde>::Serde::default(),
1828 <VOut as DefaultSerde>::Serde::default(),
1829 )
1830 }
1831
1832 pub fn process_values<VOut, PS>(
1860 &self,
1861 supplier: PS,
1862 store_names: impl IntoIterator<Item = impl Into<String>>,
1863 ) -> KStream<K, VOut, KS, <VOut as DefaultSerde>::Serde>
1864 where
1865 VOut: DefaultSerde + Any + Send + Clone,
1866 PS: crate::processor::fixed_key::FixedKeyProcessorSupplier<K, V, VOut> + Clone + 'static,
1867 {
1868 let stores: Vec<String> = store_names.into_iter().map(Into::into).collect();
1869 let thunks: Vec<crate::dsl::builder::StoreConnectThunk> = {
1872 let g = self.builder.borrow();
1873 stores
1874 .iter()
1875 .map(|s| {
1876 g.store_thunk(s).unwrap_or_else(|| {
1877 panic!(
1878 "process_values references store '{s}' that was not added \
1879 via add_state_store"
1880 )
1881 })
1882 })
1883 .collect()
1884 };
1885 let parent_id = self.node;
1886 let mut g = self.builder.borrow_mut();
1887 let name = g.new_processor_name(names::KSTREAM_PROCESSVALUES);
1888 let id = g.graph.add(
1889 name.clone(),
1890 GraphNodeKind::StatelessProcessor {
1891 repartition_required: false,
1892 },
1893 vec![parent_id],
1894 );
1895 let sup = supplier;
1901 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
1902 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
1903 let sup2 = sup.clone();
1904 let h = state.topology.add_processor::<K, V, K, VOut, _, _, _>(
1905 name.clone(),
1906 move || crate::processor::fixed_key::FixedKeyAdapter { inner: sup2.get() },
1907 [parent],
1908 );
1909 let proc_name = h.name().to_string();
1910 for t in &thunks {
1913 t(state, &proc_name);
1914 }
1915 state.handle_name.insert(id, proc_name);
1916 }));
1917 drop(g);
1918 KStream::new_with_key_changing(
1920 Rc::clone(&self.builder),
1921 id,
1922 self.key_changing,
1923 self.key_serde.clone(),
1924 <VOut as DefaultSerde>::Serde::default(),
1925 )
1926 .with_source_topic(self.source_topic.clone())
1927 }
1928
1929 pub fn to_table_explicit<NKS, NVS>(
1943 &self,
1944 materialized: impl Into<Materialized<NKS, NVS>>,
1945 ) -> KTable<K, V, NKS, NVS>
1946 where
1947 NKS: Serde<K> + Clone + 'static,
1948 NVS: Serde<V> + Clone + 'static,
1949 {
1950 let materialized = materialized.into();
1951 let parent_id = self.node;
1952 let store_name = match &materialized.store_name {
1955 Some(name) => name.clone(),
1956 None => self
1957 .builder
1958 .borrow_mut()
1959 .new_processor_name(names::TOTABLE_STORE),
1960 };
1961 let Materialized {
1962 key_serde,
1963 value_serde,
1964 logging,
1965 caching,
1966 ..
1967 } = materialized;
1968 let mut g = self.builder.borrow_mut();
1969 let name = g.new_processor_name(names::TOTABLE);
1970 let id = g.graph.add(
1971 name.clone(),
1972 GraphNodeKind::Aggregate {
1973 store_name: store_name.clone(),
1974 changelog: logging,
1975 },
1976 vec![parent_id],
1977 );
1978 let store_for_thunk = store_name.clone();
1979 let key_serde_for_lower = key_serde.clone();
1980 let value_serde_for_lower = value_serde.clone();
1981 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
1982 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
1983 let store_for_proc = store_for_thunk.clone();
1984 let h = state.topology.add_processor::<K, V, K, Change<V>, _, _, _>(
1986 name.clone(),
1987 move || KStreamToTableProcessor {
1988 store_name: store_for_proc.clone(),
1989 forwarder: TupleForwarder::default(),
1990 _pd: PhantomData,
1991 },
1992 [parent],
1993 );
1994 if logging {
1998 state.topology.add_state_store::<K, V, NKS, NVS>(
1999 store_for_thunk.clone(),
2000 key_serde_for_lower.clone(),
2001 value_serde_for_lower.clone(),
2002 [h.name().to_string()],
2003 );
2004 } else {
2005 state
2006 .topology
2007 .add_state_store_no_changelog::<K, V, NKS, NVS>(
2008 store_for_thunk.clone(),
2009 key_serde_for_lower.clone(),
2010 value_serde_for_lower.clone(),
2011 );
2012 }
2013 state.topology.mark_store_caching(&store_for_thunk, caching);
2017 state.handle_name.insert(id, h.name().to_string());
2018 }));
2019 drop(g);
2020 KTable::new(
2021 Rc::clone(&self.builder),
2022 id,
2023 Some(store_name),
2024 None,
2025 key_serde,
2026 value_serde,
2027 )
2028 }
2029
2030 pub fn to_table(&self, store_name: impl Into<String>) -> KTable<K, V, KS, VS>
2032 where
2033 KS: Serde<K> + Clone + 'static,
2034 VS: Serde<V> + Clone + 'static,
2035 {
2036 self.to_table_explicit(
2037 Materialized::with(self.key_serde.clone(), self.value_serde.clone())
2038 .as_store(store_name),
2039 )
2040 }
2041}
2042
2043pub struct BranchedStream<K, V, KS = <K as DefaultSerde>::Serde, VS = <V as DefaultSerde>::Serde> {
2063 pub(crate) builder: Rc<RefCell<InternalStreamsBuilder>>,
2064 pub(crate) parent: NodeId,
2065 pub(crate) key_changing: bool,
2066 pub(crate) source_topic: Option<String>,
2067 pub(crate) key_serde: KS,
2068 pub(crate) value_serde: VS,
2069 pub(crate) _pd: std::marker::PhantomData<fn() -> (K, V)>,
2070}
2071
2072impl<K, V, KS, VS> BranchedStream<K, V, KS, VS>
2073where
2074 K: Any + Send + Clone + Default,
2075 V: Any + Send + Clone,
2076 KS: Clone,
2077 VS: Clone,
2078{
2079 pub fn branch<P>(&self, predicate: P) -> KStream<K, V, KS, VS>
2085 where
2086 P: Fn(&K, &V) -> bool + Clone + Send + Sync + 'static,
2087 {
2088 let parent_id = self.parent;
2089 let mut g = self.builder.borrow_mut();
2090 let name = g.new_processor_name(names::BRANCHCHILD);
2091 let id = g.graph.add(
2092 name.clone(),
2093 GraphNodeKind::StatelessProcessor {
2094 repartition_required: false,
2095 },
2096 vec![parent_id],
2097 );
2098 let p2 = predicate.clone();
2099 g.graph.nodes[id].lower = Some(Box::new(move |state: &mut LowerState| {
2100 let parent = NodeHandle::<K, V>::from_name(state.handle_name[&parent_id].clone());
2101 let h = state.topology.add_processor::<K, V, K, V, _, _, _>(
2102 name.clone(),
2103 move || stateless::FilterProcessor {
2104 predicate: p2.clone(),
2105 negate: false,
2106 _pd: std::marker::PhantomData,
2107 },
2108 [parent],
2109 );
2110 state.handle_name.insert(id, h.name().to_string());
2111 }));
2112 drop(g);
2113 KStream::new_with_key_changing(
2115 Rc::clone(&self.builder),
2116 id,
2117 self.key_changing,
2118 self.key_serde.clone(),
2119 self.value_serde.clone(),
2120 )
2121 .with_source_topic(self.source_topic.clone())
2122 }
2123}
2124
2125#[cfg(test)]
2126mod tests {
2127 use assert2::check;
2128 use krabka_units::prelude::*;
2129
2130 use crate::dsl::builder::StreamsBuilder;
2131
2132 #[test]
2133 fn stateless_chain_records_named_nodes() {
2134 let b = StreamsBuilder::new();
2135 b.stream::<String, String>(["in"])
2136 .map_values(|v: &String| v.to_uppercase())
2137 .filter(|_k: &String, _v: &String| true)
2138 .to("out");
2139 let g = b.internal.borrow();
2140 let names: Vec<&str> = g.graph.nodes.iter().map(|n| n.name.as_str()).collect();
2141 check!(
2142 names
2143 == vec![
2144 "KSTREAM-SOURCE-0000000000",
2145 "KSTREAM-MAPVALUES-0000000001",
2146 "KSTREAM-FILTER-0000000002",
2147 "KSTREAM-SINK-0000000003",
2148 ]
2149 );
2150 }
2151
2152 #[test]
2153 fn select_key_marks_key_changing() {
2154 let b = StreamsBuilder::new();
2155 b.stream::<String, String>(["in"])
2156 .select_key(|_k: &String, v: &String| v.clone());
2157 let g = b.internal.borrow();
2158 check!(g.graph.nodes[1].key_changing_operation);
2159 }
2160
2161 #[test]
2162 fn grace_join_builds_and_names_buffer_store() {
2163 use crate::{
2171 dsl::config::{Joined, Materialized},
2172 processor::serde::{Consumed, I64Serde, Produced, StringSerde},
2173 };
2174 let b = StreamsBuilder::new();
2175 let s = b
2176 .stream_explicit::<StringSerde, I64Serde>(["s"], Consumed::with(StringSerde, I64Serde));
2177 let t = b.table_explicit::<StringSerde, I64Serde>(
2178 "t",
2179 Consumed::with(StringSerde, I64Serde),
2180 Materialized::with(StringSerde, I64Serde).as_versioned("vt", millis(600_000)),
2181 );
2182 s.join_table_with(
2183 &t,
2184 |a: &i64, c: &i64| a + c,
2185 Joined::with_grace_period(millis(60_000)),
2186 )
2187 .to_explicit("out", Produced::with(StringSerde, I64Serde));
2188 drop(s);
2189 drop(t);
2190 let wire = b.build("app").unwrap().to_wire();
2191 let changelogs: Vec<&str> = wire
2192 .subtopologies
2193 .iter()
2194 .flat_map(|st| st.state_changelog_topics.iter())
2195 .map(|t| t.name.as_str())
2196 .collect();
2197 check!(
2198 changelogs.contains(&"app-KSTREAM-JOIN-0000000003-Buffer-changelog"),
2199 "buffer changelog missing; got {changelogs:?}"
2200 );
2201 }
2202
2203 #[test]
2204 #[should_panic(expected = "grace requires a versioned table")]
2205 fn grace_on_unversioned_table_panics() {
2206 use crate::{
2207 dsl::config::{Joined, Materialized},
2208 processor::serde::{Consumed, I64Serde, StringSerde},
2209 };
2210 let b = StreamsBuilder::new();
2211 let s = b
2212 .stream_explicit::<StringSerde, I64Serde>(["s"], Consumed::with(StringSerde, I64Serde));
2213 let t = b.table_explicit::<StringSerde, I64Serde>(
2214 "t",
2215 Consumed::with(StringSerde, I64Serde),
2216 Materialized::with(StringSerde, I64Serde).as_store("plain"),
2217 );
2218 let _ = s.join_table_with(
2219 &t,
2220 |a: &i64, c: &i64| a + c,
2221 Joined::with_grace_period(millis(1000)),
2222 );
2223 }
2224
2225 #[test]
2226 #[should_panic(expected = "grace must be < history_retention")]
2227 fn grace_geq_retention_panics() {
2228 use crate::{
2229 dsl::config::{Joined, Materialized},
2230 processor::serde::{Consumed, I64Serde, StringSerde},
2231 };
2232 let b = StreamsBuilder::new();
2233 let s = b
2234 .stream_explicit::<StringSerde, I64Serde>(["s"], Consumed::with(StringSerde, I64Serde));
2235 let t = b.table_explicit::<StringSerde, I64Serde>(
2236 "t",
2237 Consumed::with(StringSerde, I64Serde),
2238 Materialized::with(StringSerde, I64Serde).as_versioned("vt", millis(1000)),
2239 );
2240 let _ = s.join_table_with(
2241 &t,
2242 |a: &i64, c: &i64| a + c,
2243 Joined::with_grace_period(millis(1000)),
2244 );
2245 }
2246}
2247
2248#[cfg(test)]
2249mod to_table_caching_tests {
2250 use assert2::check;
2251 use krabka_units::prelude::*;
2252
2253 use crate::{
2254 I64Serde, Materialized, Produced, StringSerde, dsl::StreamsBuilder,
2255 store::backend::StoreBackend,
2256 };
2257
2258 #[test]
2262 fn to_table_caches_marks_and_dedups_emit() {
2263 let b = StreamsBuilder::new();
2264 b.stream::<String, i64>(["in"])
2265 .to_table_explicit(Materialized::with(StringSerde, I64Serde).as_store("t"))
2266 .to_stream()
2267 .to_explicit("out", Produced::with(StringSerde, I64Serde));
2268 let built = b.build("app").unwrap();
2269 let mut g =
2270 pollster::block_on(built.instantiate(&StoreBackend::InMemory, "app", kibibytes(1)))
2271 .unwrap();
2272 check!(g.cache_owner.contains_key("t"));
2273 pollster::block_on(g.init_processors()).unwrap();
2274
2275 pollster::block_on(g.pipe("in", 0, 0, Some(b"k"), &7i64.to_be_bytes(), 0)).unwrap();
2277 pollster::block_on(g.pipe("in", 0, 1, Some(b"k"), &9i64.to_be_bytes(), 1)).unwrap();
2278 check!(g.take_output().is_empty());
2280
2281 pollster::block_on(g.flush_caches()).unwrap();
2282 let out = g.take_output();
2283 check!(out.len() == 1);
2284 check!(out[0].topic == "out");
2285 check!(out[0].value.as_ref().unwrap().as_ref() == 9i64.to_be_bytes());
2287 }
2288
2289 #[test]
2292 fn to_table_uncached_when_caching_off() {
2293 let b = StreamsBuilder::new();
2294 b.stream::<String, i64>(["in"])
2295 .to_table_explicit(
2296 Materialized::with(StringSerde, I64Serde)
2297 .as_store("t")
2298 .with_caching(false),
2299 )
2300 .to_stream()
2301 .to_explicit("out", Produced::with(StringSerde, I64Serde));
2302 let built = b.build("app").unwrap();
2303 let g = pollster::block_on(built.instantiate(&StoreBackend::InMemory, "app", kibibytes(1)))
2304 .unwrap();
2305 check!(!g.cache_owner.contains_key("t"));
2306 }
2307}