Skip to main content

ab_farmer/cluster/
nats_client.rs

1//! NATS client
2//!
3//! [`NatsClient`] provided here is a wrapper around [`Client`] that provides convenient methods
4//! using domain-specific traits.
5//!
6//! Before reading code, make sure to familiarize yourself with NATS documentation, especially with
7//! [subjects](https://docs.nats.io/nats-concepts/subjects) and
8//! [Core NATS](https://docs.nats.io/nats-concepts/core-nats) features.
9//!
10//! Abstractions provided here cover a few use cases:
11//! * request/response (for example piece request)
12//! * request/stream of responses (for example a stream of plotted sectors of the farmer)
13//! * notifications (typically targeting a particular instance of an app) and corresponding
14//!   subscriptions (for example solution notification)
15//! * broadcasts and corresponding subscriptions (for example slot info broadcast)
16
17use crate::utils::AsyncJoinOnDrop;
18use anyhow::anyhow;
19use async_nats::{
20    Client, ConnectOptions, HeaderMap, HeaderValue, Message, PublishError, RequestError,
21    RequestErrorKind, Subject, SubscribeError, Subscriber, ToServerAddrs,
22};
23use backon::{BackoffBuilder, ExponentialBuilder};
24use futures::channel::mpsc;
25use futures::stream::FuturesUnordered;
26use futures::{FutureExt, Stream, StreamExt, select};
27use parity_scale_codec::{Decode, Encode, decode_from_bytes};
28use std::any::type_name;
29use std::collections::VecDeque;
30use std::fmt;
31use std::future::Future;
32use std::marker::PhantomData;
33use std::ops::Deref;
34use std::pin::Pin;
35use std::task::{Context, Poll};
36use std::time::Duration;
37use thiserror::Error;
38use tracing::{Instrument, debug, error, trace, warn};
39use triomphe::Arc;
40use ulid::Ulid;
41
42const EXPECTED_MESSAGE_SIZE: usize = 2 * 1024 * 1024;
43const ACKNOWLEDGEMENT_TIMEOUT: Duration = Duration::from_mins(1);
44/// Requests should time out eventually, but we should set a larger timeout to allow for spikes in
45/// load to be absorbed gracefully
46const REQUEST_TIMEOUT: Duration = Duration::from_mins(5);
47
48/// Generic request with associated response.
49///
50/// Used for cases where request/response pattern is needed and response contains a single small
51/// message. For large messages or multiple messages chunking with [`GenericStreamRequest`] can be
52/// used instead.
53pub trait GenericRequest: Encode + Decode + fmt::Debug + Send + Sync + 'static {
54    /// Request subject with optional `*` in place of application instance to receive the request
55    const SUBJECT: &'static str;
56    /// Response type that corresponds to this request
57    type Response: Encode + Decode + fmt::Debug + Send + Sync + 'static;
58}
59
60/// Generic stream request where response is streamed using
61/// [`NatsClient::stream_request_responder`].
62///
63/// Used for cases where a large payload that doesn't fit into NATS message needs to be sent or
64/// there is a very large number of messages to send. For simple request/response patten
65/// [`GenericRequest`] can be used instead.
66pub trait GenericStreamRequest: Encode + Decode + fmt::Debug + Send + Sync + 'static {
67    /// Request subject with optional `*` in place of application instance to receive the request
68    const SUBJECT: &'static str;
69    /// Response type that corresponds to this stream request.
70    ///
71    /// These responses are send as a stream of messages, each message must fit into NATS message,
72    /// [`NatsClient::approximate_max_message_size()`] can be used to estimate appropriate message
73    /// size in case chunking is needed.
74    type Response: Encode + Decode + fmt::Debug + Send + Sync + 'static;
75}
76
77/// Messages sent in response to [`GenericStreamRequest`].
78///
79/// Empty list of responses means the end of the stream.
80#[derive(Debug, Encode, Decode)]
81enum GenericStreamResponses<Response> {
82    /// Some responses, but the stream didn't end yet
83    Continue {
84        /// Monotonically increasing index of responses in a stream
85        index: u32,
86        /// Individual responses
87        responses: VecDeque<Response>,
88        /// Subject where to send acknowledgement of received stream response indices, which acts
89        /// as a backpressure mechanism
90        ack_subject: String,
91    },
92    /// Remaining responses and this is the end of the stream.
93    Last {
94        /// Monotonically increasing index of responses in a stream
95        index: u32,
96        /// Individual responses
97        responses: VecDeque<Response>,
98    },
99}
100
101impl<Response> From<GenericStreamResponses<Response>> for VecDeque<Response> {
102    #[inline]
103    fn from(value: GenericStreamResponses<Response>) -> Self {
104        #[expect(
105            clippy::rest_pattern_accessible_field,
106            reason = "Do not need other fields"
107        )]
108        match value {
109            GenericStreamResponses::Continue { responses, .. } => responses,
110            GenericStreamResponses::Last { responses, .. } => responses,
111        }
112    }
113}
114
115impl<Response> GenericStreamResponses<Response> {
116    fn next(&mut self) -> Option<Response> {
117        #[expect(
118            clippy::rest_pattern_accessible_field,
119            reason = "Do not need other fields"
120        )]
121        match self {
122            GenericStreamResponses::Continue { responses, .. } => responses.pop_front(),
123            GenericStreamResponses::Last { responses, .. } => responses.pop_front(),
124        }
125    }
126
127    fn index(&self) -> u32 {
128        #[expect(
129            clippy::rest_pattern_accessible_field,
130            reason = "Do not need other fields"
131        )]
132        match self {
133            GenericStreamResponses::Continue { index, .. } => *index,
134            GenericStreamResponses::Last { index, .. } => *index,
135        }
136    }
137
138    fn ack_subject(&self) -> Option<&str> {
139        #[expect(
140            clippy::rest_pattern_accessible_field,
141            reason = "Do not need other fields"
142        )]
143        if let GenericStreamResponses::Continue { ack_subject, .. } = self {
144            Some(ack_subject)
145        } else {
146            None
147        }
148    }
149
150    #[expect(
151        clippy::rest_pattern_accessible_field,
152        reason = "Do not care about fields"
153    )]
154    fn is_last(&self) -> bool {
155        matches!(self, Self::Last { .. })
156    }
157}
158
159/// Stream request error
160#[derive(Debug, Error)]
161pub enum StreamRequestError {
162    /// Subscribe error
163    #[error("Subscribe error: {0}")]
164    Subscribe(#[from] SubscribeError),
165    /// Publish error
166    #[error("Publish error: {0}")]
167    Publish(#[from] PublishError),
168}
169
170/// Wrapper around subscription that transforms stream of wrapped response messages into a normal
171/// `Response` stream.
172#[derive(Debug)]
173#[pin_project::pin_project]
174pub struct StreamResponseSubscriber<Response> {
175    #[pin]
176    subscriber: Subscriber,
177    response_subject: String,
178    buffered_responses: Option<GenericStreamResponses<Response>>,
179    next_index: u32,
180    acknowledgement_sender: mpsc::UnboundedSender<(String, u32)>,
181    _background_task: AsyncJoinOnDrop<()>,
182    _phantom: PhantomData<Response>,
183}
184
185impl<Response> Stream for StreamResponseSubscriber<Response>
186where
187    Response: Decode,
188{
189    type Item = Response;
190
191    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
192        if let Some(buffered_responses) = self.buffered_responses.as_mut() {
193            if let Some(response) = buffered_responses.next() {
194                return Poll::Ready(Some(response));
195            } else if buffered_responses.is_last() {
196                return Poll::Ready(None);
197            }
198
199            self.buffered_responses.take();
200            self.next_index += 1;
201        }
202
203        let mut projected = self.project();
204        match projected.subscriber.poll_next_unpin(cx) {
205            Poll::Ready(Some(message)) => {
206                match decode_from_bytes::<GenericStreamResponses<Response>>(message.payload.clone())
207                {
208                    Ok(mut responses) => {
209                        if responses.index() != *projected.next_index {
210                            warn!(
211                                actual_index = %responses.index(),
212                                expected_index = %*projected.next_index,
213                                message_type = %type_name::<Response>(),
214                                response_subject = %projected.response_subject,
215                                "Received unexpected response stream index, aborting stream"
216                            );
217
218                            return Poll::Ready(None);
219                        }
220
221                        if let Some(ack_subject) = responses.ack_subject() {
222                            let index = responses.index();
223                            let ack_subject = ack_subject.to_string();
224
225                            if let Err(error) = projected
226                                .acknowledgement_sender
227                                .unbounded_send((ack_subject.clone(), index))
228                            {
229                                warn!(
230                                    %error,
231                                    %index,
232                                    message_type = %type_name::<Response>(),
233                                    response_subject = %projected.response_subject,
234                                    %ack_subject,
235                                    "Failed to send acknowledgement for stream response"
236                                );
237                            }
238                        }
239
240                        if let Some(response) = responses.next() {
241                            *projected.buffered_responses = Some(responses);
242                            Poll::Ready(Some(response))
243                        } else {
244                            Poll::Ready(None)
245                        }
246                    }
247                    Err(error) => {
248                        warn!(
249                            %error,
250                            response_type = %type_name::<Response>(),
251                            response_subject = %projected.response_subject,
252                            message = %hex::encode(message.payload),
253                            "Failed to decode stream response"
254                        );
255
256                        Poll::Ready(None)
257                    }
258                }
259            }
260            Poll::Ready(None) => Poll::Ready(None),
261            Poll::Pending => Poll::Pending,
262        }
263    }
264}
265
266impl<Response> StreamResponseSubscriber<Response> {
267    fn new(subscriber: Subscriber, response_subject: String, nats_client: NatsClient) -> Self {
268        let (acknowledgement_sender, mut acknowledgement_receiver) =
269            mpsc::unbounded::<(String, u32)>();
270
271        let ack_publisher_fut = {
272            let response_subject = response_subject.clone();
273
274            async move {
275                while let Some((subject, index)) = acknowledgement_receiver.next().await {
276                    trace!(
277                        %subject,
278                        %index,
279                        %response_subject,
280                        %index,
281                        "Sending stream response acknowledgement"
282                    );
283                    if let Err(error) = nats_client
284                        .publish(subject.clone(), index.to_le_bytes().to_vec().into())
285                        .await
286                    {
287                        warn!(
288                            %error,
289                            %subject,
290                            %index,
291                            %response_subject,
292                            %index,
293                            "Failed to send stream response acknowledgement"
294                        );
295                        return;
296                    }
297                }
298            }
299        };
300        let background_task =
301            AsyncJoinOnDrop::new(tokio::spawn(ack_publisher_fut.in_current_span()), true);
302
303        Self {
304            response_subject,
305            subscriber,
306            buffered_responses: None,
307            next_index: 0,
308            acknowledgement_sender,
309            _background_task: background_task,
310            _phantom: PhantomData,
311        }
312    }
313}
314
315/// Generic one-off notification
316pub trait GenericNotification: Encode + Decode + fmt::Debug + Send + Sync + 'static {
317    /// Notification subject with optional `*` in place of application instance receiving the
318    /// request
319    const SUBJECT: &'static str;
320}
321
322/// Generic broadcast message.
323///
324/// Broadcast messages are sent by an instance to (potentially) an instance-specific subject that
325/// any other app can subscribe to. The same broadcast message can also originate from multiple
326/// places and be de-duplicated using [`Self::deterministic_message_id`].
327pub trait GenericBroadcast: Encode + Decode + fmt::Debug + Send + Sync + 'static {
328    /// Broadcast subject with optional `*` in place of application instance sending broadcast
329    const SUBJECT: &'static str;
330
331    /// Deterministic message ID that is used for de-duplicating messages broadcast by different
332    /// instances
333    fn deterministic_message_id(&self) -> Option<HeaderValue> {
334        None
335    }
336}
337
338/// Subscriber wrapper that decodes messages automatically and skips messages that can't be decoded
339#[derive(Debug)]
340#[pin_project::pin_project]
341pub struct SubscriberWrapper<Message> {
342    #[pin]
343    subscriber: Subscriber,
344    _phantom: PhantomData<Message>,
345}
346
347impl<Message> Stream for SubscriberWrapper<Message>
348where
349    Message: Decode,
350{
351    type Item = Message;
352
353    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
354        match self.project().subscriber.poll_next_unpin(cx) {
355            Poll::Ready(Some(message)) => {
356                match decode_from_bytes::<Message>(message.payload.clone()) {
357                    Ok(message) => Poll::Ready(Some(message)),
358                    Err(error) => {
359                        warn!(
360                            %error,
361                            message_type = %type_name::<Message>(),
362                            message = %hex::encode(message.payload),
363                            "Failed to decode stream message"
364                        );
365
366                        Poll::Pending
367                    }
368                }
369            }
370            Poll::Ready(None) => Poll::Ready(None),
371            Poll::Pending => Poll::Pending,
372        }
373    }
374}
375
376#[derive(Debug)]
377struct Inner {
378    client: Client,
379    request_retry_backoff_policy: ExponentialBuilder,
380    approximate_max_message_size: usize,
381    max_message_size: usize,
382}
383
384/// NATS client wrapper that can be used to interact with other cluster-specific clients
385#[derive(Debug, Clone)]
386pub struct NatsClient {
387    inner: Arc<Inner>,
388}
389
390impl Deref for NatsClient {
391    type Target = Client;
392
393    #[inline]
394    fn deref(&self) -> &Self::Target {
395        &self.inner.client
396    }
397}
398
399impl NatsClient {
400    /// Create a new instance by connecting to specified addresses
401    pub async fn new<A>(
402        addrs: A,
403        request_retry_backoff_policy: ExponentialBuilder,
404    ) -> Result<Self, async_nats::Error>
405    where
406        A: ToServerAddrs,
407    {
408        let servers = addrs.to_server_addrs()?.collect::<Vec<_>>();
409        Self::from_client(
410            async_nats::connect_with_options(
411                &servers,
412                ConnectOptions::default().request_timeout(Some(REQUEST_TIMEOUT)),
413            )
414            .await?,
415            request_retry_backoff_policy,
416        )
417    }
418
419    /// Create new client from existing NATS instance
420    pub fn from_client(
421        client: Client,
422        request_retry_backoff_policy: ExponentialBuilder,
423    ) -> Result<Self, async_nats::Error> {
424        let max_payload = client.server_info().max_payload;
425        if max_payload < EXPECTED_MESSAGE_SIZE {
426            return Err(format!(
427                "Max payload {max_payload} is smaller than expected {EXPECTED_MESSAGE_SIZE}, \
428                increase it by specifying max_payload = 2MB or higher number in NATS configuration"
429            )
430            .into());
431        }
432
433        let inner = Inner {
434            client,
435            request_retry_backoff_policy,
436            // Allow up to 90%, the rest will be wrapper data structures, etc.
437            approximate_max_message_size: max_payload * 9 / 10,
438            // Allow up to 90%, the rest will be wrapper data structures, etc.
439            max_message_size: max_payload,
440        };
441
442        Ok(Self {
443            inner: Arc::new(inner),
444        })
445    }
446
447    /// Approximate max message size (a few more bytes will not hurt), the actual limit is expected
448    /// to be a bit higher
449    pub fn approximate_max_message_size(&self) -> usize {
450        self.inner.approximate_max_message_size
451    }
452
453    /// Make request and wait for response
454    pub async fn request<Request>(
455        &self,
456        request: &Request,
457        instance: Option<&str>,
458    ) -> Result<Request::Response, RequestError>
459    where
460        Request: GenericRequest,
461    {
462        let subject = subject_with_instance(Request::SUBJECT, instance);
463        let mut maybe_retry_backoff = None;
464        let message = loop {
465            match self
466                .inner
467                .client
468                .request(subject.clone(), request.encode().into())
469                .await
470            {
471                Ok(message) => {
472                    break message;
473                }
474                Err(error) => {
475                    match error.kind() {
476                        RequestErrorKind::TimedOut | RequestErrorKind::NoResponders => {
477                            // Continue with retries
478                        }
479                        RequestErrorKind::InvalidSubject
480                        | RequestErrorKind::MaxPayloadExceeded
481                        | RequestErrorKind::Other => {
482                            return Err(error);
483                        }
484                    }
485
486                    let retry_backoff = maybe_retry_backoff
487                        .get_or_insert_with(|| self.inner.request_retry_backoff_policy.build());
488
489                    if let Some(delay) = retry_backoff.next() {
490                        debug!(
491                            %subject,
492                            %error,
493                            request_type = %type_name::<Request>(),
494                            ?delay,
495                            "Failed to make request, retrying after some delay"
496                        );
497
498                        tokio::time::sleep(delay).await;
499                    } else {
500                        return Err(error);
501                    }
502                }
503            }
504        };
505
506        let response =
507            decode_from_bytes::<Request::Response>(message.payload.clone()).map_err(|error| {
508                warn!(
509                    %subject,
510                    %error,
511                    response_type = %type_name::<Request::Response>(),
512                    response = %hex::encode(message.payload),
513                    "Response decoding failed"
514                );
515
516                RequestErrorKind::Other
517            })?;
518
519        Ok(response)
520    }
521
522    /// Responds to requests from the given subject using the provided processing function.
523    ///
524    /// This will create a subscription on the subject for the given instance (if provided) and
525    /// queue group. Incoming messages will be deserialized as the request type `Request` and passed
526    /// to the `process` function to produce a response of type `Request::Response`. The response
527    /// will then be sent back on the reply subject from the original request.
528    ///
529    /// Each request is processed in a newly created async tokio task.
530    ///
531    /// # Arguments
532    ///
533    /// * `instance` - Optional instance name to use in place of the `*` in the subject
534    /// * `group` - The queue group name for the subscription
535    /// * `process` - The function to call with the decoded request to produce a response
536    pub async fn request_responder<Request, F, OP>(
537        &self,
538        instance: Option<&str>,
539        queue_group: Option<String>,
540        process: OP,
541    ) -> anyhow::Result<()>
542    where
543        Request: GenericRequest,
544        F: Future<Output = Option<Request::Response>> + Send,
545        OP: Fn(Request) -> F + Send + Sync,
546    {
547        // Initialize with pending future so it never ends
548        let mut processing = FuturesUnordered::new();
549
550        let subscription = self
551            .common_subscribe(Request::SUBJECT, instance, queue_group)
552            .await
553            .map_err(|error| {
554                anyhow!(
555                    "Failed to subscribe to {} requests for {instance:?}: {error}",
556                    type_name::<Request>(),
557                )
558            })?;
559
560        debug!(
561            request_type = %type_name::<Request>(),
562            ?subscription,
563            "Requests subscription"
564        );
565        let mut subscription = subscription.fuse();
566
567        loop {
568            select! {
569                message = subscription.select_next_some() => {
570                    // Create background task for concurrent processing
571                    processing.push(
572                        self
573                            .process_request(
574                                message,
575                                &process,
576                            )
577                            .in_current_span(),
578                    );
579                },
580                _ = processing.next() => {
581                    // Nothing to do here
582                },
583                complete => {
584                    break;
585                }
586            }
587        }
588
589        Ok(())
590    }
591
592    async fn process_request<Request, F, OP>(&self, message: Message, process: OP)
593    where
594        Request: GenericRequest,
595        F: Future<Output = Option<Request::Response>> + Send,
596        OP: Fn(Request) -> F + Send + Sync,
597    {
598        let Some(reply_subject) = message.reply else {
599            return;
600        };
601
602        let message_payload_size = message.payload.len();
603        let request = match decode_from_bytes::<Request>(message.payload.clone()) {
604            Ok(request) => {
605                // Free allocation early
606                drop(message.payload);
607                request
608            }
609            Err(error) => {
610                warn!(
611                    request_type = %type_name::<Request>(),
612                    %error,
613                    message = %hex::encode(message.payload),
614                    "Failed to decode request"
615                );
616                return;
617            }
618        };
619
620        // Avoid printing large messages in logs
621        if message_payload_size > 1024 {
622            trace!(
623                request_type = %type_name::<Request>(),
624                %reply_subject,
625                "Processing request"
626            );
627        } else {
628            trace!(
629                request_type = %type_name::<Request>(),
630                ?request,
631                %reply_subject,
632                "Processing request"
633            );
634        }
635
636        if let Some(response) = process(request).await
637            && let Err(error) = self.publish(reply_subject, response.encode().into()).await
638        {
639            warn!(
640                request_type = %type_name::<Request>(),
641                %error,
642                "Failed to send response"
643            );
644        }
645    }
646
647    /// Make request that expects stream response
648    pub async fn stream_request<Request>(
649        &self,
650        request: &Request,
651        instance: Option<&str>,
652    ) -> Result<StreamResponseSubscriber<Request::Response>, StreamRequestError>
653    where
654        Request: GenericStreamRequest,
655    {
656        let stream_request_subject = subject_with_instance(Request::SUBJECT, instance);
657        let stream_response_subject = format!("stream-response.{}", Ulid::generate());
658
659        let subscriber = self
660            .inner
661            .client
662            .subscribe(stream_response_subject.clone())
663            .await?;
664
665        debug!(
666            request_type = %type_name::<Request>(),
667            %stream_request_subject,
668            %stream_response_subject,
669            ?subscriber,
670            "Stream request subscription"
671        );
672
673        self.inner
674            .client
675            .publish_with_reply(
676                stream_request_subject,
677                stream_response_subject.clone(),
678                request.encode().into(),
679            )
680            .await?;
681
682        Ok(StreamResponseSubscriber::new(
683            subscriber,
684            stream_response_subject,
685            self.clone(),
686        ))
687    }
688
689    /// Responds to stream requests from the given subject using the provided processing function.
690    ///
691    /// This will create a subscription on the subject for the given instance (if provided) and
692    /// queue group. Incoming messages will be deserialized as the request type `Request` and passed
693    /// to the `process` function to produce a stream response of type `Request::Response`. The
694    /// stream response will then be sent back on the reply subject from the original request.
695    ///
696    /// Each request is processed in a newly created async tokio task.
697    ///
698    /// # Arguments
699    ///
700    /// * `instance` - Optional instance name to use in place of the `*` in the subject
701    /// * `group` - The queue group name for the subscription
702    /// * `process` - The function to call with the decoded request to produce a response
703    pub async fn stream_request_responder<Request, F, S, OP>(
704        &self,
705        instance: Option<&str>,
706        queue_group: Option<String>,
707        process: OP,
708    ) -> anyhow::Result<()>
709    where
710        Request: GenericStreamRequest,
711        F: Future<Output = Option<S>> + Send,
712        S: Stream<Item = Request::Response> + Unpin,
713        OP: Fn(Request) -> F + Send + Sync,
714    {
715        // Initialize with pending future so it never ends
716        let mut processing = FuturesUnordered::new();
717
718        let subscription = self
719            .common_subscribe(Request::SUBJECT, instance, queue_group)
720            .await
721            .map_err(|error| {
722                anyhow!(
723                    "Failed to subscribe to {} stream requests for {instance:?}: {error}",
724                    type_name::<Request>(),
725                )
726            })?;
727
728        debug!(
729            request_type = %type_name::<Request>(),
730            ?subscription,
731            "Stream requests subscription"
732        );
733        let mut subscription = subscription.fuse();
734
735        loop {
736            select! {
737                message = subscription.select_next_some() => {
738                    // Create background task for concurrent processing
739                    processing.push(
740                        self
741                        .process_stream_request(
742                            message,
743                            &process,
744                        )
745                        .in_current_span(),
746                    );
747                },
748                _ = processing.next() => {
749                    // Nothing to do here
750                },
751                complete => {
752                    break;
753                }
754            }
755        }
756
757        Ok(())
758    }
759
760    async fn process_stream_request<Request, F, S, OP>(&self, message: Message, process: OP)
761    where
762        Request: GenericStreamRequest,
763        F: Future<Output = Option<S>> + Send,
764        S: Stream<Item = Request::Response> + Unpin,
765        OP: Fn(Request) -> F + Send + Sync,
766    {
767        let Some(reply_subject) = message.reply else {
768            return;
769        };
770
771        let message_payload_size = message.payload.len();
772        let request = match decode_from_bytes::<Request>(message.payload.clone()) {
773            Ok(request) => {
774                // Free allocation early
775                drop(message.payload);
776                request
777            }
778            Err(error) => {
779                warn!(
780                    request_type = %type_name::<Request>(),
781                    %error,
782                    message = %hex::encode(message.payload),
783                    "Failed to decode request"
784                );
785                return;
786            }
787        };
788
789        // Avoid printing large messages in logs
790        if message_payload_size > 1024 {
791            trace!(
792                request_type = %type_name::<Request>(),
793                %reply_subject,
794                "Processing request"
795            );
796        } else {
797            trace!(
798                request_type = %type_name::<Request>(),
799                ?request,
800                %reply_subject,
801                "Processing request"
802            );
803        }
804
805        if let Some(stream) = process(request).await {
806            self.stream_response::<Request, _>(reply_subject, stream)
807                .await;
808        }
809    }
810
811    /// Helper method to send responses to requests initiated with [`Self::stream_request`]
812    async fn stream_response<Request, S>(&self, response_subject: Subject, response_stream: S)
813    where
814        Request: GenericStreamRequest,
815        S: Stream<Item = Request::Response> + Unpin,
816    {
817        type Response<Request> =
818            GenericStreamResponses<<Request as GenericStreamRequest>::Response>;
819
820        let mut response_stream = response_stream.fuse();
821
822        // Pull the first element to measure response size
823        let Some(first_element) = response_stream.next().await else {
824            if let Err(error) = self
825                .publish(
826                    response_subject.clone(),
827                    Response::<Request>::Last {
828                        index: 0,
829                        responses: VecDeque::new(),
830                    }
831                    .encode()
832                    .into(),
833                )
834                .await
835            {
836                warn!(
837                    %response_subject,
838                    %error,
839                    request_type = %type_name::<Request>(),
840                    response_type = %type_name::<Request::Response>(),
841                    "Failed to send stream response"
842                );
843            }
844
845            return;
846        };
847        let max_message_size = self.inner.max_message_size;
848        let approximate_max_message_size = self.approximate_max_message_size();
849        let max_responses_per_message = approximate_max_message_size / first_element.encoded_size();
850
851        let ack_subject = format!("stream-response-ack.{}", Ulid::generate());
852        let mut ack_subscription = match self.subscribe(ack_subject.clone()).await {
853            Ok(ack_subscription) => ack_subscription,
854            Err(error) => {
855                warn!(
856                    %response_subject,
857                    %error,
858                    request_type = %type_name::<Request>(),
859                    response_type = %type_name::<Request::Response>(),
860                    "Failed to subscribe to ack subject"
861                );
862                return;
863            }
864        };
865        debug!(
866            %response_subject,
867            request_type = %type_name::<Request>(),
868            response_type = %type_name::<Request::Response>(),
869            ?ack_subscription,
870            "Ack subscription subscription"
871        );
872        let mut index = 0;
873        // Initialize buffer that will be reused for responses
874        let mut buffer = VecDeque::with_capacity(max_responses_per_message);
875        buffer.push_back(first_element);
876        let mut overflow_buffer = VecDeque::new();
877
878        loop {
879            // Try to fill the buffer
880            if buffer.is_empty()
881                && let Some(element) = response_stream.next().await
882            {
883                buffer.push_back(element);
884            }
885            // Tracked incrementally, encoding the whole buffer for every added element is
886            // quadratic. Growth of the length prefix is not accounted for, but the size is
887            // approximate anyway and the real one is checked below.
888            let mut buffer_encoded_size = buffer.encoded_size();
889            while buffer_encoded_size < approximate_max_message_size
890                && let Some(element) = response_stream.next().now_or_never().flatten()
891            {
892                buffer_encoded_size += element.encoded_size();
893                buffer.push_back(element);
894            }
895
896            loop {
897                let is_done = response_stream.is_done() && overflow_buffer.is_empty();
898                let num_messages = buffer.len();
899                let response = if is_done {
900                    Response::<Request>::Last {
901                        index,
902                        responses: buffer,
903                    }
904                } else {
905                    Response::<Request>::Continue {
906                        index,
907                        responses: buffer,
908                        ack_subject: ack_subject.clone(),
909                    }
910                };
911                let encoded_response = response.encode();
912                let encoded_response_len = encoded_response.len();
913                // When encoded response is too large, remove one of the responses from it and try
914                // again
915                if encoded_response_len > max_message_size {
916                    buffer = response.into();
917                    if let Some(element) = buffer.pop_back() {
918                        if buffer.is_empty() {
919                            error!(
920                                ?element,
921                                encoded_response_len,
922                                max_message_size,
923                                "Element was too large to fit into NATS message, this is an \
924                                implementation bug"
925                            );
926                        }
927                        overflow_buffer.push_front(element);
928                        continue;
929                    }
930
931                    error!(
932                        %response_subject,
933                        request_type = %type_name::<Request>(),
934                        response_type = %type_name::<Request::Response>(),
935                        "Empty response overflown message size, this should never happen"
936                    );
937                    return;
938                }
939
940                debug!(
941                    %response_subject,
942                    num_messages,
943                    %index,
944                    %is_done,
945                    "Publishing stream response messages",
946                );
947
948                if let Err(error) = self
949                    .publish(response_subject.clone(), encoded_response.into())
950                    .await
951                {
952                    warn!(
953                        %response_subject,
954                        %error,
955                        request_type = %type_name::<Request>(),
956                        response_type = %type_name::<Request::Response>(),
957                        "Failed to send stream response"
958                    );
959                    return;
960                }
961
962                if is_done {
963                    return;
964                }
965
966                buffer = response.into();
967                buffer.clear();
968                // Fill buffer with any overflown responses that may have been stored
969                buffer.append(&mut overflow_buffer);
970
971                if index >= 1 {
972                    // Acknowledgements are received with delay
973                    let expected_index = index - 1;
974
975                    trace!(
976                        %response_subject,
977                        %expected_index,
978                        "Waiting for acknowledgement"
979                    );
980                    match tokio::time::timeout(ACKNOWLEDGEMENT_TIMEOUT, ack_subscription.next())
981                        .await
982                    {
983                        Ok(Some(message)) => {
984                            if let Some(received_index) =
985                                message.payload.get(..size_of::<u32>()).map(|bytes| {
986                                    u32::from_le_bytes(
987                                        bytes.try_into().expect("Correctly chunked slice; qed"),
988                                    )
989                                })
990                            {
991                                debug!(
992                                    %response_subject,
993                                    %received_index,
994                                    "Received acknowledgement"
995                                );
996                                if received_index != expected_index {
997                                    warn!(
998                                        %response_subject,
999                                        %received_index,
1000                                        %expected_index,
1001                                        request_type = %type_name::<Request>(),
1002                                        response_type = %type_name::<Request::Response>(),
1003                                        message = %hex::encode(message.payload),
1004                                        "Unexpected acknowledgement index"
1005                                    );
1006                                    return;
1007                                }
1008                            } else {
1009                                warn!(
1010                                    %response_subject,
1011                                    request_type = %type_name::<Request>(),
1012                                    response_type = %type_name::<Request::Response>(),
1013                                    message = %hex::encode(message.payload),
1014                                    "Unexpected acknowledgement message"
1015                                );
1016                                return;
1017                            }
1018                        }
1019                        Ok(None) => {
1020                            warn!(
1021                                %response_subject,
1022                                request_type = %type_name::<Request>(),
1023                                response_type = %type_name::<Request::Response>(),
1024                                "Acknowledgement stream ended unexpectedly"
1025                            );
1026                            return;
1027                        }
1028                        Err(_error) => {
1029                            warn!(
1030                                %response_subject,
1031                                %expected_index,
1032                                request_type = %type_name::<Request>(),
1033                                response_type = %type_name::<Request::Response>(),
1034                                "Acknowledgement wait timed out"
1035                            );
1036                            return;
1037                        }
1038                    }
1039                }
1040
1041                index += 1;
1042
1043                // Unless `overflow_buffer` wasn't empty abort inner loop
1044                if buffer.is_empty() {
1045                    break;
1046                }
1047            }
1048        }
1049    }
1050
1051    /// Make notification without waiting for response
1052    pub async fn notification<Notification>(
1053        &self,
1054        notification: &Notification,
1055        instance: Option<&str>,
1056    ) -> Result<(), PublishError>
1057    where
1058        Notification: GenericNotification,
1059    {
1060        self.inner
1061            .client
1062            .publish(
1063                subject_with_instance(Notification::SUBJECT, instance),
1064                notification.encode().into(),
1065            )
1066            .await
1067    }
1068
1069    /// Send a broadcast message
1070    pub async fn broadcast<Broadcast>(
1071        &self,
1072        message: &Broadcast,
1073        instance: &str,
1074    ) -> Result<(), PublishError>
1075    where
1076        Broadcast: GenericBroadcast,
1077    {
1078        self.inner
1079            .client
1080            .publish_with_headers(
1081                Broadcast::SUBJECT.replace('*', instance),
1082                {
1083                    let mut headers = HeaderMap::new();
1084                    if let Some(message_id) = message.deterministic_message_id() {
1085                        headers.insert("Nats-Msg-Id", message_id);
1086                    }
1087                    headers
1088                },
1089                message.encode().into(),
1090            )
1091            .await
1092    }
1093
1094    /// Simple subscription that will produce decoded notifications, while skipping messages that
1095    /// fail to decode
1096    pub async fn subscribe_to_notifications<Notification>(
1097        &self,
1098        instance: Option<&str>,
1099        queue_group: Option<String>,
1100    ) -> Result<SubscriberWrapper<Notification>, SubscribeError>
1101    where
1102        Notification: GenericNotification,
1103    {
1104        self.simple_subscribe(Notification::SUBJECT, instance, queue_group)
1105            .await
1106    }
1107
1108    /// Simple subscription that will produce decoded broadcasts, while skipping messages that
1109    /// fail to decode
1110    pub async fn subscribe_to_broadcasts<Broadcast>(
1111        &self,
1112        instance: Option<&str>,
1113        queue_group: Option<String>,
1114    ) -> Result<SubscriberWrapper<Broadcast>, SubscribeError>
1115    where
1116        Broadcast: GenericBroadcast,
1117    {
1118        self.simple_subscribe(Broadcast::SUBJECT, instance, queue_group)
1119            .await
1120    }
1121
1122    /// Simple subscription that will produce decoded messages, while skipping messages that fail to
1123    /// decode
1124    async fn simple_subscribe<Message>(
1125        &self,
1126        subject: &'static str,
1127        instance: Option<&str>,
1128        queue_group: Option<String>,
1129    ) -> Result<SubscriberWrapper<Message>, SubscribeError>
1130    where
1131        Message: Decode,
1132    {
1133        let subscriber = self
1134            .common_subscribe(subject, instance, queue_group)
1135            .await?;
1136        debug!(
1137            %subject,
1138            message_type = %type_name::<Message>(),
1139            ?subscriber,
1140            "Simple subscription"
1141        );
1142
1143        Ok(SubscriberWrapper {
1144            subscriber,
1145            _phantom: PhantomData,
1146        })
1147    }
1148
1149    /// Simple subscription that will produce decoded messages, while skipping messages that fail to
1150    /// decode
1151    async fn common_subscribe(
1152        &self,
1153        subject: &'static str,
1154        instance: Option<&str>,
1155        queue_group: Option<String>,
1156    ) -> Result<Subscriber, SubscribeError> {
1157        let subscriber = if let Some(queue_group) = queue_group {
1158            self.inner
1159                .client
1160                .queue_subscribe(subject_with_instance(subject, instance), queue_group)
1161                .await?
1162        } else {
1163            self.inner
1164                .client
1165                .subscribe(subject_with_instance(subject, instance))
1166                .await?
1167        };
1168
1169        Ok(subscriber)
1170    }
1171}
1172
1173fn subject_with_instance(subject: &'static str, instance: Option<&str>) -> Subject {
1174    if let Some(instance) = instance {
1175        Subject::from(subject.replace('*', instance))
1176    } else {
1177        Subject::from_static(subject)
1178    }
1179}