use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::task::{Context, Poll};
use std::time::Duration;
use ruststream::AckError;
use ruststream::testing::Coordinator;
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
use tokio::time::{Instant, sleep_until};
use super::bus::{Bus, Group};
pub(crate) struct Release(Option<Coordinator>);
impl Release {
pub(crate) fn counted(coordinator: Option<Coordinator>) -> Self {
if let Some(coordinator) = &coordinator {
coordinator.enqueued();
}
Self(coordinator)
}
pub(crate) const fn uncounted() -> Self {
Self(None)
}
}
impl Drop for Release {
fn drop(&mut self) {
if let Some(coordinator) = self.0.take() {
coordinator.consumed();
}
}
}
pub(crate) struct BusDelivery {
pub(crate) message: async_nats::Message,
pub(crate) sequence: u64,
pub(crate) delivered: u64,
pub(crate) release: Release,
}
pub(crate) type DeliverySender = UnboundedSender<BusDelivery>;
pub(crate) struct Feed {
rx: UnboundedReceiver<BusDelivery>,
id: u64,
bus: Arc<Bus>,
}
impl Feed {
pub(crate) const fn new(rx: UnboundedReceiver<BusDelivery>, id: u64, bus: Arc<Bus>) -> Self {
Self { rx, id, bus }
}
pub(crate) fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<BusDelivery>> {
self.rx.poll_recv(cx)
}
pub(crate) async fn recv(&mut self) -> Option<BusDelivery> {
self.rx.recv().await
}
fn waiting(&self) -> usize {
self.rx.len()
}
}
impl Drop for Feed {
fn drop(&mut self) {
if let Some(group @ Group::Durable { .. }) = self.bus.unsubscribe(self.id) {
self.rx.close();
while let Ok(delivery) = self.rx.try_recv() {
self.bus.reroute(&group, delivery);
}
}
}
}
#[derive(Debug)]
pub(crate) struct ConsumerState {
stream: Arc<str>,
name: Arc<str>,
sequence: AtomicU64,
durable: bool,
}
impl ConsumerState {
pub(crate) fn new(stream: &str, name: &str, durable: bool) -> Arc<Self> {
Arc::new(Self {
stream: Arc::from(stream),
name: Arc::from(name),
sequence: AtomicU64::new(0),
durable,
})
}
fn group(&self) -> Option<Group> {
self.durable.then(|| Group::Durable {
stream: self.stream.to_string(),
name: self.name.to_string(),
})
}
}
pub(crate) struct Consumer {
feed: Feed,
requeue: DeliverySender,
state: Arc<ConsumerState>,
ack_wait: Duration,
}
impl std::fmt::Debug for Consumer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Consumer")
.field("stream", &self.state.stream)
.field("consumer", &self.state.name)
.finish_non_exhaustive()
}
}
impl Consumer {
pub(crate) const fn new(
feed: Feed,
requeue: DeliverySender,
state: Arc<ConsumerState>,
ack_wait: Duration,
) -> Self {
Self {
feed,
requeue,
state,
ack_wait,
}
}
pub(crate) fn stream(&self) -> &str {
&self.state.stream
}
pub(crate) fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<JetStreamDelivery>> {
self.feed
.poll_next(cx)
.map(|next| next.map(|delivery| self.deliver(delivery)))
}
fn deliver(&self, delivery: BusDelivery) -> JetStreamDelivery {
let consumer_sequence = self.state.sequence.fetch_add(1, Ordering::Relaxed) + 1;
JetStreamDelivery {
info: DeliveryInfo {
stream: Arc::clone(&self.state.stream),
consumer: Arc::clone(&self.state.name),
stream_sequence: delivery.sequence,
consumer_sequence,
delivered: delivery.delivered,
pending: self.feed.waiting() as u64,
},
redeliver: Some(Redeliver {
bus: Arc::clone(&self.feed.bus),
requeue: self.requeue.clone(),
consumer: Arc::clone(&self.state),
ack_wait: self.ack_wait,
sequence: delivery.sequence,
delivered: delivery.delivered,
}),
message: delivery.message,
_release: delivery.release,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct DeliveryInfo {
pub(crate) stream: Arc<str>,
pub(crate) consumer: Arc<str>,
pub(crate) stream_sequence: u64,
pub(crate) consumer_sequence: u64,
pub(crate) delivered: u64,
pub(crate) pending: u64,
}
pub(crate) struct JetStreamDelivery {
pub(crate) message: async_nats::Message,
pub(crate) info: DeliveryInfo,
redeliver: Option<Redeliver>,
_release: Release,
}
impl JetStreamDelivery {
pub(crate) fn ack(mut self) -> Result<(), AckError> {
self.settle().map(drop)
}
pub(crate) fn nack(mut self, requeue: bool) -> Result<(), AckError> {
let redeliver = self.settle()?;
if requeue {
redeliver.now(self.message.clone());
}
Ok(())
}
pub(crate) fn nack_after(mut self, delay: Duration) -> Result<(), AckError> {
let redeliver = self.settle()?;
redeliver.after(delay, self.message.clone());
Ok(())
}
fn settle(&mut self) -> Result<Redeliver, AckError> {
let redeliver = self
.redeliver
.take()
.ok_or_else(|| AckError::Broker("the delivery is already settled".into()))?;
if redeliver.bus.is_closed() {
return Err(AckError::Broker(
"the connection is closed, so the acknowledgement cannot be sent".into(),
));
}
Ok(redeliver)
}
}
impl Drop for JetStreamDelivery {
fn drop(&mut self) {
if let Some(redeliver) = self.redeliver.take()
&& !redeliver.bus.is_closed()
{
let ack_wait = redeliver.ack_wait;
redeliver.after(ack_wait, self.message.clone());
}
}
}
struct Redeliver {
bus: Arc<Bus>,
requeue: DeliverySender,
consumer: Arc<ConsumerState>,
ack_wait: Duration,
sequence: u64,
delivered: u64,
}
impl Redeliver {
fn again(
bus: &Bus,
requeue: &DeliverySender,
consumer: &ConsumerState,
coordinator: Option<Coordinator>,
message: async_nats::Message,
sequence: u64,
delivered: u64,
) {
let release = Release::counted(coordinator);
let sent = requeue.send(BusDelivery {
message,
sequence,
delivered: delivered + 1,
release,
});
if let (Err(failed), Some(group)) = (sent, consumer.group()) {
bus.reroute(&group, failed.0);
}
}
fn now(self, message: async_nats::Message) {
Self::again(
&self.bus,
&self.requeue,
&self.consumer,
self.bus.coordinator(),
message,
self.sequence,
self.delivered,
);
}
fn after(self, delay: Duration, message: async_nats::Message) {
let Self {
bus,
requeue,
consumer,
sequence,
delivered,
..
} = self;
if let Some(coordinator) = bus.coordinator() {
let counter = coordinator.clone();
coordinator.schedule_redelivery(delay, move || {
Self::again(
&bus,
&requeue,
&consumer,
Some(counter),
message,
sequence,
delivered,
);
});
return;
}
let due = Instant::now() + delay;
let runtime = bus.runtime().clone();
runtime.spawn(async move {
sleep_until(due).await;
Self::again(
&bus, &requeue, &consumer, None, message, sequence, delivered,
);
});
}
}