use std::sync::Arc;
use argonauts_core::control::{ControlReply, Frame};
use argonauts_core::events::RunEvent;
use argonauts_core::journal::{read_all, read_since_snapshot};
use tokio::sync::broadcast::error::RecvError;
use tokio::sync::mpsc;
use tokio_util::sync::CancellationToken;
use super::{ServerState, error};
pub(super) async fn subscription(
state: Arc<ServerState>,
id: u64,
from_seq: u64,
from_snapshot: bool,
out: mpsc::Sender<Frame<ControlReply>>,
closing: CancellationToken,
) {
let mut live = state.events.subscribe();
let backlog = if from_snapshot {
read_since_snapshot(&state.journal)
} else {
read_all(&state.journal)
};
let mut next = if from_snapshot { 0 } else { from_seq };
let backlog = match backlog {
Ok(back) => back.events,
Err(err) => {
let _ = out
.send(Frame {
id,
body: error(err.to_string()),
})
.await;
return;
}
};
if !send_events(&out, id, backlog, &mut next).await {
return;
}
loop {
let received = tokio::select! {
biased;
received = live.recv() => received,
() = closing.cancelled() => return,
};
let sent = match received {
Ok(event) if event.seq <= next => send_events(&out, id, vec![event], &mut next).await,
Ok(_) | Err(RecvError::Lagged(_)) => match read_all(&state.journal) {
Ok(back) => send_events(&out, id, back.events, &mut next).await,
Err(err) => {
let _ = out
.send(Frame {
id,
body: error(err.to_string()),
})
.await;
false
}
},
Err(RecvError::Closed) => false,
};
if !sent {
return;
}
}
}
async fn send_events(
out: &mpsc::Sender<Frame<ControlReply>>,
id: u64,
events: Vec<RunEvent>,
next: &mut u64,
) -> bool {
for event in events {
if event.seq < *next {
continue;
}
*next = event.seq + 1;
let frame = Frame {
id,
body: ControlReply::Event {
event: Box::new(event),
},
};
if out.send(frame).await.is_err() {
return false;
}
}
true
}