1use std::collections::{HashMap, VecDeque};
5use std::sync::atomic::AtomicBool;
6use std::sync::Arc;
7use std::time::Duration;
8
9use parking_lot::RwLock;
10use tokio::sync::mpsc;
11use tokio::time::{sleep, Instant};
12
13use super::subscriber::{ActiveSubscription, SubscriptionRequest};
14use super::{Error, Event, Pubsub, Spec};
15use crate::task::spawn;
16
17const STREAM_CONNECTION_BACKOFF: Duration = Duration::from_millis(2_000);
18
19const STREAM_CONNECTION_MAX_BACKOFF: Duration = Duration::from_millis(30_000);
20
21const INTERNAL_POLL_SIZE: usize = 1_000;
22
23const POLL_SLEEP: Duration = Duration::from_millis(2_000);
24
25struct UniqueSubscription<S>
26where
27 S: Spec,
28{
29 name: S::SubscriptionId,
30 total_subscribers: usize,
31}
32
33type UniqueSubscriptions<S> = Arc<RwLock<HashMap<<S as Spec>::Topic, UniqueSubscription<S>>>>;
34
35type ActiveSubscriptions<S> =
36 RwLock<HashMap<Arc<<S as Spec>::SubscriptionId>, Vec<<S as Spec>::Topic>>>;
37
38type CacheEvent<S> = HashMap<<<S as Spec>::Event as Event>::Topic, <S as Spec>::Event>;
39
40#[allow(missing_debug_implementations)]
42pub struct Consumer<T>
43where
44 T: Transport + 'static,
45{
46 transport: T,
47 inner_pubsub: Arc<Pubsub<T::Spec>>,
48 remote_subscriptions: UniqueSubscriptions<T::Spec>,
49 subscriptions: ActiveSubscriptions<T::Spec>,
50 stream_ctrl: RwLock<Option<mpsc::Sender<StreamCtrl<T::Spec>>>>,
51 still_running: AtomicBool,
52 prefer_polling: bool,
53 cached_events: Arc<RwLock<CacheEvent<T::Spec>>>,
59}
60
61#[allow(missing_debug_implementations)]
63pub struct RemoteActiveConsumer<T>
64where
65 T: Transport + 'static,
66{
67 inner: ActiveSubscription<T::Spec>,
68 previous_messages: VecDeque<<T::Spec as Spec>::Event>,
69 consumer: Arc<Consumer<T>>,
70}
71
72impl<T> RemoteActiveConsumer<T>
73where
74 T: Transport + 'static,
75{
76 pub async fn recv(&mut self) -> Option<<T::Spec as Spec>::Event> {
78 if let Some(event) = self.previous_messages.pop_front() {
79 Some(event)
80 } else {
81 self.inner.recv().await
82 }
83 }
84
85 pub fn try_recv(&mut self) -> Option<<T::Spec as Spec>::Event> {
87 if let Some(event) = self.previous_messages.pop_front() {
88 Some(event)
89 } else {
90 self.inner.try_recv()
91 }
92 }
93
94 pub fn name(&self) -> &<T::Spec as Spec>::SubscriptionId {
96 self.inner.name()
97 }
98}
99
100impl<T> Drop for RemoteActiveConsumer<T>
101where
102 T: Transport + 'static,
103{
104 fn drop(&mut self) {
105 let _ = self.consumer.unsubscribe(self.name().clone());
106 }
107}
108
109#[allow(missing_debug_implementations)]
112pub struct InternalRelay<S>
113where
114 S: Spec + 'static,
115{
116 inner: Arc<Pubsub<S>>,
117 remote_subscriptions: UniqueSubscriptions<S>,
118 cached_events: Arc<RwLock<CacheEvent<S>>>,
119}
120
121impl<S> InternalRelay<S>
122where
123 S: Spec + 'static,
124{
125 pub fn send<X>(&self, event: X)
127 where
128 X: Into<S::Event>,
129 {
130 let event = event.into();
131
132 {
133 let active_topics = self.remote_subscriptions.read();
134 let mut cached_events = self.cached_events.write();
135
136 for topic in event.get_topics() {
137 if active_topics.contains_key(&topic) {
138 cached_events.insert(topic, event.clone());
139 }
140 }
141 }
142
143 self.inner.publish(event);
144 }
145}
146
147impl<T> Consumer<T>
148where
149 T: Transport + 'static,
150{
151 pub fn new(
153 transport: T,
154 prefer_polling: bool,
155 context: <T::Spec as Spec>::Context,
156 ) -> Arc<Self> {
157 let this = Arc::new(Self {
158 transport,
159 prefer_polling,
160 inner_pubsub: Arc::new(Pubsub::new(T::Spec::new_instance(context))),
161 subscriptions: Default::default(),
162 remote_subscriptions: Default::default(),
163 stream_ctrl: RwLock::new(None),
164 cached_events: Default::default(),
165 still_running: true.into(),
166 });
167
168 spawn(Self::stream(this.clone()));
169
170 this
171 }
172
173 async fn stream(instance: Arc<Self>) {
174 let mut stream_supported = true;
175 let mut poll_supported = true;
176
177 let mut backoff = STREAM_CONNECTION_BACKOFF;
178 let mut retry_at = None;
179
180 loop {
181 if (!stream_supported && !poll_supported)
182 || !instance
183 .still_running
184 .load(std::sync::atomic::Ordering::Relaxed)
185 {
186 break;
187 }
188
189 if instance.remote_subscriptions.read().is_empty() {
190 sleep(Duration::from_millis(100)).await;
191 continue;
192 }
193
194 if stream_supported
195 && !instance.prefer_polling
196 && retry_at
197 .map(|retry_at| retry_at < Instant::now())
198 .unwrap_or(true)
199 {
200 let (sender, receiver) = mpsc::channel(INTERNAL_POLL_SIZE);
201
202 {
203 *instance.stream_ctrl.write() = Some(sender);
204 }
205
206 let current_subscriptions = {
207 instance
208 .remote_subscriptions
209 .read()
210 .iter()
211 .map(|(key, name)| (name.name.clone(), key.clone()))
212 .collect::<Vec<_>>()
213 };
214
215 if let Err(err) = instance
216 .transport
217 .stream(
218 receiver,
219 current_subscriptions,
220 InternalRelay {
221 inner: instance.inner_pubsub.clone(),
222 remote_subscriptions: instance.remote_subscriptions.clone(),
223 cached_events: instance.cached_events.clone(),
224 },
225 )
226 .await
227 {
228 if matches!(&err, Error::NotSupported | Error::Terminal(_)) {
229 stream_supported = false;
230 } else {
231 retry_at = Some(Instant::now() + backoff);
232 backoff = backoff.saturating_mul(2).min(STREAM_CONNECTION_MAX_BACKOFF);
233 }
234 tracing::error!("Long connection failed with error {:?}", err);
235 } else {
236 backoff = STREAM_CONNECTION_BACKOFF;
237 }
238
239 let _ = instance.stream_ctrl.write().take();
241 }
242
243 if poll_supported {
244 let current_subscriptions = {
245 instance
246 .remote_subscriptions
247 .read()
248 .iter()
249 .map(|(key, name)| (name.name.clone(), key.clone()))
250 .collect::<Vec<_>>()
251 };
252
253 if let Err(err) = instance
254 .transport
255 .poll(
256 current_subscriptions,
257 InternalRelay {
258 inner: instance.inner_pubsub.clone(),
259 remote_subscriptions: instance.remote_subscriptions.clone(),
260 cached_events: instance.cached_events.clone(),
261 },
262 )
263 .await
264 {
265 if matches!(&err, Error::NotSupported | Error::Terminal(_)) {
266 poll_supported = false;
267 }
268 tracing::error!("Polling failed with error {:?}", err);
269 }
270
271 sleep(POLL_SLEEP).await;
272 }
273 }
274 }
275
276 fn unsubscribe(
279 self: &Arc<Self>,
280 subscription_name: <T::Spec as Spec>::SubscriptionId,
281 ) -> Result<(), Error> {
282 let topics = self
283 .subscriptions
284 .write()
285 .remove(&subscription_name)
286 .ok_or(Error::NoSubscription)?;
287
288 let mut remote_subscriptions = self.remote_subscriptions.write();
289
290 for topic in topics {
291 let mut remote_subscription =
292 if let Some(remote_subscription) = remote_subscriptions.remove(&topic) {
293 remote_subscription
294 } else {
295 continue;
296 };
297
298 remote_subscription.total_subscribers =
299 remote_subscription.total_subscribers.saturating_sub(1);
300
301 if remote_subscription.total_subscribers == 0 {
302 let mut cached_events = self.cached_events.write();
303
304 cached_events.remove(&topic);
305
306 self.message_to_stream(StreamCtrl::Unsubscribe(remote_subscription.name.clone()))?;
307 } else {
308 remote_subscriptions.insert(topic, remote_subscription);
309 }
310 }
311
312 if remote_subscriptions.is_empty() {
313 self.cached_events.write().clear();
314 self.message_to_stream(StreamCtrl::Stop)?;
315 }
316
317 Ok(())
318 }
319
320 #[inline(always)]
321 fn message_to_stream(&self, message: StreamCtrl<T::Spec>) -> Result<(), Error> {
322 let to_stream = self.stream_ctrl.read();
323
324 if let Some(to_stream) = to_stream.as_ref() {
325 Ok(to_stream.try_send(message)?)
326 } else {
327 Ok(())
328 }
329 }
330
331 pub fn subscribe<I>(self: &Arc<Self>, request: I) -> Result<RemoteActiveConsumer<T>, Error>
342 where
343 I: SubscriptionRequest<
344 Topic = <T::Spec as Spec>::Topic,
345 SubscriptionId = <T::Spec as Spec>::SubscriptionId,
346 >,
347 {
348 let subscription_name = request.subscription_name();
349 let topics = request.try_get_topics()?;
350
351 let mut remote_subscriptions = self.remote_subscriptions.write();
352 let mut subscriptions = self.subscriptions.write();
353
354 if subscriptions.get(&subscription_name).is_some() {
355 return Err(Error::NoSubscription);
356 }
357
358 let mut previous_messages = Vec::new();
359 let cached_events = self.cached_events.read();
360
361 for topic in topics.iter() {
362 if let Some(subscription) = remote_subscriptions.get_mut(topic) {
363 subscription.total_subscribers += 1;
364
365 if let Some(v) = cached_events.get(topic).cloned() {
366 previous_messages.push(v);
367 }
368 } else {
369 let internal_sub_name = self.transport.new_name();
370 remote_subscriptions.insert(
371 topic.clone(),
372 UniqueSubscription {
373 total_subscribers: 1,
374 name: internal_sub_name.clone(),
375 },
376 );
377
378 self.message_to_stream(StreamCtrl::Subscribe((internal_sub_name, topic.clone())))?;
380 }
381 }
382
383 subscriptions.insert(subscription_name, topics);
384 drop(subscriptions);
385
386 Ok(RemoteActiveConsumer {
387 inner: self.inner_pubsub.subscribe(request)?,
388 previous_messages: previous_messages.into(),
389 consumer: self.clone(),
390 })
391 }
392}
393
394impl<T> Drop for Consumer<T>
395where
396 T: Transport + 'static,
397{
398 fn drop(&mut self) {
399 self.still_running
400 .store(false, std::sync::atomic::Ordering::Release);
401 if let Some(to_stream) = self.stream_ctrl.read().as_ref() {
402 let _ = to_stream.try_send(StreamCtrl::Stop).inspect_err(|err| {
403 tracing::error!("Failed to send message LongPoll::Stop due to {err:?}")
404 });
405 }
406 }
407}
408
409pub type SubscribeMessage<S> = (<S as Spec>::SubscriptionId, <S as Spec>::Topic);
411
412#[allow(missing_debug_implementations)]
414pub enum StreamCtrl<S>
415where
416 S: Spec + 'static,
417{
418 Subscribe(SubscribeMessage<S>),
420 Unsubscribe(S::SubscriptionId),
422 Stop,
424}
425
426impl<S> Clone for StreamCtrl<S>
427where
428 S: Spec + 'static,
429{
430 fn clone(&self) -> Self {
431 match self {
432 Self::Subscribe(s) => Self::Subscribe(s.clone()),
433 Self::Unsubscribe(u) => Self::Unsubscribe(u.clone()),
434 Self::Stop => Self::Stop,
435 }
436 }
437}
438
439#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
457#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
458pub trait Transport: Send + Sync {
459 type Spec: Spec;
461
462 fn new_name(&self) -> <Self::Spec as Spec>::SubscriptionId;
464
465 async fn stream(
468 &self,
469 subscribe_changes: mpsc::Receiver<StreamCtrl<Self::Spec>>,
470 topics: Vec<SubscribeMessage<Self::Spec>>,
471 reply_to: InternalRelay<Self::Spec>,
472 ) -> Result<(), Error>;
473
474 async fn poll(
477 &self,
478 topics: Vec<SubscribeMessage<Self::Spec>>,
479 reply_to: InternalRelay<Self::Spec>,
480 ) -> Result<(), Error>;
481}
482
483#[cfg(test)]
484mod tests {
485 use std::sync::atomic::{AtomicUsize, Ordering};
486 use std::sync::Arc;
487
488 use tokio::sync::{mpsc, Mutex};
489 use tokio::time::{timeout, Duration};
490
491 use super::{
492 InternalRelay, RemoteActiveConsumer, StreamCtrl, SubscribeMessage, Transport,
493 INTERNAL_POLL_SIZE,
494 };
495 use crate::pub_sub::remote_consumer::Consumer;
496 use crate::pub_sub::test::{CustomPubSub, IndexTest, Message};
497 use crate::pub_sub::{Error, Spec, SubscriptionRequest};
498
499 #[derive(Clone, Debug)]
502 enum SubscriptionReq {
503 Foo(String, u64),
504 Bar(String, u64),
505 }
506
507 impl SubscriptionRequest for SubscriptionReq {
508 type Topic = IndexTest;
509
510 type SubscriptionId = String;
511
512 fn try_get_topics(&self) -> Result<Vec<Self::Topic>, Error> {
513 Ok(vec![match self {
514 SubscriptionReq::Foo(_, n) => IndexTest::Foo(*n),
515 SubscriptionReq::Bar(_, n) => IndexTest::Bar(*n),
516 }])
517 }
518
519 fn subscription_name(&self) -> Arc<Self::SubscriptionId> {
520 Arc::new(match self {
521 SubscriptionReq::Foo(n, _) => n.to_string(),
522 SubscriptionReq::Bar(n, _) => n.to_string(),
523 })
524 }
525 }
526
527 struct TestTransport {
532 name_ctr: AtomicUsize,
533 observe_ctrl_tx: mpsc::Sender<StreamCtrl<CustomPubSub>>,
535 support_long: bool,
537 support_poll: bool,
538 rx: Mutex<mpsc::Receiver<Message>>,
539 }
540
541 struct FailingStreamTransport {
542 name_ctr: AtomicUsize,
543 attempts: Arc<AtomicUsize>,
544 terminal: bool,
545 }
546
547 impl TestTransport {
548 fn new(
549 support_long: bool,
550 support_poll: bool,
551 ) -> (
552 Self,
553 mpsc::Sender<Message>,
554 mpsc::Receiver<StreamCtrl<CustomPubSub>>,
555 ) {
556 let (events_tx, rx) = mpsc::channel::<Message>(INTERNAL_POLL_SIZE);
557 let (observe_ctrl_tx, observe_ctrl_rx) =
558 mpsc::channel::<StreamCtrl<_>>(INTERNAL_POLL_SIZE);
559
560 let t = TestTransport {
561 name_ctr: AtomicUsize::new(1),
562 rx: Mutex::new(rx),
563 observe_ctrl_tx,
564 support_long,
565 support_poll,
566 };
567
568 (t, events_tx, observe_ctrl_rx)
569 }
570 }
571
572 impl FailingStreamTransport {
573 fn new(terminal: bool) -> (Self, Arc<AtomicUsize>) {
574 let attempts = Arc::new(AtomicUsize::new(0));
575 (
576 Self {
577 name_ctr: AtomicUsize::new(1),
578 attempts: attempts.clone(),
579 terminal,
580 },
581 attempts,
582 )
583 }
584 }
585
586 #[async_trait::async_trait]
587 impl Transport for TestTransport {
588 type Spec = CustomPubSub;
589
590 fn new_name(&self) -> <Self::Spec as Spec>::SubscriptionId {
591 format!("sub-{}", self.name_ctr.fetch_add(1, Ordering::Relaxed))
592 }
593
594 async fn stream(
595 &self,
596 mut subscribe_changes: mpsc::Receiver<StreamCtrl<Self::Spec>>,
597 topics: Vec<SubscribeMessage<Self::Spec>>,
598 reply_to: InternalRelay<Self::Spec>,
599 ) -> Result<(), Error> {
600 if !self.support_long {
601 return Err(Error::NotSupported);
602 }
603
604 let mut rx = self.rx.lock().await;
606 let observe = self.observe_ctrl_tx.clone();
607
608 for topic in topics {
609 observe.try_send(StreamCtrl::Subscribe(topic)).unwrap();
610 }
611
612 loop {
613 tokio::select! {
614 Some(ctrl) = subscribe_changes.recv() => {
616 observe.try_send(ctrl.clone()).unwrap();
617 if matches!(ctrl, StreamCtrl::Stop) {
618 break;
619 }
620 }
621 Some(msg) = rx.recv() => {
623 reply_to.send(msg);
624 }
625 }
626 }
627
628 Ok(())
629 }
630
631 async fn poll(
632 &self,
633 _topics: Vec<SubscribeMessage<Self::Spec>>,
634 reply_to: InternalRelay<Self::Spec>,
635 ) -> Result<(), Error> {
636 if !self.support_poll {
637 return Err(Error::NotSupported);
638 }
639
640 let mut rx = self.rx.lock().await;
643 for _ in 0..32 {
645 match rx.try_recv() {
646 Ok(msg) => reply_to.send(msg),
647 Err(mpsc::error::TryRecvError::Empty) => continue,
648 Err(mpsc::error::TryRecvError::Disconnected) => break,
649 }
650 }
651 Ok(())
652 }
653 }
654
655 #[async_trait::async_trait]
656 impl Transport for FailingStreamTransport {
657 type Spec = CustomPubSub;
658
659 fn new_name(&self) -> <Self::Spec as Spec>::SubscriptionId {
660 format!("sub-{}", self.name_ctr.fetch_add(1, Ordering::Relaxed))
661 }
662
663 async fn stream(
664 &self,
665 _subscribe_changes: mpsc::Receiver<StreamCtrl<Self::Spec>>,
666 _topics: Vec<SubscribeMessage<Self::Spec>>,
667 _reply_to: InternalRelay<Self::Spec>,
668 ) -> Result<(), Error> {
669 self.attempts.fetch_add(1, Ordering::Relaxed);
670 match self.terminal {
671 true => Err(Error::Terminal("permanent failure".to_string())),
672 false => Err(Error::InternalStr("temporary failure".to_string())),
673 }
674 }
675
676 async fn poll(
677 &self,
678 _topics: Vec<SubscribeMessage<Self::Spec>>,
679 _reply_to: InternalRelay<Self::Spec>,
680 ) -> Result<(), Error> {
681 Ok(())
682 }
683 }
684
685 async fn recv_next<T: Transport>(
688 sub: &mut RemoteActiveConsumer<T>,
689 dur_ms: u64,
690 ) -> Option<<T::Spec as Spec>::Event> {
691 timeout(Duration::from_millis(dur_ms), sub.recv())
692 .await
693 .ok()
694 .flatten()
695 }
696
697 async fn expect_ctrl(
698 rx: &mut mpsc::Receiver<StreamCtrl<CustomPubSub>>,
699 dur_ms: u64,
700 pred: impl Fn(&StreamCtrl<CustomPubSub>) -> bool,
701 ) -> StreamCtrl<CustomPubSub> {
702 timeout(Duration::from_millis(dur_ms), async {
703 loop {
704 if let Some(msg) = rx.recv().await {
705 if pred(&msg) {
706 break msg;
707 }
708 }
709 }
710 })
711 .await
712 .expect("timed out waiting for control message")
713 }
714
715 async fn wait_for_attempts(attempts: &AtomicUsize, expected: usize) {
716 for _ in 0..20 {
717 if attempts.load(Ordering::Relaxed) >= expected {
718 return;
719 }
720 tokio::task::yield_now().await;
721 }
722 panic!("timed out waiting for {expected} stream attempts");
723 }
724
725 #[tokio::test]
728 async fn stream_delivery_and_unsubscribe_on_drop() {
729 let (transport, events_tx, mut ctrl_rx) = TestTransport::new(true, true);
731
732 let consumer = Consumer::new(transport, false, ());
734
735 let mut sub = consumer
737 .subscribe(SubscriptionReq::Foo("t".to_owned(), 7))
738 .expect("subscribe ok");
739
740 let ctrl = expect_ctrl(
742 &mut ctrl_rx,
743 1000,
744 |m| matches!(m, StreamCtrl::Subscribe((_, idx)) if *idx == IndexTest::Foo(7)),
745 )
746 .await;
747 match ctrl {
748 StreamCtrl::Subscribe((name, idx)) => {
749 assert_ne!(name, "t".to_owned());
750 assert_eq!(idx, IndexTest::Foo(7));
751 }
752 _ => unreachable!(),
753 }
754
755 events_tx.send(Message { foo: 7, bar: 1 }).await.unwrap();
757 let got = recv_next::<TestTransport>(&mut sub, 1000)
758 .await
759 .expect("got event");
760 assert_eq!(got, Message { foo: 7, bar: 1 });
761
762 drop(sub);
764 let _ctrl = expect_ctrl(&mut ctrl_rx, 1000, |m| {
765 matches!(m, StreamCtrl::Unsubscribe(_))
766 })
767 .await;
768
769 drop(consumer);
771 let _ = expect_ctrl(&mut ctrl_rx, 1000, |m| matches!(m, StreamCtrl::Stop)).await;
772 }
773
774 #[tokio::test]
775 async fn test_cache_and_invalation() {
776 let (transport, events_tx, mut ctrl_rx) = TestTransport::new(true, true);
778
779 let consumer = Consumer::new(transport, false, ());
781
782 let mut sub_1 = consumer
784 .subscribe(SubscriptionReq::Foo("t".to_owned(), 7))
785 .expect("subscribe ok");
786
787 let ctrl = expect_ctrl(
789 &mut ctrl_rx,
790 1000,
791 |m| matches!(m, StreamCtrl::Subscribe((_, idx)) if *idx == IndexTest::Foo(7)),
792 )
793 .await;
794 match ctrl {
795 StreamCtrl::Subscribe((name, idx)) => {
796 assert_ne!(name, "t1".to_owned());
797 assert_eq!(idx, IndexTest::Foo(7));
798 }
799 _ => unreachable!(),
800 }
801
802 events_tx.send(Message { foo: 7, bar: 1 }).await.unwrap();
804 let got = recv_next::<TestTransport>(&mut sub_1, 1000)
805 .await
806 .expect("got event");
807 assert_eq!(got, Message { foo: 7, bar: 1 });
808
809 let mut sub_2 = consumer
811 .subscribe(SubscriptionReq::Foo("t2".to_owned(), 7))
812 .expect("subscribe ok");
813
814 let got = recv_next::<TestTransport>(&mut sub_2, 1000)
815 .await
816 .expect("got event");
817 assert_eq!(got, Message { foo: 7, bar: 1 });
818
819 drop(sub_1);
821
822 let mut sub_3 = consumer
824 .subscribe(SubscriptionReq::Foo("t3".to_owned(), 7))
825 .expect("subscribe ok");
826
827 let got = recv_next::<TestTransport>(&mut sub_3, 1000)
829 .await
830 .expect("got event");
831 assert_eq!(got, Message { foo: 7, bar: 1 });
832
833 events_tx.send(Message { foo: 7, bar: 2 }).await.unwrap();
835
836 let got = recv_next::<TestTransport>(&mut sub_2, 1000)
838 .await
839 .expect("got event");
840 assert_eq!(got, Message { foo: 7, bar: 2 });
841
842 let got = recv_next::<TestTransport>(&mut sub_3, 1000)
843 .await
844 .expect("got event");
845 assert_eq!(got, Message { foo: 7, bar: 2 });
846
847 drop(sub_2);
848 drop(sub_3);
849
850 let _ctrl = expect_ctrl(&mut ctrl_rx, 1000, |m| {
851 matches!(m, StreamCtrl::Unsubscribe(_))
852 })
853 .await;
854
855 let mut sub_4 = consumer
857 .subscribe(SubscriptionReq::Foo("t4".to_owned(), 7))
858 .expect("subscribe ok");
859
860 assert!(
861 recv_next::<TestTransport>(&mut sub_4, 1000).await.is_none(),
862 "Should have not receive any update"
863 );
864
865 drop(sub_4);
866
867 let _ = expect_ctrl(&mut ctrl_rx, 2000, |m| matches!(m, StreamCtrl::Stop)).await;
869 }
870
871 #[tokio::test]
872 async fn cache_ignores_orphan_event_topics() {
873 let (transport, events_tx, mut ctrl_rx) = TestTransport::new(true, true);
874 let consumer = Consumer::new(transport, false, ());
875
876 let mut sub = consumer
877 .subscribe(SubscriptionReq::Foo("t".to_owned(), 7))
878 .expect("subscribe ok");
879 let _ = expect_ctrl(&mut ctrl_rx, 1000, |m| {
880 matches!(m, StreamCtrl::Subscribe(_))
881 })
882 .await;
883
884 events_tx.send(Message { foo: 7, bar: 99 }).await.unwrap();
885 let _ = recv_next::<TestTransport>(&mut sub, 1000)
886 .await
887 .expect("got event");
888
889 drop(sub);
890 let _ = expect_ctrl(&mut ctrl_rx, 1000, |m| {
891 matches!(m, StreamCtrl::Unsubscribe(_))
892 })
893 .await;
894
895 let cache = consumer.cached_events.read();
896 assert!(
897 cache.is_empty(),
898 "cache leaked entries for orphan topics: {:?}",
899 cache.keys().collect::<Vec<_>>()
900 );
901 }
902
903 #[tokio::test]
904 async fn falls_back_to_poll_when_stream_not_supported() {
905 let (transport, events_tx, _) = TestTransport::new(false, true);
907 let consumer = Consumer::new(transport, true, ());
910
911 let mut sub = consumer
913 .subscribe(SubscriptionReq::Bar("t".to_owned(), 5))
914 .expect("subscribe ok");
915
916 events_tx.send(Message { foo: 9, bar: 5 }).await.unwrap();
918 let got = recv_next::<TestTransport>(&mut sub, 1500)
919 .await
920 .expect("event relayed via polling");
921 assert_eq!(got, Message { foo: 9, bar: 5 });
922 }
923
924 #[tokio::test(start_paused = true)]
925 async fn terminal_stream_failure_is_not_retried() {
926 let (transport, attempts) = FailingStreamTransport::new(true);
927 let consumer = Consumer::new(transport, false, ());
928 let _subscription = consumer
929 .subscribe(SubscriptionReq::Foo("t".to_owned(), 1))
930 .expect("subscribe");
931
932 wait_for_attempts(&attempts, 1).await;
933 tokio::time::advance(Duration::from_secs(60)).await;
934 tokio::task::yield_now().await;
935
936 assert_eq!(attempts.load(Ordering::Relaxed), 1);
937 }
938
939 #[tokio::test(start_paused = true)]
940 async fn transient_stream_failures_use_increasing_backoff() {
941 let (transport, attempts) = FailingStreamTransport::new(false);
942 let consumer = Consumer::new(transport, false, ());
943 let _subscription = consumer
944 .subscribe(SubscriptionReq::Foo("t".to_owned(), 1))
945 .expect("subscribe");
946
947 wait_for_attempts(&attempts, 1).await;
948
949 tokio::time::advance(Duration::from_secs(3)).await;
950 wait_for_attempts(&attempts, 2).await;
951
952 tokio::time::advance(Duration::from_secs(3)).await;
953 tokio::task::yield_now().await;
954 assert_eq!(attempts.load(Ordering::Relaxed), 2);
955
956 tokio::time::advance(Duration::from_secs(2)).await;
957 wait_for_attempts(&attempts, 3).await;
958 }
959
960 #[tokio::test]
961 async fn multiple_subscribers_share_single_remote_subscription() {
962 let (transport, events_tx, mut ctrl_rx) = TestTransport::new(true, true);
965 let consumer = Consumer::new(transport, false, ());
966
967 let mut a = consumer
969 .subscribe(SubscriptionReq::Foo("t".to_owned(), 1))
970 .expect("subscribe A");
971 let _ = expect_ctrl(
972 &mut ctrl_rx,
973 1000,
974 |m| matches!(m, StreamCtrl::Subscribe((_, idx)) if *idx == IndexTest::Foo(1)),
975 )
976 .await;
977
978 let mut b = consumer
979 .subscribe(SubscriptionReq::Foo("b".to_owned(), 1))
980 .expect("subscribe B");
981
982 if let Ok(Some(StreamCtrl::Subscribe((_, idx)))) =
985 timeout(Duration::from_millis(400), ctrl_rx.recv()).await
986 {
987 assert_ne!(idx, IndexTest::Foo(1), "should not resubscribe same topic");
988 }
989
990 events_tx.send(Message { foo: 1, bar: 42 }).await.unwrap();
992 let got_a = recv_next::<TestTransport>(&mut a, 1000)
993 .await
994 .expect("A got");
995 let got_b = recv_next::<TestTransport>(&mut b, 1000)
996 .await
997 .expect("B got");
998 assert_eq!(got_a, Message { foo: 1, bar: 42 });
999 assert_eq!(got_b, Message { foo: 1, bar: 42 });
1000
1001 drop(b);
1003 if let Ok(Some(StreamCtrl::Unsubscribe(_))) =
1004 timeout(Duration::from_millis(400), ctrl_rx.recv()).await
1005 {
1006 panic!("Should NOT unsubscribe while another local subscriber exists");
1007 }
1008
1009 drop(a);
1011 let _ = expect_ctrl(&mut ctrl_rx, 1000, |m| {
1012 matches!(m, StreamCtrl::Unsubscribe(_))
1013 })
1014 .await;
1015
1016 let _ = expect_ctrl(&mut ctrl_rx, 1000, |m| matches!(m, StreamCtrl::Stop)).await;
1017 }
1018}