use super::Collector;
use super::chain::{FatalSlot, OpMeterSlot, StageLifecycle};
use super::handoff::{ChunkConfig, SinkHandoff};
use crate::backpressure::InflightBudget;
use crate::checkpoint::AckRef;
use crate::deser::RecFamily;
use crate::error::{ErrorPolicy, FatalError, SinkError};
use crate::record::{Flow, Record, RecordMeta};
use crate::sink::{RecordRouter, RowEncoder, ShardQueues};
use bytes::BytesMut;
use std::any::Any;
use std::marker::PhantomData;
use std::sync::Arc;
use std::time::Duration;
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct SinkCtx {
pub(crate) name: String,
pub(crate) queues: ShardQueues,
pub(crate) budget: Arc<InflightBudget>,
pub(crate) chunk: ChunkConfig,
}
impl SinkCtx {
#[must_use]
pub fn new(name: String, queues: ShardQueues, budget: Arc<InflightBudget>) -> Self {
SinkCtx {
name,
queues,
budget,
chunk: ChunkConfig::default(),
}
}
#[must_use]
pub fn with_chunk(mut self, chunk: ChunkConfig) -> Self {
self.chunk = chunk;
self
}
}
pub struct Sink<F: RecFamily> {
idx: usize,
_f: PhantomData<fn() -> F>,
}
impl<F: RecFamily> Sink<F> {
pub(crate) fn new(idx: usize) -> Self {
Sink {
idx,
_f: PhantomData,
}
}
}
impl<F: RecFamily> Clone for Sink<F> {
fn clone(&self) -> Self {
*self
}
}
impl<F: RecFamily> Copy for Sink<F> {}
impl<F: RecFamily> std::fmt::Debug for Sink<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Sink").field("idx", &self.idx).finish()
}
}
trait EncoderClone<F: RecFamily>: RowEncoder<F> {
fn clone_box(&self) -> Box<dyn EncoderClone<F>>;
}
impl<F: RecFamily, T> EncoderClone<F> for T
where
T: RowEncoder<F> + Clone + 'static,
{
fn clone_box(&self) -> Box<dyn EncoderClone<F>> {
Box::new(self.clone())
}
}
type BoxedEncoder<F> = Box<dyn EncoderClone<F>>;
impl<F: RecFamily> Clone for BoxedEncoder<F> {
fn clone(&self) -> Self {
(**self).clone_box()
}
}
impl<F: RecFamily> RowEncoder<F> for BoxedEncoder<F> {
fn encode<'buf>(
&mut self,
rec: &Record<F::Rec<'buf>>,
buf: &mut BytesMut,
) -> Result<(), SinkError> {
(**self).encode(rec, buf)
}
fn buffered_bytes(&self) -> usize {
(**self).buffered_bytes()
}
fn finish_chunk(&mut self, buf: &mut BytesMut) -> Result<(), SinkError> {
(**self).finish_chunk(buf)
}
}
type BoxedRouter<F> = Box<dyn RecordRouter<F>>;
impl<F: RecFamily> RecordRouter<F> for BoxedRouter<F> {
fn route_record<'buf>(&self, rec: &Record<F::Rec<'buf>>, num_shards: usize) -> usize {
(**self).route_record(rec, num_shards)
}
}
type Branch<F> = SinkHandoff<F, BoxedEncoder<F>, BoxedRouter<F>>;
pub(crate) trait ErasedBranch: Send {
fn relieve(&mut self) -> Flow;
fn flush_terminal(&mut self) -> Flow;
fn take_fatal(&mut self) -> Option<FatalError>;
fn on_batch_end(&mut self, elapsed: Duration);
fn as_any_mut(&mut self) -> &mut dyn Any;
}
impl<F, E, R> ErasedBranch for SinkHandoff<F, E, R>
where
F: RecFamily + 'static,
E: RowEncoder<F> + Clone + 'static,
R: RecordRouter<F> + 'static,
{
fn relieve(&mut self) -> Flow {
StageLifecycle::relieve(self)
}
fn flush_terminal(&mut self) -> Flow {
StageLifecycle::flush_terminal(self)
}
fn take_fatal(&mut self) -> Option<FatalError> {
StageLifecycle::take_fatal(self)
}
fn on_batch_end(&mut self, elapsed: Duration) {
StageLifecycle::on_batch_end(self, elapsed);
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
pub(crate) fn new_branch<F, E, R>(
encoder: E,
router: R,
queues: ShardQueues,
budget: Arc<InflightBudget>,
cfg: ChunkConfig,
meter: OpMeterSlot,
component: Arc<str>,
) -> Box<dyn ErasedBranch>
where
F: RecFamily + 'static,
E: RowEncoder<F> + Clone + Send + 'static,
R: RecordRouter<F> + 'static,
{
let encoder: BoxedEncoder<F> = Box::new(encoder);
let router: BoxedRouter<F> = Box::new(router);
let handoff: Branch<F> =
SinkHandoff::new(encoder, router, queues, budget, cfg, meter, component);
Box::new(handoff)
}
pub struct SplitEmitter<'a> {
branches: &'a mut [Box<dyn ErasedBranch>],
meta: RecordMeta,
ack: &'a AckRef,
emitted: u32,
flow: Flow,
}
impl std::fmt::Debug for SplitEmitter<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SplitEmitter")
.field("emitted", &self.emitted)
.field("flow", &self.flow)
.finish_non_exhaustive()
}
}
impl SplitEmitter<'_> {
#[inline]
pub fn emit<'buf, F: RecFamily + 'static>(&mut self, handle: Sink<F>, row: F::Rec<'buf>) {
let branch = self
.branches
.get_mut(handle.idx)
.and_then(|b| b.as_any_mut().downcast_mut::<Branch<F>>())
.expect(
"split branch/handle mismatch: this Sink<F> handle does not name a \
branch of this split (a handle from another split, or the wrong \
record family)",
);
let flow = branch.push(Record {
payload: row,
meta: self.meta,
ack: self.ack.clone(),
});
self.emitted += 1;
if self.flow != Flow::Blocked {
self.flow = flow;
}
}
#[must_use]
pub fn meta(&self) -> RecordMeta {
self.meta
}
}
pub struct SplitTerminal<SrcF: RecFamily, G> {
route: G,
branches: Vec<Box<dyn ErasedBranch>>,
unmatched: ErrorPolicy,
meter: OpMeterSlot,
fatal: FatalSlot,
component: Arc<str>,
_family: PhantomData<fn() -> SrcF>,
}
impl<SrcF: RecFamily, G> SplitTerminal<SrcF, G> {
pub(crate) fn new(
route: G,
branches: Vec<Box<dyn ErasedBranch>>,
unmatched: ErrorPolicy,
meter: OpMeterSlot,
component: Arc<str>,
) -> Self {
SplitTerminal {
route,
branches,
unmatched,
meter,
fatal: FatalSlot(None),
component,
_family: PhantomData,
}
}
}
impl<SrcF: RecFamily, G> std::fmt::Debug for SplitTerminal<SrcF, G> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SplitTerminal")
.field("branches", &self.branches.len())
.field("unmatched", &self.unmatched)
.finish_non_exhaustive()
}
}
impl<'buf, SrcF, G> Collector<<SrcF as RecFamily>::Rec<'buf>> for SplitTerminal<SrcF, G>
where
SrcF: RecFamily,
G: for<'b> FnMut(SrcF::Rec<'b>, &mut SplitEmitter<'_>),
{
fn push(&mut self, rec: Record<SrcF::Rec<'buf>>) -> Flow {
self.meter.0.seen();
if self.fatal.0.is_some() {
return Flow::Continue;
}
let Record {
payload, meta, ack, ..
} = rec;
let mut em = SplitEmitter {
branches: &mut self.branches,
meta,
ack: &ack,
emitted: 0,
flow: Flow::Continue,
};
(self.route)(payload, &mut em);
let (emitted, flow) = (em.emitted, em.flow);
if emitted == 0 {
match self.unmatched {
ErrorPolicy::Skip => self.meter.0.unrouted(),
_ => {
self.fatal.0 = Some(FatalError {
component: self.component.to_string(),
reason: "record matched no split branch".into(),
});
}
}
} else {
self.meter.0.out_n(u64::from(emitted));
}
flow
}
}
impl<SrcF: RecFamily, G> StageLifecycle for SplitTerminal<SrcF, G> {
fn on_batch_end(&mut self, elapsed: Duration) {
self.meter.0.flush(elapsed);
for branch in &mut self.branches {
branch.on_batch_end(elapsed);
}
}
fn take_fatal(&mut self) -> Option<FatalError> {
if let Some(fatal) = self.fatal.0.take() {
return Some(fatal);
}
for branch in &mut self.branches {
if let Some(fatal) = branch.take_fatal() {
return Some(fatal);
}
}
None
}
fn relieve(&mut self) -> Flow {
let mut flow = Flow::Continue;
for branch in &mut self.branches {
if branch.relieve() == Flow::Blocked {
flow = Flow::Blocked;
}
}
flow
}
fn flush_terminal(&mut self) -> Flow {
let mut flow = Flow::Continue;
for branch in &mut self.branches {
if branch.flush_terminal() == Flow::Blocked {
flow = Flow::Blocked;
}
}
flow
}
}