ruststream_nats/
subscriber.rs1use async_nats::jetstream::consumer::{PullConsumer, pull::Stream as PullStream};
4use futures::stream::{poll_fn, unfold};
5use futures::{Stream, StreamExt, future::Either};
6use ruststream::{BatchSubscriber, Subscriber};
7use std::fmt::{Debug, Formatter};
8use std::{pin::Pin, task::Poll, time::Duration};
9use tracing::warn;
10
11use crate::{
12 error::NatsError,
13 message::{CoreMessage, JetStreamMessage, NatsMessage},
14};
15
16const CORE_BATCH_LIMIT: usize = 256;
19
20enum SubscriberKind {
21 Core { inner: async_nats::Subscriber },
22 JetStream(Box<JetStreamKind>),
25}
26
27struct JetStreamKind {
28 inner: Pin<Box<PullStream>>,
29 consumer: PullConsumer,
30 stream_name: String,
31 pull_batch: usize,
32 pull_expires: Duration,
33}
34
35pub struct NatsSubscriber {
44 subject: String,
45 kind: SubscriberKind,
46}
47
48impl Debug for NatsSubscriber {
49 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
50 let mut s = f.debug_struct("NatsSubscriber");
51 s.field("subject", &self.subject);
52 match &self.kind {
53 SubscriberKind::Core { .. } => {
54 s.field("kind", &"core");
55 }
56 SubscriberKind::JetStream(js) => {
57 s.field("kind", &"jetstream")
58 .field("stream", &js.stream_name);
59 }
60 }
61 s.finish_non_exhaustive()
62 }
63}
64
65impl NatsSubscriber {
66 pub(crate) const fn from_core(subject: String, inner: async_nats::Subscriber) -> Self {
67 Self {
68 subject,
69 kind: SubscriberKind::Core { inner },
70 }
71 }
72
73 pub(crate) fn from_jetstream(
74 subject: String,
75 stream_name: String,
76 inner: PullStream,
77 consumer: PullConsumer,
78 pull_batch: usize,
79 pull_expires: Duration,
80 ) -> Self {
81 Self {
82 subject,
83 kind: SubscriberKind::JetStream(Box::new(JetStreamKind {
84 inner: Box::pin(inner),
85 consumer,
86 stream_name,
87 pull_batch,
88 pull_expires,
89 })),
90 }
91 }
92}
93
94fn core_message(msg: async_nats::Message) -> NatsMessage {
95 NatsMessage::Core(Box::new(CoreMessage::new(msg)))
96}
97
98fn jetstream_message(msg: async_nats::jetstream::Message) -> NatsMessage {
99 NatsMessage::JetStream(Box::new(JetStreamMessage::new(msg)))
100}
101
102impl Subscriber for NatsSubscriber {
103 type Message = NatsMessage;
104 type Error = NatsError;
105
106 fn stream(&mut self) -> impl Stream<Item = Result<Self::Message, Self::Error>> + Send + '_ {
107 match &mut self.kind {
111 SubscriberKind::Core { inner } => Either::Left(
112 poll_fn(move |cx| Pin::new(&mut *inner).poll_next(cx))
113 .map(|msg| Ok(core_message(msg))),
114 ),
115 SubscriberKind::JetStream(js) => Either::Right(
116 poll_fn(move |cx| js.inner.as_mut().poll_next(cx)).map(|item| match item {
117 Ok(msg) => Ok(jetstream_message(msg)),
118 Err(err) => {
119 warn!(target: "ruststream::nats", error = %err, "jetstream fetch error");
120 Err(NatsError::JetStream(Box::new(err)))
121 }
122 }),
123 ),
124 }
125 }
126}
127
128impl BatchSubscriber for NatsSubscriber {
129 type Batch = Vec<NatsMessage>;
130
131 fn batches(&mut self) -> impl Stream<Item = Result<Self::Batch, Self::Error>> + Send + '_ {
150 match &mut self.kind {
151 SubscriberKind::Core { inner } => Either::Left(poll_fn(move |cx| {
152 let first = match Pin::new(&mut *inner).poll_next(cx) {
153 Poll::Pending => return Poll::Pending,
154 Poll::Ready(None) => return Poll::Ready(None),
155 Poll::Ready(Some(msg)) => msg,
156 };
157 let mut batch = vec![core_message(first)];
158 while batch.len() < CORE_BATCH_LIMIT {
159 match Pin::new(&mut *inner).poll_next(cx) {
160 Poll::Ready(Some(msg)) => batch.push(core_message(msg)),
161 Poll::Ready(None) | Poll::Pending => break,
162 }
163 }
164 Poll::Ready(Some(Ok(batch)))
165 })),
166 SubscriberKind::JetStream(js) => {
167 let max = js.pull_batch;
168 let expires = js.pull_expires;
169 Either::Right(unfold(&mut js.consumer, move |consumer| async move {
170 loop {
171 let fetch = consumer
172 .fetch()
173 .max_messages(max)
174 .expires(expires)
175 .messages()
176 .await;
177 let mut messages = match fetch {
178 Ok(messages) => messages,
179 Err(err) => {
181 return Some((Err(NatsError::JetStream(Box::new(err))), consumer));
182 }
183 };
184 let mut batch = Vec::new();
185 while let Some(item) = messages.next().await {
186 match item {
187 Ok(msg) => batch.push(jetstream_message(msg)),
188 Err(err) => {
190 if batch.is_empty() {
191 return Some((Err(NatsError::JetStream(err)), consumer));
192 }
193 warn!(
194 target: "ruststream::nats",
195 error = %err,
196 "jetstream fetch error mid-batch; delivering the partial batch",
197 );
198 break;
199 }
200 }
201 }
202 if !batch.is_empty() {
203 return Some((Ok(batch), consumer));
204 }
205 }
207 }))
208 }
209 }
210 }
211}