1use super::Collector;
29use super::chain::{FatalSlot, OpMeterSlot, StageLifecycle};
30use super::handoff::{ChunkConfig, SinkHandoff};
31use crate::backpressure::InflightBudget;
32use crate::checkpoint::AckRef;
33use crate::deser::RecFamily;
34use crate::error::{ErrorPolicy, FatalError, SinkError};
35use crate::record::{Flow, Record, RecordMeta};
36use crate::sink::{RecordRouter, RowEncoder, ShardQueues};
37use bytes::BytesMut;
38use std::any::Any;
39use std::marker::PhantomData;
40use std::sync::Arc;
41use std::time::Duration;
42
43#[derive(Clone, Debug)]
49#[non_exhaustive]
50pub struct SplitTarget {
51 pub(crate) name: String,
52 pub(crate) queues: ShardQueues,
53 pub(crate) budget: Arc<InflightBudget>,
54 pub(crate) chunk: ChunkConfig,
58}
59
60impl SplitTarget {
61 #[must_use]
67 pub fn new(name: String, queues: ShardQueues, budget: Arc<InflightBudget>) -> Self {
68 SplitTarget {
69 name,
70 queues,
71 budget,
72 chunk: ChunkConfig::default(),
73 }
74 }
75
76 #[must_use]
79 pub fn with_chunk(mut self, chunk: ChunkConfig) -> Self {
80 self.chunk = chunk;
81 self
82 }
83}
84
85pub struct SplitBranch<F: RecFamily> {
90 idx: usize,
91 _f: PhantomData<fn() -> F>,
92}
93
94impl<F: RecFamily> SplitBranch<F> {
95 pub(crate) fn new(idx: usize) -> Self {
96 SplitBranch {
97 idx,
98 _f: PhantomData,
99 }
100 }
101}
102
103impl<F: RecFamily> Clone for SplitBranch<F> {
104 fn clone(&self) -> Self {
105 *self
106 }
107}
108
109impl<F: RecFamily> Copy for SplitBranch<F> {}
110
111impl<F: RecFamily> std::fmt::Debug for SplitBranch<F> {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 f.debug_struct("SplitBranch")
114 .field("idx", &self.idx)
115 .finish()
116 }
117}
118
119trait EncoderClone<F: RecFamily>: RowEncoder<F> {
124 fn clone_box(&self) -> Box<dyn EncoderClone<F>>;
126}
127
128impl<F: RecFamily, T> EncoderClone<F> for T
129where
130 T: RowEncoder<F> + Clone + 'static,
131{
132 fn clone_box(&self) -> Box<dyn EncoderClone<F>> {
133 Box::new(self.clone())
134 }
135}
136
137type BoxedEncoder<F> = Box<dyn EncoderClone<F>>;
139
140impl<F: RecFamily> Clone for BoxedEncoder<F> {
141 fn clone(&self) -> Self {
142 (**self).clone_box()
147 }
148}
149
150impl<F: RecFamily> RowEncoder<F> for BoxedEncoder<F> {
151 fn encode<'buf>(
152 &mut self,
153 rec: &Record<F::Rec<'buf>>,
154 buf: &mut BytesMut,
155 ) -> Result<(), SinkError> {
156 (**self).encode(rec, buf)
157 }
158
159 fn buffered_bytes(&self) -> usize {
160 (**self).buffered_bytes()
161 }
162
163 fn finish_chunk(&mut self, buf: &mut BytesMut) -> Result<(), SinkError> {
164 (**self).finish_chunk(buf)
165 }
166}
167
168type BoxedRouter<F> = Box<dyn RecordRouter<F>>;
170
171impl<F: RecFamily> RecordRouter<F> for BoxedRouter<F> {
172 fn route_record<'buf>(&self, rec: &Record<F::Rec<'buf>>, num_shards: usize) -> usize {
173 (**self).route_record(rec, num_shards)
174 }
175}
176
177type Branch<F> = SinkHandoff<F, BoxedEncoder<F>, BoxedRouter<F>>;
180
181pub(crate) trait ErasedBranch: Send {
187 fn relieve(&mut self) -> Flow;
188 fn flush_terminal(&mut self) -> Flow;
189 fn take_fatal(&mut self) -> Option<FatalError>;
190 fn on_batch_end(&mut self, elapsed: Duration);
191 fn as_any_mut(&mut self) -> &mut dyn Any;
192}
193
194impl<F, E, R> ErasedBranch for SinkHandoff<F, E, R>
195where
196 F: RecFamily + 'static,
197 E: RowEncoder<F> + Clone + 'static,
198 R: RecordRouter<F> + 'static,
199{
200 fn relieve(&mut self) -> Flow {
201 StageLifecycle::relieve(self)
202 }
203
204 fn flush_terminal(&mut self) -> Flow {
205 StageLifecycle::flush_terminal(self)
206 }
207
208 fn take_fatal(&mut self) -> Option<FatalError> {
209 StageLifecycle::take_fatal(self)
210 }
211
212 fn on_batch_end(&mut self, elapsed: Duration) {
213 StageLifecycle::on_batch_end(self, elapsed);
214 }
215
216 fn as_any_mut(&mut self) -> &mut dyn Any {
217 self
218 }
219}
220
221pub(crate) fn new_branch<F, E, R>(
223 encoder: E,
224 router: R,
225 queues: ShardQueues,
226 budget: Arc<InflightBudget>,
227 cfg: ChunkConfig,
228 meter: OpMeterSlot,
229 component: Arc<str>,
230) -> Box<dyn ErasedBranch>
231where
232 F: RecFamily + 'static,
233 E: RowEncoder<F> + Clone + Send + 'static,
234 R: RecordRouter<F> + 'static,
235{
236 let encoder: BoxedEncoder<F> = Box::new(encoder);
237 let router: BoxedRouter<F> = Box::new(router);
238 let handoff: Branch<F> =
239 SinkHandoff::new(encoder, router, queues, budget, cfg, meter, component);
240 Box::new(handoff)
241}
242
243pub struct SplitEmitter<'a> {
252 branches: &'a mut [Box<dyn ErasedBranch>],
253 meta: RecordMeta,
254 ack: &'a AckRef,
255 emitted: u32,
256 flow: Flow,
257 latched: bool,
259}
260
261impl std::fmt::Debug for SplitEmitter<'_> {
262 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263 f.debug_struct("SplitEmitter")
264 .field("emitted", &self.emitted)
265 .field("flow", &self.flow)
266 .finish_non_exhaustive()
267 }
268}
269
270impl SplitEmitter<'_> {
271 #[inline]
284 pub fn emit<'buf, F: RecFamily + 'static>(
285 &mut self,
286 handle: SplitBranch<F>,
287 row: F::Rec<'buf>,
288 ) {
289 let branch = self
290 .branches
291 .get_mut(handle.idx)
292 .and_then(|b| b.as_any_mut().downcast_mut::<Branch<F>>())
293 .expect(
294 "split branch/handle mismatch: this SplitBranch<F> handle does not \
295 name a branch of this split (an index past its branches, or the \
296 wrong record family)",
297 );
298 if self.latched {
302 return;
303 }
304 let flow = branch.push(Record {
305 payload: row,
306 meta: self.meta,
307 ack: self.ack.clone(),
308 });
309 self.latched = branch.fatal.0.is_some();
310 self.emitted += 1;
311 if self.flow != Flow::Blocked {
312 self.flow = flow;
313 }
314 }
315
316 #[must_use]
318 pub fn meta(&self) -> RecordMeta {
319 self.meta
320 }
321}
322
323pub struct SplitTerminal<SrcF: RecFamily, G> {
329 route: G,
330 branches: Vec<Box<dyn ErasedBranch>>,
331 unmatched: ErrorPolicy,
332 meter: OpMeterSlot,
333 fatal: FatalSlot,
334 latched: bool,
337 component: Arc<str>,
338 _family: PhantomData<fn() -> SrcF>,
339}
340
341impl<SrcF: RecFamily, G> SplitTerminal<SrcF, G> {
342 pub(crate) fn new(
343 route: G,
344 branches: Vec<Box<dyn ErasedBranch>>,
345 unmatched: ErrorPolicy,
346 meter: OpMeterSlot,
347 component: Arc<str>,
348 ) -> Self {
349 SplitTerminal {
350 route,
351 branches,
352 unmatched,
353 meter,
354 fatal: FatalSlot(None),
355 latched: false,
356 component,
357 _family: PhantomData,
358 }
359 }
360}
361
362impl<SrcF: RecFamily, G> std::fmt::Debug for SplitTerminal<SrcF, G> {
363 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
364 f.debug_struct("SplitTerminal")
365 .field("branches", &self.branches.len())
366 .field("unmatched", &self.unmatched)
367 .finish_non_exhaustive()
368 }
369}
370
371impl<'buf, SrcF, G> Collector<<SrcF as RecFamily>::Rec<'buf>> for SplitTerminal<SrcF, G>
372where
373 SrcF: RecFamily,
374 G: for<'b> FnMut(SrcF::Rec<'b>, &mut SplitEmitter<'_>),
375{
376 fn push(&mut self, rec: Record<SrcF::Rec<'buf>>) -> Flow {
377 self.meter.0.seen();
378 if self.fatal.0.is_some() || self.latched {
383 return Flow::Continue;
384 }
385 let Record {
386 payload, meta, ack, ..
387 } = rec;
388 let mut em = SplitEmitter {
389 branches: &mut self.branches,
390 meta,
391 ack: &ack,
392 emitted: 0,
393 flow: Flow::Continue,
394 latched: false,
395 };
396 (self.route)(payload, &mut em);
397 let (emitted, flow) = (em.emitted, em.flow);
398 self.latched = em.latched;
399 if emitted == 0 {
400 match self.unmatched {
401 ErrorPolicy::Skip => self.meter.0.unrouted(),
404 _ => {
406 self.meter.0.fatal_error();
407 self.fatal.0 = Some(FatalError {
408 component: self.component.to_string(),
409 reason: "record matched no split branch".into(),
410 });
411 }
412 }
413 } else {
414 self.meter.0.out_n(u64::from(emitted));
415 }
416 flow
417 }
418}
419
420impl<SrcF: RecFamily, G> StageLifecycle for SplitTerminal<SrcF, G> {
421 fn on_batch_end(&mut self, elapsed: Duration) {
422 self.meter.0.flush(elapsed);
423 for branch in &mut self.branches {
424 branch.on_batch_end(elapsed);
425 }
426 }
427
428 fn take_fatal(&mut self) -> Option<FatalError> {
429 self.latched = false;
434 let mut earliest = None;
435 for branch in &mut self.branches {
436 let fatal = branch.take_fatal();
437 if earliest.is_none() {
438 earliest = fatal;
439 }
440 }
441 earliest.or_else(|| self.fatal.0.take())
442 }
443
444 fn relieve(&mut self) -> Flow {
445 let mut flow = Flow::Continue;
448 for branch in &mut self.branches {
449 if branch.relieve() == Flow::Blocked {
450 flow = Flow::Blocked;
451 }
452 }
453 flow
454 }
455
456 fn flush_terminal(&mut self) -> Flow {
457 let mut flow = Flow::Continue;
458 for branch in &mut self.branches {
459 if branch.flush_terminal() == Flow::Blocked {
460 flow = Flow::Blocked;
461 }
462 }
463 flow
464 }
465}