1use super::chain::{
9 FatalSlot, Filter, FlatMap, Inspect, Map, OpMeter, OpMeterSlot, StageLifecycle, TryMap,
10 TypedChain,
11};
12use super::handoff::{ChunkConfig, SinkHandoff};
13use super::split::{
14 ErasedBranch, SplitBranch, SplitEmitter, SplitTarget, SplitTerminal, new_branch,
15};
16use super::{Collector, Emitter, RunnableChain};
17use crate::backpressure::InflightBudget;
18use crate::deser::{Deserializer, Owned, RecFamily};
19use crate::error::ErrorPolicy;
20use crate::metrics::{ComponentLabels, DeserMetrics, OperatorMetrics};
21use crate::sink::{RecordRouter, RowEncoder, ShardQueues};
22use std::marker::PhantomData;
23use std::sync::Arc;
24
25pub trait MapFn<In, Out>: FnMut(In) -> Out {}
62impl<G, In, Out> MapFn<In, Out> for G where G: FnMut(In) -> Out {}
63
64pub trait TryMapFn<In, Out, Err>: FnMut(In) -> Result<Out, Err> {}
96impl<G, In, Out, Err> TryMapFn<In, Out, Err> for G where G: FnMut(In) -> Result<Out, Err> {}
97
98pub trait Assemble<Term> {
103 type Out;
105 fn assemble(&self, term: Term) -> Self::Out;
107}
108
109#[derive(Clone, Copy, Debug, Default)]
111pub struct Root;
112
113impl<T> Assemble<T> for Root {
114 type Out = T;
115 fn assemble(&self, term: T) -> T {
116 term
117 }
118}
119
120#[derive(Clone, Debug)]
122pub struct MapPart<Prev, G> {
123 prev: Prev,
124 f: G,
125 meter: OpMeterSlot,
126}
127
128impl<Prev, G: Clone, Term> Assemble<Term> for MapPart<Prev, G>
129where
130 Prev: Assemble<Map<G, Term>>,
131{
132 type Out = Prev::Out;
133 fn assemble(&self, term: Term) -> Self::Out {
134 self.prev.assemble(Map {
135 f: self.f.clone(),
136 next: term,
137 meter: self.meter.clone(),
138 })
139 }
140}
141
142#[derive(Clone, Debug)]
144pub struct FilterPart<Prev, P> {
145 prev: Prev,
146 p: P,
147 meter: OpMeterSlot,
148}
149
150impl<Prev, P: Clone, Term> Assemble<Term> for FilterPart<Prev, P>
151where
152 Prev: Assemble<Filter<P, Term>>,
153{
154 type Out = Prev::Out;
155 fn assemble(&self, term: Term) -> Self::Out {
156 self.prev.assemble(Filter {
157 p: self.p.clone(),
158 next: term,
159 meter: self.meter.clone(),
160 })
161 }
162}
163
164#[derive(Clone, Debug)]
166pub struct InspectPart<Prev, G> {
167 prev: Prev,
168 f: G,
169}
170
171impl<Prev, G: Clone, Term> Assemble<Term> for InspectPart<Prev, G>
172where
173 Prev: Assemble<Inspect<G, Term>>,
174{
175 type Out = Prev::Out;
176 fn assemble(&self, term: Term) -> Self::Out {
177 self.prev.assemble(Inspect {
178 f: self.f.clone(),
179 next: term,
180 })
181 }
182}
183
184#[derive(Clone, Debug)]
186pub struct TryMapPart<Prev, G> {
187 prev: Prev,
188 f: G,
189 policy: ErrorPolicy,
190 component: Arc<str>,
191 meter: OpMeterSlot,
192}
193
194impl<Prev, G: Clone, Term> Assemble<Term> for TryMapPart<Prev, G>
195where
196 Prev: Assemble<TryMap<G, Term>>,
197{
198 type Out = Prev::Out;
199 fn assemble(&self, term: Term) -> Self::Out {
200 self.prev.assemble(TryMap {
201 f: self.f.clone(),
202 next: term,
203 policy: self.policy,
204 component: Arc::clone(&self.component),
205 meter: self.meter.clone(),
206 fatal: FatalSlot(None),
207 })
208 }
209}
210
211#[derive(Clone, Debug)]
213pub struct FlatMapPart<OutF: RecFamily, Prev, G> {
214 prev: Prev,
215 g: G,
216 meter: OpMeterSlot,
217 _out: PhantomData<fn() -> OutF>,
218}
219
220impl<OutF: RecFamily, Prev, G: Clone, Term> Assemble<Term> for FlatMapPart<OutF, Prev, G>
221where
222 Prev: Assemble<FlatMap<OutF, G, Term>>,
223{
224 type Out = Prev::Out;
225 fn assemble(&self, term: Term) -> Self::Out {
226 self.prev.assemble(FlatMap {
227 g: self.g.clone(),
228 next: term,
229 meter: self.meter.clone(),
230 _out: PhantomData,
231 })
232 }
233}
234
235#[derive(Clone, Debug)]
236struct MetricsSpec {
237 pipeline: String,
238 component: String,
239 deser: Arc<DeserMetrics>,
240}
241
242impl MetricsSpec {
243 fn op_handle(&self, idx: usize, kind: &'static str) -> Arc<OperatorMetrics> {
244 let labels = ComponentLabels::new(
245 self.pipeline.clone(),
246 format!("{}.{idx}_{kind}", self.component),
247 kind,
248 );
249 Arc::new(OperatorMetrics::new(&labels))
250 }
251}
252
253fn meter_for(metrics: &Option<MetricsSpec>, idx: usize, kind: &'static str) -> OpMeterSlot {
254 OpMeterSlot(OpMeter::new(
255 metrics.as_ref().map(|m| m.op_handle(idx, kind)),
256 ))
257}
258
259#[derive(Clone, Debug)]
264pub struct ChainBuilder<DF: RecFamily, CurF: RecFamily, D, P> {
265 deser: D,
266 parts: P,
267 deser_policy: ErrorPolicy,
268 metrics: Option<MetricsSpec>,
269 stage_idx: usize,
270 _fam: PhantomData<fn() -> (DF, CurF)>,
271}
272
273pub fn chain<F: RecFamily, D: Deserializer<F>>(deser: D) -> ChainBuilder<F, F, D, Root> {
275 ChainBuilder {
276 deser,
277 parts: Root,
278 deser_policy: ErrorPolicy::Skip,
279 metrics: None,
280 stage_idx: 0,
281 _fam: PhantomData,
282 }
283}
284
285pub fn chain_owned<T, D>(deser: D) -> ChainBuilder<Owned<T>, Owned<T>, D, Root>
287where
288 T: Send + 'static,
289 D: Deserializer<Owned<T>>,
290{
291 chain(deser)
292}
293
294impl<DF: RecFamily, CurF: RecFamily, D, P> ChainBuilder<DF, CurF, D, P> {
295 #[must_use]
302 pub fn with_metrics(
303 mut self,
304 pipeline: impl Into<String>,
305 component: impl Into<String>,
306 ) -> Self {
307 assert_eq!(
308 self.stage_idx, 0,
309 "with_metrics must be called before stages are added"
310 );
311 let pipeline = pipeline.into();
312 let component = component.into();
313 let deser_labels = ComponentLabels::new(
314 pipeline.clone(),
315 format!("{component}.deserializer"),
316 "deserializer",
317 );
318 self.metrics = Some(MetricsSpec {
319 pipeline,
320 component,
321 deser: Arc::new(DeserMetrics::new(&deser_labels)),
322 });
323 self
324 }
325
326 #[must_use]
328 pub fn deser_error_policy(mut self, policy: ErrorPolicy) -> Self {
329 self.deser_policy = policy;
330 self
331 }
332
333 #[must_use]
337 pub fn map_rec<NF, G>(self, f: G) -> ChainBuilder<DF, NF, D, MapPart<P, G>>
338 where
339 NF: RecFamily,
340 G: for<'buf> MapFn<CurF::Rec<'buf>, NF::Rec<'buf>>,
341 {
342 let Self {
343 deser,
344 parts,
345 deser_policy,
346 metrics,
347 stage_idx,
348 _fam,
349 } = self;
350 let meter = meter_for(&metrics, stage_idx, "map");
351 ChainBuilder {
352 deser,
353 parts: MapPart {
354 prev: parts,
355 f,
356 meter,
357 },
358 deser_policy,
359 metrics,
360 stage_idx: stage_idx + 1,
361 _fam: PhantomData,
362 }
363 }
364
365 #[must_use]
368 pub fn try_map_rec<NF, G, E>(
369 self,
370 f: G,
371 policy: ErrorPolicy,
372 ) -> ChainBuilder<DF, NF, D, TryMapPart<P, G>>
373 where
374 NF: RecFamily,
375 G: for<'buf> TryMapFn<CurF::Rec<'buf>, NF::Rec<'buf>, E>,
376 E: std::fmt::Display,
377 {
378 let Self {
379 deser,
380 parts,
381 deser_policy,
382 metrics,
383 stage_idx,
384 _fam,
385 } = self;
386 let meter = meter_for(&metrics, stage_idx, "try_map");
387 ChainBuilder {
388 deser,
389 parts: TryMapPart {
390 prev: parts,
391 f,
392 policy,
393 component: Arc::from(format!("try_map_{stage_idx}")),
394 meter,
395 },
396 deser_policy,
397 metrics,
398 stage_idx: stage_idx + 1,
399 _fam: PhantomData,
400 }
401 }
402
403 #[must_use]
405 pub fn filter<Pr>(self, p: Pr) -> ChainBuilder<DF, CurF, D, FilterPart<P, Pr>>
406 where
407 Pr: for<'buf> FnMut(&CurF::Rec<'buf>) -> bool,
408 {
409 let Self {
410 deser,
411 parts,
412 deser_policy,
413 metrics,
414 stage_idx,
415 _fam,
416 } = self;
417 let meter = meter_for(&metrics, stage_idx, "filter");
418 ChainBuilder {
419 deser,
420 parts: FilterPart {
421 prev: parts,
422 p,
423 meter,
424 },
425 deser_policy,
426 metrics,
427 stage_idx: stage_idx + 1,
428 _fam: PhantomData,
429 }
430 }
431
432 #[must_use]
434 pub fn inspect<G>(self, f: G) -> ChainBuilder<DF, CurF, D, InspectPart<P, G>>
435 where
436 G: for<'buf> FnMut(&CurF::Rec<'buf>),
437 {
438 let Self {
439 deser,
440 parts,
441 deser_policy,
442 metrics,
443 stage_idx,
444 _fam,
445 } = self;
446 ChainBuilder {
447 deser,
448 parts: InspectPart { prev: parts, f },
449 deser_policy,
450 metrics,
451 stage_idx: stage_idx + 1,
452 _fam: PhantomData,
453 }
454 }
455
456 #[must_use]
459 pub fn flat_map<OutF, G>(self, g: G) -> ChainBuilder<DF, OutF, D, FlatMapPart<OutF, P, G>>
460 where
461 OutF: RecFamily,
462 G: for<'buf> FnMut(CurF::Rec<'buf>, &mut Emitter<'_, OutF>),
463 {
464 let Self {
465 deser,
466 parts,
467 deser_policy,
468 metrics,
469 stage_idx,
470 _fam,
471 } = self;
472 let meter = meter_for(&metrics, stage_idx, "flat_map");
473 ChainBuilder {
474 deser,
475 parts: FlatMapPart {
476 prev: parts,
477 g,
478 meter,
479 _out: PhantomData,
480 },
481 deser_policy,
482 metrics,
483 stage_idx: stage_idx + 1,
484 _fam: PhantomData,
485 }
486 }
487
488 #[must_use]
496 pub fn sink<E, R>(
497 self,
498 encoder: E,
499 router: R,
500 cfg: ChunkConfig,
501 queues: ShardQueues,
502 budget: Arc<InflightBudget>,
503 ) -> SinkedChain<DF, CurF, D, P, E, R> {
504 let handoff_meter = meter_for(&self.metrics, self.stage_idx, "sink_handoff");
505 SinkedChain {
506 builder: self,
507 encoder,
508 router,
509 cfg,
510 queues,
511 budget,
512 handoff_meter,
513 }
514 }
515
516 #[must_use]
528 pub fn split(self, unmatched: ErrorPolicy) -> SplitBuilder<DF, CurF, D, P> {
529 SplitBuilder {
530 builder: self,
531 unmatched,
532 branches: Vec::new(),
533 next_idx: 0,
534 }
535 }
536}
537
538impl<DF: RecFamily, T: Send + 'static, D, P> ChainBuilder<DF, Owned<T>, D, P> {
540 #[must_use]
543 pub fn map<U, G>(self, f: G) -> ChainBuilder<DF, Owned<U>, D, MapPart<P, G>>
544 where
545 U: Send + 'static,
546 G: FnMut(T) -> U,
547 {
548 let Self {
549 deser,
550 parts,
551 deser_policy,
552 metrics,
553 stage_idx,
554 _fam,
555 } = self;
556 let meter = meter_for(&metrics, stage_idx, "map");
557 ChainBuilder {
558 deser,
559 parts: MapPart {
560 prev: parts,
561 f,
562 meter,
563 },
564 deser_policy,
565 metrics,
566 stage_idx: stage_idx + 1,
567 _fam: PhantomData,
568 }
569 }
570
571 #[must_use]
574 pub fn try_map<U, G, E>(
575 self,
576 f: G,
577 policy: ErrorPolicy,
578 ) -> ChainBuilder<DF, Owned<U>, D, TryMapPart<P, G>>
579 where
580 U: Send + 'static,
581 G: FnMut(T) -> Result<U, E>,
582 E: std::fmt::Display,
583 {
584 let Self {
585 deser,
586 parts,
587 deser_policy,
588 metrics,
589 stage_idx,
590 _fam,
591 } = self;
592 let meter = meter_for(&metrics, stage_idx, "try_map");
593 ChainBuilder {
594 deser,
595 parts: TryMapPart {
596 prev: parts,
597 f,
598 policy,
599 component: Arc::from(format!("try_map_{stage_idx}")),
600 meter,
601 },
602 deser_policy,
603 metrics,
604 stage_idx: stage_idx + 1,
605 _fam: PhantomData,
606 }
607 }
608}
609
610#[derive(Clone, Debug)]
613pub struct SinkedChain<DF: RecFamily, CurF: RecFamily, D, P, E, R> {
614 builder: ChainBuilder<DF, CurF, D, P>,
615 encoder: E,
616 router: R,
617 cfg: ChunkConfig,
618 queues: ShardQueues,
619 budget: Arc<InflightBudget>,
620 handoff_meter: OpMeterSlot,
621}
622
623impl<DF, CurF, D, P, E, R> SinkedChain<DF, CurF, D, P, E, R>
624where
625 DF: RecFamily,
626 CurF: RecFamily,
627 D: Deserializer<DF> + 'static,
628 P: Assemble<SinkHandoff<CurF, E, R>>,
629 P::Out: for<'buf> Collector<<DF as RecFamily>::Rec<'buf>> + StageLifecycle + Send + 'static,
630 E: RowEncoder<CurF> + Clone + 'static,
631 R: RecordRouter<CurF> + Send + 'static,
632{
633 #[must_use]
638 pub fn build(self) -> Box<dyn RunnableChain> {
639 let SinkedChain {
640 builder,
641 encoder,
642 router,
643 cfg,
644 queues,
645 budget,
646 handoff_meter,
647 } = self;
648 let term = SinkHandoff::new(
649 encoder,
650 router,
651 queues,
652 budget,
653 cfg,
654 handoff_meter,
655 Arc::from("sink_handoff"),
656 );
657 let ops = builder.parts.assemble(term);
658 Box::new(TypedChain::<DF, D, _>::new(
659 builder.deser,
660 ops,
661 builder.deser_policy,
662 builder.metrics.as_ref().map(|m| Arc::clone(&m.deser)),
663 ))
664 }
665
666 #[must_use]
669 pub fn build_factory(self) -> ChainFactory<DF, CurF, D, P, E, R>
670 where
671 D: Clone,
672 E: Clone,
673 R: Clone,
674 {
675 ChainFactory { spec: self }
676 }
677}
678
679#[derive(Clone, Debug)]
682pub struct ChainFactory<DF: RecFamily, CurF: RecFamily, D, P, E, R> {
683 spec: SinkedChain<DF, CurF, D, P, E, R>,
684}
685
686impl<DF, CurF, D, P, E, R> ChainFactory<DF, CurF, D, P, E, R>
687where
688 DF: RecFamily,
689 CurF: RecFamily,
690 D: Deserializer<DF> + Clone + 'static,
691 P: Assemble<SinkHandoff<CurF, E, R>>,
692 P::Out: for<'buf> Collector<<DF as RecFamily>::Rec<'buf>> + StageLifecycle + Send + 'static,
693 E: RowEncoder<CurF> + Clone + 'static,
694 R: RecordRouter<CurF> + Clone + Send + 'static,
695{
696 #[must_use]
698 pub fn make(&self) -> Box<dyn RunnableChain> {
699 let spec = &self.spec;
700 let term = SinkHandoff::new(
701 spec.encoder.clone(),
702 spec.router.clone(),
703 spec.queues.clone(),
704 Arc::clone(&spec.budget),
705 spec.cfg,
706 spec.handoff_meter.clone(),
707 Arc::from("sink_handoff"),
708 );
709 let ops = spec.builder.parts.assemble(term);
710 Box::new(TypedChain::<DF, D, _>::new(
711 spec.builder.deser.clone(),
712 ops,
713 spec.builder.deser_policy,
714 spec.builder.metrics.as_ref().map(|m| Arc::clone(&m.deser)),
715 ))
716 }
717}
718
719pub struct SplitBuilder<DF: RecFamily, CurF: RecFamily, D, P> {
724 builder: ChainBuilder<DF, CurF, D, P>,
725 unmatched: ErrorPolicy,
726 branches: Vec<Box<dyn ErasedBranch>>,
727 next_idx: usize,
728}
729
730impl<DF: RecFamily, CurF: RecFamily, D, P> std::fmt::Debug for SplitBuilder<DF, CurF, D, P> {
731 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
732 f.debug_struct("SplitBuilder")
733 .field("branches", &self.branches.len())
734 .field("unmatched", &self.unmatched)
735 .finish_non_exhaustive()
736 }
737}
738
739impl<DF: RecFamily, CurF: RecFamily, D, P> SplitBuilder<DF, CurF, D, P> {
740 #[must_use = "a dropped SplitBranch<F> handle leaves its branch permanently unreachable"]
747 pub fn add<F, E, R>(&mut self, encoder: E, router: R, sink: SplitTarget) -> SplitBranch<F>
748 where
749 F: RecFamily + 'static,
750 E: RowEncoder<F> + Clone + Send + 'static,
751 R: RecordRouter<F> + 'static,
752 {
753 let idx = self.next_idx;
754 self.next_idx += 1;
755 let meter = OpMeterSlot(OpMeter::new(self.builder.metrics.as_ref().map(|m| {
758 let labels = ComponentLabels::new(
759 m.pipeline.clone(),
760 format!("{}.sink.{}", m.component, sink.name),
761 "sink_handoff",
762 );
763 Arc::new(OperatorMetrics::new(&labels))
764 })));
765 let component: Arc<str> = Arc::from(format!("sink.{}", sink.name));
766 let branch = new_branch::<F, E, R>(
767 encoder,
768 router,
769 sink.queues,
770 sink.budget,
771 sink.chunk,
772 meter,
773 component,
774 );
775 self.branches.push(branch);
776 SplitBranch::new(idx)
777 }
778
779 #[must_use]
784 pub fn route<G>(self, route: G) -> RoutedSplit<DF, CurF, D, P, G>
785 where
786 G: for<'buf> FnMut(CurF::Rec<'buf>, &mut SplitEmitter<'_>) + Send + 'static,
787 {
788 let handoff_meter = meter_for(&self.builder.metrics, self.builder.stage_idx, "split");
789 RoutedSplit {
790 builder: self.builder,
791 unmatched: self.unmatched,
792 branches: self.branches,
793 route,
794 handoff_meter,
795 }
796 }
797}
798
799pub struct RoutedSplit<DF: RecFamily, CurF: RecFamily, D, P, G> {
803 builder: ChainBuilder<DF, CurF, D, P>,
804 unmatched: ErrorPolicy,
805 branches: Vec<Box<dyn ErasedBranch>>,
806 route: G,
807 handoff_meter: OpMeterSlot,
808}
809
810impl<DF: RecFamily, CurF: RecFamily, D, P, G> std::fmt::Debug for RoutedSplit<DF, CurF, D, P, G> {
811 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
812 f.debug_struct("RoutedSplit")
813 .field("branches", &self.branches.len())
814 .field("unmatched", &self.unmatched)
815 .finish_non_exhaustive()
816 }
817}
818
819impl<DF, CurF, D, P, G> RoutedSplit<DF, CurF, D, P, G>
820where
821 DF: RecFamily,
822 CurF: RecFamily,
823 D: Deserializer<DF> + 'static,
824 G: for<'buf> FnMut(CurF::Rec<'buf>, &mut SplitEmitter<'_>) + Send + 'static,
825 P: Assemble<SplitTerminal<CurF, G>>,
826 P::Out: for<'buf> Collector<<DF as RecFamily>::Rec<'buf>> + StageLifecycle + Send + 'static,
827{
828 #[must_use]
830 pub fn build(self) -> Box<dyn RunnableChain> {
831 let RoutedSplit {
832 builder,
833 unmatched,
834 branches,
835 route,
836 handoff_meter,
837 } = self;
838 let term = SplitTerminal::new(
839 route,
840 branches,
841 unmatched,
842 handoff_meter,
843 Arc::from("split"),
844 );
845 let ops = builder.parts.assemble(term);
846 Box::new(TypedChain::<DF, D, _>::new(
847 builder.deser,
848 ops,
849 builder.deser_policy,
850 builder.metrics.as_ref().map(|m| Arc::clone(&m.deser)),
851 ))
852 }
853}