use std::sync::Arc;
use tokio_util::sync::CancellationToken;
use tracing::{debug, instrument, span, Level};
use super::types::CallbackMessage;
use super::ZerobusStream;
use crate::AckCallback;
impl ZerobusStream {
#[instrument(level = "debug", skip_all)]
pub(super) fn spawn_callback_handler_task(
mut callback_rx: tokio::sync::mpsc::UnboundedReceiver<CallbackMessage>,
ack_callback: Option<Arc<dyn AckCallback>>,
cancellation_token: CancellationToken,
) -> tokio::task::JoinHandle<()> {
tokio::spawn(async move {
let span = span!(Level::DEBUG, "callback_handler");
let _guard = span.enter();
loop {
tokio::select! {
biased;
message = callback_rx.recv() => {
match message {
Some(message) => {
match message {
CallbackMessage::Ack(logical_offset) => {
if let Some(ref callback) = ack_callback {
callback.on_ack(logical_offset);
}
}
CallbackMessage::Error(logical_offset, error_message) => {
if let Some(ref callback) = ack_callback {
callback.on_error(logical_offset, &error_message);
}
}
}
}
None => { debug!("Callback handler task shutting down");
return;
}
}
}
_ = cancellation_token.cancelled() => {
debug!("Callback handler task cancelled");
return;
}
}
}
})
}
}
#[cfg(feature = "testing")]
pub struct CallbackHandlerHarness {
sender: tokio::sync::mpsc::UnboundedSender<CallbackMessage>,
task: Option<tokio::task::JoinHandle<()>>,
cancellation_token: CancellationToken,
}
#[cfg(feature = "testing")]
impl CallbackHandlerHarness {
pub fn spawn(ack_callback: Arc<dyn AckCallback>) -> Self {
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
let cancellation_token = CancellationToken::new();
let task = ZerobusStream::spawn_callback_handler_task(
rx,
Some(ack_callback),
cancellation_token.clone(),
);
Self {
sender: tx,
task: Some(task),
cancellation_token,
}
}
pub fn send_ack(&self, offset_id: crate::OffsetId) -> bool {
self.sender.send(CallbackMessage::Ack(offset_id)).is_ok()
}
pub fn send_error(&self, offset_id: crate::OffsetId, message: &str) -> bool {
self.sender
.send(CallbackMessage::Error(offset_id, message.to_string()))
.is_ok()
}
pub fn is_task_gone(&self) -> bool {
self.sender.is_closed()
}
pub async fn teardown(&mut self, callback_max_wait_time_ms: Option<u64>) {
self.cancellation_token.cancel();
if let Some(task) = self.task.take() {
ZerobusStream::shutdown_callback_task(task, callback_max_wait_time_ms).await;
}
}
}