1const MATCHERS_TASK_CHANNEL_BUFFER_SIZE: usize = 80_000;
35
36const SUBSCRIPTION_BUFFER_SIZE: usize = 128;
38
39use futures::{Stream, StreamExt};
40use itertools::Itertools;
41
42use crate::LOG_TARGET;
43use sc_utils::id_sequence::SeqID;
44use sp_core::{traits::SpawnNamed, Bytes, Encode};
45pub use sp_statement_store::StatementStore;
46use sp_statement_store::{
47 OptimizedTopicFilter, Result, Statement, StatementEvent, Topic, MAX_TOPICS,
48};
49use std::{
50 collections::{hash_map::Entry, HashMap, HashSet},
51 sync::atomic::AtomicU64,
52};
53
54pub trait StatementStoreSubscriptionApi: Send + Sync {
56 fn subscribe_statement(
61 &self,
62 topic_filter: OptimizedTopicFilter,
63 ) -> Result<(Vec<Vec<u8>>, async_channel::Sender<StatementEvent>, SubscriptionStatementsStream)>;
64}
65
66#[derive(Clone, Debug)]
68pub enum MatcherMessage {
69 NewStatement(Statement),
71 Subscribe(SubscriptionInfo),
73 Unsubscribe(SeqID),
75}
76
77pub struct SubscriptionsHandle {
79 id_sequence: AtomicU64,
82 matchers: SubscriptionsMatchersHandlers,
84}
85
86impl SubscriptionsHandle {
87 pub(crate) fn new(
89 task_spawner: Box<dyn SpawnNamed>,
90 num_matcher_workers: usize,
91 ) -> SubscriptionsHandle {
92 let mut subscriptions_matchers_senders = Vec::with_capacity(num_matcher_workers);
93
94 for task in 0..num_matcher_workers {
95 let (subscription_matcher_sender, subscription_matcher_receiver) =
96 async_channel::bounded(MATCHERS_TASK_CHANNEL_BUFFER_SIZE);
97 subscriptions_matchers_senders.push(subscription_matcher_sender);
98 task_spawner.spawn_blocking(
99 "statement-store-subscription-filters",
100 Some("statement-store"),
101 Box::pin(async move {
102 let mut subscriptions = SubscriptionsInfo::new();
103 log::debug!(
104 target: LOG_TARGET,
105 "Started statement subscription matcher task: {task}"
106 );
107 loop {
108 let res = subscription_matcher_receiver.recv().await;
109 match res {
110 Ok(MatcherMessage::NewStatement(statement)) => {
111 subscriptions.notify_matching_filters(&statement);
112 },
113 Ok(MatcherMessage::Subscribe(info)) => {
114 subscriptions.subscribe(info);
115 },
116 Ok(MatcherMessage::Unsubscribe(seq_id)) => {
117 subscriptions.unsubscribe(seq_id);
118 },
119 Err(_) => {
120 log::error!(
122 target: LOG_TARGET,
123 "Statement subscription matcher channel closed: {task}"
124 );
125 break;
126 },
127 };
128 }
129 }),
130 );
131 }
132 SubscriptionsHandle {
133 id_sequence: AtomicU64::new(0),
134 matchers: SubscriptionsMatchersHandlers::new(subscriptions_matchers_senders),
135 }
136 }
137
138 fn next_id(&self) -> SeqID {
140 let id = self.id_sequence.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
141 SeqID::from(id)
142 }
143
144 pub(crate) fn subscribe(
146 &self,
147 topic_filter: OptimizedTopicFilter,
148 ) -> (async_channel::Sender<StatementEvent>, SubscriptionStatementsStream) {
149 let next_id = self.next_id();
150 let (tx, rx) = async_channel::bounded(SUBSCRIPTION_BUFFER_SIZE);
151 let subscription_info =
152 SubscriptionInfo { topic_filter: topic_filter.clone(), seq_id: next_id, tx };
153
154 let result = (
155 subscription_info.tx.clone(),
156 SubscriptionStatementsStream {
157 rx,
158 sub_id: subscription_info.seq_id,
159 matchers: self.matchers.clone(),
160 },
161 );
162
163 self.matchers
164 .send_by_seq_id(subscription_info.seq_id, MatcherMessage::Subscribe(subscription_info));
165 result
166 }
167
168 pub(crate) fn notify(&self, statement: Statement) {
169 self.matchers.send_all(MatcherMessage::NewStatement(statement));
170 }
171}
172
173struct SubscriptionsInfo {
176 subscriptions_match_all_by_topic:
185 HashMap<Topic, [HashMap<SeqID, SubscriptionInfo>; MAX_TOPICS]>,
186 subscriptions_match_any_by_topic: HashMap<Topic, HashMap<SeqID, SubscriptionInfo>>,
188 subscriptions_any: HashMap<SeqID, SubscriptionInfo>,
190 by_sub_id: HashMap<SeqID, OptimizedTopicFilter>,
192}
193
194#[derive(Clone, Debug)]
196pub(crate) struct SubscriptionInfo {
197 topic_filter: OptimizedTopicFilter,
199 seq_id: SeqID,
201 tx: async_channel::Sender<StatementEvent>,
203}
204
205impl SubscriptionsInfo {
206 fn new() -> SubscriptionsInfo {
207 SubscriptionsInfo {
208 subscriptions_match_all_by_topic: HashMap::new(),
209 subscriptions_match_any_by_topic: HashMap::new(),
210 subscriptions_any: HashMap::new(),
211 by_sub_id: HashMap::new(),
212 }
213 }
214
215 fn subscribe(&mut self, subscription_info: SubscriptionInfo) {
217 self.by_sub_id
218 .insert(subscription_info.seq_id, subscription_info.topic_filter.clone());
219 match &subscription_info.topic_filter {
220 OptimizedTopicFilter::Any => {
221 self.subscriptions_any.insert(subscription_info.seq_id, subscription_info);
222 },
223 OptimizedTopicFilter::MatchAll(topics) => {
224 for topic in topics {
225 self.subscriptions_match_all_by_topic.entry(*topic).or_default()
226 [topics.len() - 1]
227 .insert(subscription_info.seq_id, subscription_info.clone());
228 }
229 },
230 OptimizedTopicFilter::MatchAny(topics) => {
231 for topic in topics {
232 self.subscriptions_match_any_by_topic
233 .entry(*topic)
234 .or_default()
235 .insert(subscription_info.seq_id, subscription_info.clone());
236 }
237 },
238 };
239 }
240
241 fn notify_subscriber(
243 &self,
244 subscription: &SubscriptionInfo,
245 bytes_to_send: Bytes,
246 needs_unsubscribing: &mut HashSet<SeqID>,
247 ) {
248 if let Err(err) = subscription.tx.try_send(StatementEvent::NewStatements {
249 statements: vec![bytes_to_send],
250 remaining: None,
251 }) {
252 log::debug!(
253 target: LOG_TARGET,
254 "Failed to send statement to subscriber {:?}: {:?} unsubscribing it", subscription.seq_id, err
255 );
256 needs_unsubscribing.insert(subscription.seq_id);
259 }
260 }
261
262 fn notify_matching_filters(&mut self, statement: &Statement) {
263 self.notify_match_all_subscribers_best(statement);
264 self.notify_match_any_subscribers(statement);
265 self.notify_any_subscribers(statement);
266 }
267
268 fn notify_match_any_subscribers(&mut self, statement: &Statement) {
270 let mut needs_unsubscribing: HashSet<SeqID> = HashSet::new();
271 let mut already_notified: HashSet<SeqID> = HashSet::new();
272
273 let bytes_to_send: Bytes = statement.encode().into();
274 for statement_topic in statement.topics() {
275 if let Some(subscriptions) = self.subscriptions_match_any_by_topic.get(statement_topic)
276 {
277 for subscription in subscriptions
278 .values()
279 .filter(|subscription| already_notified.insert(subscription.seq_id))
280 {
281 self.notify_subscriber(
282 subscription,
283 bytes_to_send.clone(),
284 &mut needs_unsubscribing,
285 );
286 }
287 }
288 }
289
290 for sub_id in needs_unsubscribing {
293 self.unsubscribe(sub_id);
294 }
295 }
296
297 fn notify_match_all_subscribers_best(&mut self, statement: &Statement) {
299 let bytes_to_send: Bytes = statement.encode().into();
300 let mut needs_unsubscribing: HashSet<SeqID> = HashSet::new();
301 let mut already_notified: HashSet<SeqID> = HashSet::new();
302 let num_topics = statement.topics().len();
303
304 for num_topics_to_check in 1..=num_topics {
307 for topics_combination in statement.topics().iter().combinations(num_topics_to_check) {
308 let Some(Some(topic_with_fewest)) = topics_combination
310 .iter()
311 .map(|topic| self.subscriptions_match_all_by_topic.get(*topic))
312 .min_by_key(|subscriptions| {
313 subscriptions.map_or(0, |subscriptions_by_length| {
314 subscriptions_by_length[num_topics_to_check - 1].len()
315 })
316 })
317 else {
318 continue;
319 };
320
321 for subscription in
326 topic_with_fewest[num_topics_to_check - 1].values().filter(|subscription| {
327 subscription.topic_filter.matches(statement) &&
328 already_notified.insert(subscription.seq_id)
329 }) {
330 self.notify_subscriber(
331 subscription,
332 bytes_to_send.clone(),
333 &mut needs_unsubscribing,
334 );
335 }
336 }
337 }
338 for sub_id in needs_unsubscribing {
341 self.unsubscribe(sub_id);
342 }
343 }
344
345 fn notify_any_subscribers(&mut self, statement: &Statement) {
347 let mut needs_unsubscribing: HashSet<SeqID> = HashSet::new();
348
349 let bytes_to_send: Bytes = statement.encode().into();
350 for subscription in self.subscriptions_any.values() {
351 self.notify_subscriber(subscription, bytes_to_send.clone(), &mut needs_unsubscribing);
352 }
353
354 for sub_id in needs_unsubscribing {
357 self.unsubscribe(sub_id);
358 }
359 }
360
361 fn unsubscribe(&mut self, id: SeqID) {
363 let Some(entry) = self.by_sub_id.remove(&id) else {
364 return;
365 };
366
367 let topics = match &entry {
368 OptimizedTopicFilter::Any => {
369 self.subscriptions_any.remove(&id);
370 return;
371 },
372 OptimizedTopicFilter::MatchAll(topics) => topics,
373 OptimizedTopicFilter::MatchAny(topics) => topics,
374 };
375
376 for topic in topics {
378 if let Entry::Occupied(mut entry) = self.subscriptions_match_any_by_topic.entry(*topic)
380 {
381 entry.get_mut().remove(&id);
382 if entry.get().is_empty() {
383 entry.remove();
384 }
385 }
386 if let Entry::Occupied(mut entry) = self.subscriptions_match_all_by_topic.entry(*topic)
388 {
389 for subscriptions in entry.get_mut().iter_mut() {
390 if subscriptions.remove(&id).is_some() {
391 break;
392 }
393 }
394 if entry.get().iter().all(|s| s.is_empty()) {
395 entry.remove();
396 }
397 }
398 }
399 }
400}
401
402#[derive(Clone)]
404pub struct SubscriptionsMatchersHandlers {
405 matchers: Vec<async_channel::Sender<MatcherMessage>>,
407}
408
409impl SubscriptionsMatchersHandlers {
410 fn new(matchers: Vec<async_channel::Sender<MatcherMessage>>) -> SubscriptionsMatchersHandlers {
412 SubscriptionsMatchersHandlers { matchers }
413 }
414
415 fn send_by_seq_id(&self, id: SeqID, message: MatcherMessage) {
417 let index: u64 = id.into();
418 if let Err(err) = self.matchers[index as usize % self.matchers.len()].send_blocking(message)
421 {
422 log::error!(
423 target: LOG_TARGET,
424 "Failed to send statement to matcher task: {:?}", err
425 );
426 }
427 }
428
429 fn send_all(&self, message: MatcherMessage) {
431 for sender in &self.matchers {
432 if let Err(err) = sender.send_blocking(message.clone()) {
433 log::error!(
434 target: LOG_TARGET,
435 "Failed to send message to matcher task: {:?}", err
436 );
437 }
438 }
439 }
440}
441
442pub struct SubscriptionStatementsStream {
444 pub rx: async_channel::Receiver<StatementEvent>,
446 sub_id: SeqID,
448 matchers: SubscriptionsMatchersHandlers,
450}
451
452impl Drop for SubscriptionStatementsStream {
454 fn drop(&mut self) {
455 self.matchers
456 .send_by_seq_id(self.sub_id, MatcherMessage::Unsubscribe(self.sub_id));
457 }
458}
459
460impl Stream for SubscriptionStatementsStream {
461 type Item = StatementEvent;
462
463 fn poll_next(
464 mut self: std::pin::Pin<&mut Self>,
465 cx: &mut std::task::Context<'_>,
466 ) -> std::task::Poll<Option<Self::Item>> {
467 self.rx.poll_next_unpin(cx)
468 }
469}
470
471#[cfg(test)]
472mod tests {
473
474 use crate::tests::signed_statement;
475
476 use super::*;
477 use sp_core::Decode;
478 use sp_statement_store::Topic;
479
480 fn unwrap_statement(item: StatementEvent) -> Bytes {
481 match item {
482 StatementEvent::NewStatements { mut statements, .. } => {
483 assert_eq!(statements.len(), 1, "Expected exactly one statement in batch");
484 statements.remove(0)
485 },
486 }
487 }
488 #[test]
489 fn test_subscribe_unsubscribe() {
490 let mut subscriptions = SubscriptionsInfo::new();
491
492 let (tx1, _rx1) = async_channel::bounded::<StatementEvent>(10);
493 let topic1 = Topic::from([8u8; 32]);
494 let topic2 = Topic::from([9u8; 32]);
495 let sub_info1 = SubscriptionInfo {
496 topic_filter: OptimizedTopicFilter::MatchAll(
497 vec![topic1, topic2].into_iter().collect(),
498 ),
499 seq_id: SeqID::from(1),
500 tx: tx1,
501 };
502 subscriptions.subscribe(sub_info1.clone());
503 assert!(subscriptions.subscriptions_match_all_by_topic.contains_key(&topic1));
504 assert!(subscriptions.subscriptions_match_all_by_topic.contains_key(&topic2));
505 assert!(subscriptions.by_sub_id.contains_key(&sub_info1.seq_id));
506 assert!(!subscriptions.subscriptions_any.contains_key(&sub_info1.seq_id));
507
508 subscriptions.unsubscribe(sub_info1.seq_id);
509 assert!(!subscriptions.subscriptions_match_all_by_topic.contains_key(&topic1));
510 assert!(!subscriptions.subscriptions_match_all_by_topic.contains_key(&topic2));
511 }
512
513 #[test]
514 fn test_subscribe_any() {
515 let mut subscriptions = SubscriptionsInfo::new();
516 let (tx1, _rx1) = async_channel::bounded::<StatementEvent>(10);
517 let sub_info1 = SubscriptionInfo {
518 topic_filter: OptimizedTopicFilter::Any,
519 seq_id: SeqID::from(1),
520 tx: tx1,
521 };
522 subscriptions.subscribe(sub_info1.clone());
523 assert!(subscriptions.subscriptions_any.contains_key(&sub_info1.seq_id));
524 assert!(subscriptions.by_sub_id.contains_key(&sub_info1.seq_id));
525 subscriptions.unsubscribe(sub_info1.seq_id);
526 assert!(!subscriptions.subscriptions_any.contains_key(&sub_info1.seq_id));
527 }
528
529 #[test]
530 fn test_subscribe_match_any() {
531 let mut subscriptions = SubscriptionsInfo::new();
532
533 let (tx1, _rx1) = async_channel::bounded::<StatementEvent>(10);
534 let topic1 = Topic::from([8u8; 32]);
535 let topic2 = Topic::from([9u8; 32]);
536 let sub_info1 = SubscriptionInfo {
537 topic_filter: OptimizedTopicFilter::MatchAny(
538 vec![topic1, topic2].into_iter().collect(),
539 ),
540 seq_id: SeqID::from(1),
541 tx: tx1,
542 };
543 subscriptions.subscribe(sub_info1.clone());
544 assert!(subscriptions.subscriptions_match_any_by_topic.contains_key(&topic1));
545 assert!(subscriptions.subscriptions_match_any_by_topic.contains_key(&topic2));
546 assert!(subscriptions.by_sub_id.contains_key(&sub_info1.seq_id));
547 assert!(!subscriptions.subscriptions_any.contains_key(&sub_info1.seq_id));
548
549 subscriptions.unsubscribe(sub_info1.seq_id);
550 assert!(!subscriptions.subscriptions_match_all_by_topic.contains_key(&topic1));
551 assert!(!subscriptions.subscriptions_match_all_by_topic.contains_key(&topic2));
552 }
553
554 #[test]
555 fn test_notify_any_subscribers() {
556 let mut subscriptions = SubscriptionsInfo::new();
557
558 let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
559 let sub_info1 = SubscriptionInfo {
560 topic_filter: OptimizedTopicFilter::Any,
561 seq_id: SeqID::from(1),
562 tx: tx1,
563 };
564 subscriptions.subscribe(sub_info1.clone());
565
566 let statement = signed_statement(1);
567 subscriptions.notify_matching_filters(&statement);
568
569 let received = unwrap_statement(rx1.try_recv().expect("Should receive statement"));
570 let decoded_statement: Statement =
571 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
572 assert_eq!(decoded_statement, statement);
573 }
574
575 #[test]
576 fn test_notify_match_all_subscribers() {
577 let mut subscriptions = SubscriptionsInfo::new();
578
579 let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
580 let topic1 = Topic::from([8u8; 32]);
581 let topic2 = Topic::from([9u8; 32]);
582 let sub_info1 = SubscriptionInfo {
583 topic_filter: OptimizedTopicFilter::MatchAll(
584 vec![topic1, topic2].into_iter().collect(),
585 ),
586 seq_id: SeqID::from(1),
587 tx: tx1,
588 };
589 subscriptions.subscribe(sub_info1.clone());
590
591 let mut statement = signed_statement(1);
592 statement.set_topic(0, topic2);
593 subscriptions.notify_matching_filters(&statement);
594
595 assert!(rx1.try_recv().is_err());
597
598 statement.set_topic(1, topic1);
599 subscriptions.notify_matching_filters(&statement);
600
601 let received = unwrap_statement(rx1.try_recv().expect("Should receive statement"));
602 let decoded_statement: Statement =
603 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
604 assert_eq!(decoded_statement, statement);
605 }
606
607 #[test]
608 fn test_notify_match_any_subscribers() {
609 let mut subscriptions = SubscriptionsInfo::new();
610 let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
611 let (tx2, rx2) = async_channel::bounded::<StatementEvent>(10);
612
613 let topic1 = Topic::from([8u8; 32]);
614 let topic2 = Topic::from([9u8; 32]);
615 let sub_info1 = SubscriptionInfo {
616 topic_filter: OptimizedTopicFilter::MatchAny(
617 vec![topic1, topic2].into_iter().collect(),
618 ),
619 seq_id: SeqID::from(1),
620 tx: tx1,
621 };
622
623 let sub_info2 = SubscriptionInfo {
624 topic_filter: OptimizedTopicFilter::MatchAny(vec![topic2].into_iter().collect()),
625 seq_id: SeqID::from(2),
626 tx: tx2,
627 };
628
629 subscriptions.subscribe(sub_info1.clone());
630 subscriptions.subscribe(sub_info2.clone());
631
632 let mut statement = signed_statement(1);
633 statement.set_topic(0, topic1);
634 statement.set_topic(1, topic2);
635 subscriptions.notify_match_any_subscribers(&statement);
636
637 let received = unwrap_statement(rx1.try_recv().expect("Should receive statement"));
638 let decoded_statement: Statement =
639 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
640 assert_eq!(decoded_statement, statement);
641
642 let received = unwrap_statement(rx2.try_recv().expect("Should receive statement"));
643 let decoded_statement: Statement =
644 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
645 assert_eq!(decoded_statement, statement);
646 }
647
648 #[tokio::test]
649 async fn test_subscription_handle_with_different_workers_number() {
650 for num_workers in 1..5 {
651 let subscriptions_handle = SubscriptionsHandle::new(
652 Box::new(sp_core::testing::TaskExecutor::new()),
653 num_workers,
654 );
655
656 let topic1 = Topic::from([8u8; 32]);
657 let topic2 = Topic::from([9u8; 32]);
658
659 let streams = (0..5)
660 .into_iter()
661 .map(|_| {
662 subscriptions_handle.subscribe(OptimizedTopicFilter::MatchAll(
663 vec![topic1, topic2].into_iter().collect(),
664 ))
665 })
666 .collect::<Vec<_>>();
667
668 let mut statement = signed_statement(1);
669 statement.set_topic(0, topic2);
670 subscriptions_handle.notify(statement.clone());
671
672 statement.set_topic(1, topic1);
673 subscriptions_handle.notify(statement.clone());
674
675 for (_tx, mut stream) in streams {
676 let received =
677 unwrap_statement(stream.next().await.expect("Should receive statement"));
678 let decoded_statement: Statement =
679 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
680 assert_eq!(decoded_statement, statement);
681 }
682 }
683 }
684
685 #[tokio::test]
686 async fn test_handle_unsubscribe() {
687 let subscriptions_handle =
688 SubscriptionsHandle::new(Box::new(sp_core::testing::TaskExecutor::new()), 2);
689
690 let topic1 = Topic::from([8u8; 32]);
691 let topic2 = Topic::from([9u8; 32]);
692
693 let (tx, mut stream) = subscriptions_handle
694 .subscribe(OptimizedTopicFilter::MatchAll(vec![topic1, topic2].into_iter().collect()));
695
696 let mut statement = signed_statement(1);
697 statement.set_topic(0, topic1);
698 statement.set_topic(1, topic2);
699
700 subscriptions_handle.notify(statement.clone());
702
703 let received = unwrap_statement(stream.next().await.expect("Should receive statement"));
704 let decoded_statement: Statement =
705 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
706 assert_eq!(decoded_statement, statement);
707
708 drop(stream);
710
711 tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
713
714 let mut statement2 = signed_statement(2);
716 statement2.set_topic(0, topic1);
717 statement2.set_topic(1, topic2);
718 subscriptions_handle.notify(statement2.clone());
719
720 tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
723
724 assert!(tx.is_closed(), "Sender should be closed after unsubscribe");
727 }
728
729 #[test]
730 fn test_unsubscribe_nonexistent() {
731 let mut subscriptions = SubscriptionsInfo::new();
732 subscriptions.unsubscribe(SeqID::from(999));
734 assert!(subscriptions.by_sub_id.is_empty());
736 assert!(subscriptions.subscriptions_any.is_empty());
737 assert!(subscriptions.subscriptions_match_all_by_topic.is_empty());
738 assert!(subscriptions.subscriptions_match_any_by_topic.is_empty());
739 }
740
741 #[test]
742 fn test_multiple_subscriptions_same_topic() {
743 let mut subscriptions = SubscriptionsInfo::new();
744
745 let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
746 let (tx2, rx2) = async_channel::bounded::<StatementEvent>(10);
747 let topic1 = Topic::from([8u8; 32]);
748 let topic2 = Topic::from([9u8; 32]);
749
750 let sub_info1 = SubscriptionInfo {
751 topic_filter: OptimizedTopicFilter::MatchAll(
752 vec![topic1, topic2].into_iter().collect(),
753 ),
754 seq_id: SeqID::from(1),
755 tx: tx1,
756 };
757 let sub_info2 = SubscriptionInfo {
758 topic_filter: OptimizedTopicFilter::MatchAll(
759 vec![topic1, topic2].into_iter().collect(),
760 ),
761 seq_id: SeqID::from(2),
762 tx: tx2,
763 };
764
765 subscriptions.subscribe(sub_info1.clone());
766 subscriptions.subscribe(sub_info2.clone());
767
768 assert_eq!(
770 subscriptions
771 .subscriptions_match_all_by_topic
772 .get(&topic1)
773 .unwrap()
774 .iter()
775 .map(|s| s.len())
776 .sum::<usize>(),
777 2
778 );
779 assert_eq!(
780 subscriptions
781 .subscriptions_match_all_by_topic
782 .get(&topic2)
783 .unwrap()
784 .iter()
785 .map(|s| s.len())
786 .sum::<usize>(),
787 2
788 );
789
790 let mut statement = signed_statement(1);
792 statement.set_topic(0, topic1);
793 statement.set_topic(1, topic2);
794 subscriptions.notify_matching_filters(&statement);
795
796 assert!(rx1.try_recv().is_ok());
798 assert!(rx2.try_recv().is_ok());
799
800 subscriptions.unsubscribe(sub_info1.seq_id);
802
803 assert_eq!(
805 subscriptions
806 .subscriptions_match_all_by_topic
807 .get(&topic1)
808 .unwrap()
809 .iter()
810 .map(|s| s.len())
811 .sum::<usize>(),
812 1
813 );
814 assert_eq!(
815 subscriptions
816 .subscriptions_match_all_by_topic
817 .get(&topic2)
818 .unwrap()
819 .iter()
820 .map(|s| s.len())
821 .sum::<usize>(),
822 1
823 );
824 assert!(!subscriptions.by_sub_id.contains_key(&sub_info1.seq_id));
825 assert!(subscriptions.by_sub_id.contains_key(&sub_info2.seq_id));
826
827 subscriptions.notify_matching_filters(&statement);
829
830 assert!(rx2.try_recv().is_ok());
832 assert!(rx1.try_recv().is_err());
833 }
834
835 #[test]
836 fn test_subscriber_auto_unsubscribe_on_channel_full() {
837 let mut subscriptions = SubscriptionsInfo::new();
838
839 let (tx1, rx1) = async_channel::bounded::<StatementEvent>(1);
841 let topic1 = Topic::from([8u8; 32]);
842
843 let sub_info1 = SubscriptionInfo {
844 topic_filter: OptimizedTopicFilter::MatchAny(vec![topic1].into_iter().collect()),
845 seq_id: SeqID::from(1),
846 tx: tx1,
847 };
848 subscriptions.subscribe(sub_info1.clone());
849
850 let mut statement = signed_statement(1);
851 statement.set_topic(0, topic1);
852
853 subscriptions.notify_matching_filters(&statement);
855 assert!(rx1.try_recv().is_ok());
856
857 subscriptions.notify_matching_filters(&statement);
859 subscriptions.notify_matching_filters(&statement);
863
864 assert!(!subscriptions.by_sub_id.contains_key(&sub_info1.seq_id));
866 assert!(!subscriptions.subscriptions_match_any_by_topic.contains_key(&topic1));
867 }
868
869 #[test]
870 fn test_match_any_receives_once_per_statement() {
871 let mut subscriptions = SubscriptionsInfo::new();
872
873 let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
874 let topic1 = Topic::from([8u8; 32]);
875 let topic2 = Topic::from([9u8; 32]);
876
877 let sub_info1 = SubscriptionInfo {
879 topic_filter: OptimizedTopicFilter::MatchAny(
880 vec![topic1, topic2].into_iter().collect(),
881 ),
882 seq_id: SeqID::from(1),
883 tx: tx1,
884 };
885 subscriptions.subscribe(sub_info1.clone());
886
887 let mut statement = signed_statement(1);
889 statement.set_topic(0, topic1);
890 statement.set_topic(1, topic2);
891
892 subscriptions.notify_match_any_subscribers(&statement);
893
894 let received = unwrap_statement(rx1.try_recv().expect("Should receive statement"));
896 let decoded_statement: Statement =
897 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
898 assert_eq!(decoded_statement, statement);
899
900 assert!(rx1.try_recv().is_err());
902 }
903
904 #[test]
905 fn test_match_all_receives_once_per_statement() {
906 let mut subscriptions = SubscriptionsInfo::new();
909
910 let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
911 let (tx2, _rx2) = async_channel::bounded::<StatementEvent>(10);
912 let (tx3, _rx3) = async_channel::bounded::<StatementEvent>(10);
913
914 let topic1 = Topic::from([1u8; 32]);
915 let topic2 = Topic::from([2u8; 32]);
916 let topic3 = Topic::from([3u8; 32]);
917 let topic4 = Topic::from([4u8; 32]);
918
919 let sub_info1 = SubscriptionInfo {
921 topic_filter: OptimizedTopicFilter::MatchAll(
922 vec![topic1, topic2].into_iter().collect(),
923 ),
924 seq_id: SeqID::from(1),
925 tx: tx1,
926 };
927 subscriptions.subscribe(sub_info1.clone());
928
929 let sub_info2 = SubscriptionInfo {
933 topic_filter: OptimizedTopicFilter::MatchAll(
934 vec![topic3, topic4].into_iter().collect(),
935 ),
936 seq_id: SeqID::from(2),
937 tx: tx2,
938 };
939 let sub_info3 = SubscriptionInfo {
940 topic_filter: OptimizedTopicFilter::MatchAll(
941 vec![topic3, topic4].into_iter().collect(),
942 ),
943 seq_id: SeqID::from(3),
944 tx: tx3,
945 };
946 subscriptions.subscribe(sub_info2);
947 subscriptions.subscribe(sub_info3);
948
949 let mut statement = signed_statement(1);
951 statement.set_topic(0, topic1);
952 statement.set_topic(1, topic2);
953 statement.set_topic(2, topic3);
954
955 subscriptions.notify_match_all_subscribers_best(&statement);
956
957 let received = unwrap_statement(rx1.try_recv().expect("Should receive statement"));
959 let decoded_statement: Statement =
960 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
961 assert_eq!(decoded_statement, statement);
962
963 assert!(
964 rx1.try_recv().is_err(),
965 "MatchAll subscriber must receive each statement only once"
966 );
967 }
968
969 #[test]
970 fn test_match_all_with_single_topic_matches_statement_with_two_topics() {
971 let mut subscriptions = SubscriptionsInfo::new();
972
973 let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
974 let topic1 = Topic::from([8u8; 32]);
975 let topic2 = Topic::from([9u8; 32]);
976
977 let sub_info1 = SubscriptionInfo {
979 topic_filter: OptimizedTopicFilter::MatchAll(vec![topic1].into_iter().collect()),
980 seq_id: SeqID::from(1),
981 tx: tx1,
982 };
983 subscriptions.subscribe(sub_info1.clone());
984
985 let mut statement = signed_statement(1);
987 statement.set_topic(0, topic1);
988 statement.set_topic(1, topic2);
989
990 subscriptions.notify_matching_filters(&statement);
991
992 let received = unwrap_statement(rx1.try_recv().expect("Should receive statement"));
994 let decoded_statement: Statement =
995 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
996 assert_eq!(decoded_statement, statement);
997
998 assert!(rx1.try_recv().is_err());
1000 }
1001
1002 #[test]
1003 fn test_match_all_no_matching_topics() {
1004 let mut subscriptions = SubscriptionsInfo::new();
1005
1006 let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
1007 let topic1 = Topic::from([8u8; 32]);
1008 let topic2 = Topic::from([9u8; 32]);
1009 let topic3 = Topic::from([10u8; 32]);
1010
1011 let sub_info1 = SubscriptionInfo {
1012 topic_filter: OptimizedTopicFilter::MatchAll(
1013 vec![topic1, topic2].into_iter().collect(),
1014 ),
1015 seq_id: SeqID::from(1),
1016 tx: tx1,
1017 };
1018 subscriptions.subscribe(sub_info1.clone());
1019
1020 let mut statement = signed_statement(1);
1022 statement.set_topic(0, topic3);
1023
1024 subscriptions.notify_matching_filters(&statement);
1025
1026 assert!(rx1.try_recv().is_err());
1028 }
1029
1030 #[test]
1031 fn test_match_all_with_unsubscribed_topic_first_in_statement() {
1032 let mut subscriptions = SubscriptionsInfo::new();
1037
1038 let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
1039 let topic1 = Topic::from([1u8; 32]);
1041 let topic2 = Topic::from([2u8; 32]);
1043
1044 let sub_info1 = SubscriptionInfo {
1046 topic_filter: OptimizedTopicFilter::MatchAll(vec![topic2].into_iter().collect()),
1047 seq_id: SeqID::from(1),
1048 tx: tx1,
1049 };
1050 subscriptions.subscribe(sub_info1);
1051
1052 let mut statement = signed_statement(1);
1057 statement.set_topic(0, topic1);
1058 statement.set_topic(1, topic2);
1059
1060 subscriptions.notify_match_all_subscribers_best(&statement);
1061
1062 let received = unwrap_statement(rx1.try_recv().expect(
1065 "Should receive statement - if this fails, the `return` bug in \
1066 notify_match_all_subscribers_best is present (should be `continue`)",
1067 ));
1068 let decoded_statement: Statement =
1069 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
1070 assert_eq!(decoded_statement, statement);
1071 }
1072
1073 #[tokio::test]
1074 async fn test_handle_with_match_any_filter() {
1075 let subscriptions_handle =
1076 SubscriptionsHandle::new(Box::new(sp_core::testing::TaskExecutor::new()), 2);
1077
1078 let topic1 = Topic::from([8u8; 32]);
1079 let topic2 = Topic::from([9u8; 32]);
1080
1081 let (_tx, mut stream) = subscriptions_handle
1082 .subscribe(OptimizedTopicFilter::MatchAny(vec![topic1, topic2].into_iter().collect()));
1083
1084 let mut statement1 = signed_statement(1);
1086 statement1.set_topic(0, topic1);
1087 subscriptions_handle.notify(statement1.clone());
1088
1089 let received = unwrap_statement(stream.next().await.expect("Should receive statement"));
1090 let decoded_statement: Statement =
1091 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
1092 assert_eq!(decoded_statement, statement1);
1093
1094 let mut statement2 = signed_statement(2);
1096 statement2.set_topic(0, topic2);
1097 subscriptions_handle.notify(statement2.clone());
1098
1099 let received = unwrap_statement(stream.next().await.expect("Should receive statement"));
1100 let decoded_statement: Statement =
1101 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
1102 assert_eq!(decoded_statement, statement2);
1103 }
1104
1105 #[tokio::test]
1106 async fn test_handle_with_any_filter() {
1107 let subscriptions_handle =
1108 SubscriptionsHandle::new(Box::new(sp_core::testing::TaskExecutor::new()), 2);
1109
1110 let (_tx, mut stream) = subscriptions_handle.subscribe(OptimizedTopicFilter::Any);
1111
1112 let statement1 = signed_statement(1);
1114 subscriptions_handle.notify(statement1.clone());
1115
1116 let received = unwrap_statement(stream.next().await.expect("Should receive statement"));
1117 let decoded_statement: Statement =
1118 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
1119 assert_eq!(decoded_statement, statement1);
1120
1121 let mut statement2 = signed_statement(2);
1122 statement2.set_topic(0, Topic::from([99u8; 32]));
1123 subscriptions_handle.notify(statement2.clone());
1124
1125 let received = unwrap_statement(stream.next().await.expect("Should receive statement"));
1126 let decoded_statement: Statement =
1127 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
1128 assert_eq!(decoded_statement, statement2);
1129 }
1130
1131 #[tokio::test]
1132 async fn test_handle_multiple_subscribers_different_filters() {
1133 let subscriptions_handle =
1134 SubscriptionsHandle::new(Box::new(sp_core::testing::TaskExecutor::new()), 2);
1135
1136 let topic1 = Topic::from([8u8; 32]);
1137 let topic2 = Topic::from([9u8; 32]);
1138
1139 let (_tx1, mut stream1) = subscriptions_handle
1141 .subscribe(OptimizedTopicFilter::MatchAll(vec![topic1, topic2].into_iter().collect()));
1142
1143 let (_tx2, mut stream2) = subscriptions_handle
1145 .subscribe(OptimizedTopicFilter::MatchAny(vec![topic1].into_iter().collect()));
1146
1147 let (_tx3, mut stream3) = subscriptions_handle.subscribe(OptimizedTopicFilter::Any);
1149
1150 let mut statement1 = signed_statement(1);
1152 statement1.set_topic(0, topic1);
1153 subscriptions_handle.notify(statement1.clone());
1154
1155 let received2 = unwrap_statement(stream2.next().await.expect("stream2 should receive"));
1160 let decoded2: Statement = Statement::decode(&mut &received2.0[..]).unwrap();
1161 assert_eq!(decoded2, statement1);
1162
1163 let received3 = unwrap_statement(stream3.next().await.expect("stream3 should receive"));
1164 let decoded3: Statement = Statement::decode(&mut &received3.0[..]).unwrap();
1165 assert_eq!(decoded3, statement1);
1166
1167 let mut statement2 = signed_statement(2);
1169 statement2.set_topic(0, topic1);
1170 statement2.set_topic(1, topic2);
1171 subscriptions_handle.notify(statement2.clone());
1172
1173 let received1 = unwrap_statement(stream1.next().await.expect("stream1 should receive"));
1175 let decoded1: Statement = Statement::decode(&mut &received1.0[..]).unwrap();
1176 assert_eq!(decoded1, statement2);
1177
1178 let received2 = unwrap_statement(stream2.next().await.expect("stream2 should receive"));
1179 let decoded2: Statement = Statement::decode(&mut &received2.0[..]).unwrap();
1180 assert_eq!(decoded2, statement2);
1181
1182 let received3 = unwrap_statement(stream3.next().await.expect("stream3 should receive"));
1183 let decoded3: Statement = Statement::decode(&mut &received3.0[..]).unwrap();
1184 assert_eq!(decoded3, statement2);
1185 }
1186
1187 #[test]
1188 fn test_statement_without_topics_matches_only_any_filter() {
1189 let mut subscriptions = SubscriptionsInfo::new();
1190
1191 let (tx_match_all, rx_match_all) = async_channel::bounded::<StatementEvent>(10);
1192 let (tx_match_any, rx_match_any) = async_channel::bounded::<StatementEvent>(10);
1193 let (tx_any, rx_any) = async_channel::bounded::<StatementEvent>(10);
1194
1195 let topic1 = Topic::from([8u8; 32]);
1196 let topic2 = Topic::from([9u8; 32]);
1197
1198 let sub_match_all = SubscriptionInfo {
1200 topic_filter: OptimizedTopicFilter::MatchAll(
1201 vec![topic1, topic2].into_iter().collect(),
1202 ),
1203 seq_id: SeqID::from(1),
1204 tx: tx_match_all,
1205 };
1206 subscriptions.subscribe(sub_match_all);
1207
1208 let sub_match_any = SubscriptionInfo {
1210 topic_filter: OptimizedTopicFilter::MatchAny(
1211 vec![topic1, topic2].into_iter().collect(),
1212 ),
1213 seq_id: SeqID::from(2),
1214 tx: tx_match_any,
1215 };
1216 subscriptions.subscribe(sub_match_any);
1217
1218 let sub_any = SubscriptionInfo {
1220 topic_filter: OptimizedTopicFilter::Any,
1221 seq_id: SeqID::from(3),
1222 tx: tx_any,
1223 };
1224 subscriptions.subscribe(sub_any);
1225
1226 let statement = signed_statement(1);
1228 assert!(statement.topics().is_empty(), "Statement should have no topics");
1229
1230 subscriptions.notify_matching_filters(&statement);
1232
1233 let received =
1235 unwrap_statement(rx_any.try_recv().expect("Any filter should receive statement"));
1236 let decoded_statement: Statement =
1237 Statement::decode(&mut &received.0[..]).expect("Should decode statement");
1238 assert_eq!(decoded_statement, statement);
1239
1240 assert!(
1242 rx_match_all.try_recv().is_err(),
1243 "MatchAll should not receive statement without topics"
1244 );
1245
1246 assert!(
1248 rx_match_any.try_recv().is_err(),
1249 "MatchAny should not receive statement without topics"
1250 );
1251 }
1252}