use std::future::Future;
use tinyflow_api::error::{StreamError, StreamResult, task_failed};
pub async fn report_task_failure(
error_tx: &tokio::sync::mpsc::Sender<StreamError>,
task_id: u32,
task_name: &str,
err: StreamError,
) {
log::error!("[{task_name}] task failed: {err}");
if error_tx.send(err).await.is_err() {
log::error!(
"[{task_name}] could not report failure to job (task_id={task_id}, error_tx closed)"
);
}
}
pub fn spawn_monitored_task<F, Fut>(
task_id: u32,
task_name: impl Into<String>,
error_tx: tokio::sync::mpsc::Sender<StreamError>,
run: F,
) -> tokio::task::JoinHandle<()>
where
F: FnOnce() -> Fut + Send + 'static,
Fut: Future<Output = StreamResult<()>> + Send + 'static,
{
let task_name = task_name.into();
tokio::spawn(async move {
let inner = tokio::spawn(run());
match inner.await {
Ok(Ok(())) => {}
Ok(Err(e)) => report_task_failure(&error_tx, task_id, &task_name, e).await,
Err(join_err) => {
report_task_failure(
&error_tx,
task_id,
&task_name,
task_failed(task_id, format!("task panicked or cancelled: {join_err}")),
)
.await;
}
}
})
}
#[cfg(test)]
mod tests {
use super::*;
use tinyflow_api::error::StreamError;
#[tokio::test]
async fn report_task_failure_logs_when_channel_closed() {
let (tx, rx) = tokio::sync::mpsc::channel(1);
drop(rx);
report_task_failure(
&tx,
1,
"test-task",
StreamError::TaskFailed {
task_id: 1,
reason: "boom".into(),
},
)
.await;
}
}