mod middle;
mod sink;
mod source;
pub use middle::{filter_chain_factory, flat_map_chain_factory, map_chain_factory};
pub use sink::sink_chain_factory;
pub use source::source_chain_factory;
use async_trait::async_trait;
use std::any::Any;
use std::collections::HashMap;
use tinyflow_api::checkpoint::CheckpointContext;
use tinyflow_api::context::RuntimeContext;
use tinyflow_api::error::{StreamError, StreamResult};
#[async_trait]
pub trait ChainSegment: Send {
async fn open(&mut self, ctx: &mut RuntimeContext) -> StreamResult<()>;
async fn close(&mut self, ctx: &mut RuntimeContext) -> StreamResult<()>;
async fn on_checkpoint(
&mut self,
runtime: &mut RuntimeContext,
ckpt: &CheckpointContext,
) -> StreamResult<()>;
async fn on_checkpoint_end(
&mut self,
runtime: &mut RuntimeContext,
ckpt: &CheckpointContext,
) -> StreamResult<()>;
fn drain_counters(&mut self) -> (u64, u64) {
(0, 0)
}
fn collect_metrics(&self) -> HashMap<String, f64> {
HashMap::new()
}
}
#[async_trait]
pub trait ChainSourceSegment: ChainSegment {
async fn poll_next(&mut self) -> StreamResult<Option<Box<dyn Any + Send>>>;
}
#[async_trait]
pub trait ChainTailSegment: ChainSegment {
async fn process_record(
&mut self,
input: Box<dyn Any + Send>,
runtime: &mut RuntimeContext,
) -> StreamResult<Vec<Box<dyn Any + Send>>>;
}
pub enum ChainStepKindInner {
Source(Box<dyn ChainSourceSegment>),
Tail(Box<dyn ChainTailSegment>),
}
pub struct NamedStep {
pub name: String,
pub kind: ChainStepKindInner,
}
pub type ChainSegmentFactory = Box<dyn Fn() -> NamedStep + Send>;
pub fn instantiate_chain_steps(factories: &[ChainSegmentFactory]) -> Vec<NamedStep> {
factories.iter().map(|f| f()).collect()
}
pub(super) fn downcast_input<T: Send + 'static>(
input: Box<dyn Any + Send>,
task_id: u32,
) -> StreamResult<T> {
match input.downcast::<T>() {
Ok(v) => Ok(*v),
Err(_) => Err(StreamError::TaskFailed {
task_id,
reason: format!(
"chain segment type mismatch: expected {}",
std::any::type_name::<T>()
),
}),
}
}
macro_rules! impl_chain_segment_lifecycle {
(
$(#[$attr:meta])*
impl<$($gen:ident),*>
ChainSegment for $seg:ty
where
$($where:tt)*
) => {
#[async_trait::async_trait]
$(#[$attr])*
impl<$($gen),*> $crate::runtime::chain_segment::ChainSegment for $seg
where
$($where)*
{
async fn open(
&mut self,
ctx: &mut tinyflow_api::context::RuntimeContext,
) -> tinyflow_api::error::StreamResult<()> {
self.task_id = ctx.task_id;
tinyflow_api::functions::LifecycleAware::open(&mut self.function, ctx).await
}
async fn close(
&mut self,
ctx: &mut tinyflow_api::context::RuntimeContext,
) -> tinyflow_api::error::StreamResult<()> {
tinyflow_api::functions::LifecycleAware::close(&mut self.function, ctx).await
}
async fn on_checkpoint(
&mut self,
runtime: &mut tinyflow_api::context::RuntimeContext,
ckpt: &tinyflow_api::checkpoint::CheckpointContext,
) -> tinyflow_api::error::StreamResult<()> {
tinyflow_api::functions::LifecycleAware::on_checkpoint(
&mut self.function,
runtime,
ckpt,
)
.await
}
async fn on_checkpoint_end(
&mut self,
runtime: &mut tinyflow_api::context::RuntimeContext,
ckpt: &tinyflow_api::checkpoint::CheckpointContext,
) -> tinyflow_api::error::StreamResult<()> {
tinyflow_api::functions::LifecycleAware::on_checkpoint_end(
&mut self.function,
runtime,
ckpt,
)
.await
}
}
};
(
$(#[$attr:meta])*
impl<$($gen:ident),*>
ChainSegment for $seg:ty,
with_metrics
where
$($where:tt)*
) => {
#[async_trait::async_trait]
$(#[$attr])*
impl<$($gen),*> $crate::runtime::chain_segment::ChainSegment for $seg
where
$($where)*
{
async fn open(
&mut self,
ctx: &mut tinyflow_api::context::RuntimeContext,
) -> tinyflow_api::error::StreamResult<()> {
self.task_id = ctx.task_id;
tinyflow_api::functions::LifecycleAware::open(&mut self.function, ctx).await
}
async fn close(
&mut self,
ctx: &mut tinyflow_api::context::RuntimeContext,
) -> tinyflow_api::error::StreamResult<()> {
tinyflow_api::functions::LifecycleAware::close(&mut self.function, ctx).await
}
async fn on_checkpoint(
&mut self,
runtime: &mut tinyflow_api::context::RuntimeContext,
ckpt: &tinyflow_api::checkpoint::CheckpointContext,
) -> tinyflow_api::error::StreamResult<()> {
tinyflow_api::functions::LifecycleAware::on_checkpoint(
&mut self.function,
runtime,
ckpt,
)
.await
}
async fn on_checkpoint_end(
&mut self,
runtime: &mut tinyflow_api::context::RuntimeContext,
ckpt: &tinyflow_api::checkpoint::CheckpointContext,
) -> tinyflow_api::error::StreamResult<()> {
tinyflow_api::functions::LifecycleAware::on_checkpoint_end(
&mut self.function,
runtime,
ckpt,
)
.await
}
fn drain_counters(&mut self) -> (u64, u64) {
let i = self.input_cnt - self.last_input_cnt;
let o = self.output_cnt - self.last_output_cnt;
self.last_input_cnt = self.input_cnt;
self.last_output_cnt = self.output_cnt;
(i, o)
}
fn collect_metrics(&self) -> std::collections::HashMap<String, f64> {
self.function.report_metrics()
}
}
};
}
pub(crate) use impl_chain_segment_lifecycle;
macro_rules! middle_tail_step {
(
struct $seg:ident<$($gen:ident),*>;
marker: $marker:ty;
where { $($bound:tt)* }
process { |$this:ident, $data:ident, $runtime:ident| $($body:tt)* }
factory $factory:ident
) => {
struct $seg<$($gen),*> {
function: F,
task_id: u32,
input_cnt: u64,
output_cnt: u64,
last_input_cnt: u64,
last_output_cnt: u64,
_marker: std::marker::PhantomData<$marker>,
}
$crate::runtime::chain_segment::impl_chain_segment_lifecycle! {
impl<$($gen),*>
ChainSegment for $seg<$($gen),*>,
with_metrics
where
$($bound)*
}
#[async_trait::async_trait]
impl<$($gen),*> $crate::runtime::chain_segment::ChainTailSegment for $seg<$($gen),*>
where
$($bound)*
{
async fn process_record(
&mut self,
input: Box<dyn std::any::Any + Send>,
runtime: &mut tinyflow_api::context::RuntimeContext,
) -> tinyflow_api::error::StreamResult<Vec<Box<dyn std::any::Any + Send>>> {
let $this = self;
$this.input_cnt += 1;
let task_id = $this.task_id;
let $data = super::downcast_input(input, task_id)?;
let $runtime = runtime;
let result = { $($body)* };
if let Ok(ref records) = result {
$this.output_cnt += records.len() as u64;
}
result
}
}
pub fn $factory<$($gen),*>(f: F) -> super::ChainSegmentFactory
where
$($bound)*
{
let step_name = stringify!($seg);
Box::new(move || {
super::NamedStep {
name: step_name.to_string(),
kind: super::ChainStepKindInner::Tail(Box::new($seg {
function: f.clone(),
task_id: 0,
input_cnt: 0,
output_cnt: 0,
last_input_cnt: 0,
last_output_cnt: 0,
_marker: std::marker::PhantomData,
})),
}
})
}
};
}
pub(crate) use middle_tail_step;