Skip to main content

topmesys/
lib.rs

1use std::{
2    any::Any,
3    fmt::Display,
4    marker::PhantomData,
5    sync::{Arc, PoisonError},
6};
7
8use bytes::Bytes;
9use dashmap::DashMap;
10use futures_util::{StreamExt, future::select};
11use tokio::{
12    sync::{mpsc, watch},
13    task::JoinSet,
14};
15use tokio_stream::wrappers::ReceiverStream;
16use type_states::{Init, Pattern, RoutingKey, Running, StateMarker, Stopped};
17
18mod dead_letter;
19mod delivery;
20mod retry;
21mod subscription;
22mod transport;
23
24pub use dead_letter::{DeadLetter, DeadLetterReason, DeadLetterSink};
25pub use delivery::{Delivery, HandlerError};
26pub use retry::{Backoff, RetryPolicy};
27pub use subscription::{Overflow, Subscription, SubscriptionInfo, Subscriptions};
28pub use transport::{DeliveryOutcome, Settlement, SubscriptionOutcome, TransportHandle};
29
30use subscription::{Defaults, Inbox, Route, Subscriber};
31use transport::Settler;
32
33type EventSubscriptions = Arc<DashMap<EventTopic<Pattern>, Vec<Route>>>;
34
35/// Allows for a concise representation and implementation of the [EventBroker]s and the [EventTopic]s
36/// lifecycle modes
37pub mod type_states {
38    // Used by EventTopic
39    #[derive(Debug, Clone)]
40    pub struct Init;
41    #[derive(Debug, Default, Clone, PartialEq, Eq)]
42    pub struct RoutingKey;
43    #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
44    pub struct Pattern;
45    // Used by EventBroker
46    #[derive(Clone, Debug)]
47    pub struct Stopped {
48        pub(super) bufsize: usize,
49    }
50    #[derive(Debug)]
51    pub struct Running {
52        pub(super) message_tx: super::mpsc::Sender<super::EventMessage>,
53        pub(super) stop_tx: super::watch::Sender<bool>,
54        pub(super) handle: tokio::task::JoinHandle<()>,
55        pub(super) runtime: tokio::runtime::Handle,
56        pub(super) workers: std::sync::Mutex<super::JoinSet<()>>,
57    }
58
59    pub trait StateMarker {}
60    impl StateMarker for () {}
61    impl StateMarker for Init {}
62    impl StateMarker for RoutingKey {}
63    impl StateMarker for Pattern {}
64
65    impl StateMarker for Stopped {}
66    impl StateMarker for Running {}
67}
68
69/// Errors that can occur when parsing and validating [EventTopic]s.
70#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
71pub enum TopicError {
72    #[error("topic cannot be empty")]
73    EmptyTopic,
74    #[error(
75        "invalid character in topic `{topic}`: topics may only contain alphanumeric characters and {allowed:?}"
76    )]
77    InvalidCharacter {
78        topic: String,
79        allowed: &'static [char],
80    },
81    #[error("found empty segment")]
82    EmptySegment,
83    #[error("invalid selection segment `{0}`")]
84    InvalidSelection(String),
85    #[error("invalid value `{value}` in selection segment `{segment}`")]
86    InvalidSelectionValue { segment: String, value: String },
87    #[error("found `,` outside of a selection segment: `{0}`")]
88    UnbracketedComma(String),
89    #[error("`*` must make up the entire segment: `{0}`")]
90    EmbeddedWildcard(String),
91}
92
93/// Errors that can occur when operating an [EventBroker].
94#[derive(Debug, thiserror::Error)]
95pub enum BrokerError {
96    #[error("no tokio runtime available to run the event loop")]
97    Runtime(#[from] tokio::runtime::TryCurrentError),
98}
99
100/// A type representing a single topic segment. Topic segments are used to match against other topic segments. Literal segments
101/// matched against other literal segments must be equal. Selection segments are matched against literal segments by checking if the literal segment
102/// value is contained within the selection segment. Wildcard segments match against any segment. Selection segments are defined by square brackets and comma separated values.
103/// When parsing selection segments, and inside the selection a wildcard segment is found, the entire selection will be parsed as wildcard. If a selection segment contains only a single
104/// value, the segment will be parsed as literal. Multiple values will be sorted and deduplicated, so selection segment lookups should be fairly quick.
105/// Wildcard segments are defined by a single asterisk. Literal segments are any other string. Selection values must be non-empty and may not
106/// contain brackets or embedded wildcards; segments that could never match a routing key (like `[a,]` or `a[b]`) are rejected when parsed.
107#[derive(Debug, Clone, Hash, PartialEq, Eq)]
108pub enum TopicSegment {
109    Literal(String),
110    Wildcard,
111    Selection(Vec<String>),
112}
113
114impl TopicSegment {
115    fn matches(&self, other: &TopicSegment) -> bool {
116        match (self, other) {
117            (TopicSegment::Literal(l), TopicSegment::Literal(r)) => l == r,
118            (TopicSegment::Selection(l), TopicSegment::Literal(r)) => l.binary_search(r).is_ok(),
119            (TopicSegment::Wildcard, _) => true,
120            _ => false,
121        }
122    }
123
124    fn parse(text: &str) -> Result<Self, TopicError> {
125        if text.is_empty() {
126            return Err(TopicError::EmptySegment);
127        }
128        if text == "*" {
129            return Ok(Self::Wildcard);
130        }
131        if text.starts_with('[') && text.ends_with(']') {
132            let mut values = Vec::new();
133            for value in text[1..text.len() - 1].split(',').map(str::trim) {
134                if value == "*" {
135                    return Ok(Self::Wildcard);
136                }
137                if value.is_empty() || value.contains(['[', ']', '*']) {
138                    return Err(TopicError::InvalidSelectionValue {
139                        segment: text.to_string(),
140                        value: value.to_string(),
141                    });
142                }
143                values.push(value.to_string());
144            }
145            values.sort();
146            values.dedup();
147            if values.len() == 1 {
148                return Ok(Self::Literal(values.pop().expect("one value is present")));
149            }
150            return Ok(Self::Selection(values));
151        }
152        if text.contains(['[', ']']) {
153            return Err(TopicError::InvalidSelection(text.to_string()));
154        }
155        if text.contains(',') {
156            return Err(TopicError::UnbracketedComma(text.to_string()));
157        }
158        if text.contains('*') {
159            return Err(TopicError::EmbeddedWildcard(text.to_string()));
160        }
161        Ok(Self::Literal(text.to_string()))
162    }
163}
164
165/// A type representing an events context. [EventTopic]s are generated from strings and used as either subscription patterns
166/// or routing keys for event messages. Periods are used to create [TopicSegment]s within a topic string to allow further categorisation
167/// and structural representation. A topic intended as a routing key only permits strings of alphanumeric characters and
168/// the characters `.` `-` `_`, ensuring it consists only of literal segments which can be used to match subscription pattern topics.
169/// Setting up the topic as a subscription pattern allows topics to consist of alphanumeric characters as well as `.`, `-`, `_`, `[`, `]`, `,`, `*`
170/// enabling literal, wildcard (`*`) and selection (`[val1,val2,valN]`) segments. This will also enable tail matching if a wildcard segment is found at the end of the topic.
171/// If many wildcard segments are found at the end of the pattern, one will be kept and the rest discarded, as it won't affect matching. A topic
172/// consisting of a single wildcard segment matches any other topic.
173///
174/// Topic strings are lowercased when the topic is created, making topic matching case-insensitive: `Orders.EU` and `orders.eu` are the same topic.
175#[derive(Debug, Default, Clone, Hash, PartialEq, Eq)]
176pub struct EventTopic<S: StateMarker> {
177    s: PhantomData<S>,
178    raw: String,
179    segments: Vec<TopicSegment>,
180    segment_count: usize,
181    is_wildcard: bool,
182    is_tail_matching: bool,
183}
184
185impl EventTopic<()> {
186    pub fn new(topic: impl Into<String>) -> EventTopic<Init> {
187        EventTopic {
188            s: PhantomData::<Init>,
189            raw: topic.into().to_lowercase(),
190            segments: Vec::new(),
191            segment_count: 0,
192            is_wildcard: false,
193            is_tail_matching: false,
194        }
195    }
196}
197
198impl EventTopic<Init> {
199    pub fn as_routing_key(self) -> Result<EventTopic<RoutingKey>, TopicError> {
200        self.sanitize_topic(&['.', '-', '_'])?;
201        let segments = self.parse_segments()?;
202        let new = EventTopic {
203            s: PhantomData::<RoutingKey>,
204            raw: self.raw.clone(),
205            segment_count: segments.len(),
206            segments,
207            is_tail_matching: false,
208            is_wildcard: false,
209        };
210        Ok(new)
211    }
212
213    pub fn as_subscription(self) -> Result<EventTopic<Pattern>, TopicError> {
214        self.sanitize_topic(&['.', ',', '*', '[', ']', '-', '_'])?;
215        let mut new = EventTopic {
216            s: PhantomData::<Pattern>,
217            raw: self.raw.clone(),
218            ..Default::default()
219        };
220        let segments = self.parse_segments()?;
221        if let Some(TopicSegment::Wildcard) = segments.last() {
222            new.is_tail_matching = true;
223            if segments.len() == 1 {
224                new.is_wildcard = true;
225            } else {
226                let tail_wildcards = segments
227                    .iter()
228                    .rev()
229                    .take_while(|s| *s == &TopicSegment::Wildcard)
230                    .count();
231                let real_segments = segments.len() - tail_wildcards + 1;
232                new.segments = segments.into_iter().take(real_segments).fold(
233                    new.segments,
234                    |mut acc, segment| {
235                        acc.push(segment);
236                        acc
237                    },
238                );
239            }
240        } else {
241            new.segments = segments;
242        }
243        new.segment_count = new.segments.len();
244        Ok(new)
245    }
246
247    fn parse_segments(&self) -> Result<Vec<TopicSegment>, TopicError> {
248        let mut parsed_segments = Vec::new();
249        for segment in self.raw.split('.') {
250            parsed_segments.push(TopicSegment::parse(segment)?);
251        }
252        Ok(parsed_segments)
253    }
254
255    fn sanitize_topic(&self, extra_keys: &'static [char]) -> Result<(), TopicError> {
256        if self.raw.is_empty() {
257            return Err(TopicError::EmptyTopic);
258        }
259        if !self
260            .raw
261            .chars()
262            .all(|c| c.is_alphanumeric() || extra_keys.contains(&c))
263        {
264            return Err(TopicError::InvalidCharacter {
265                topic: self.raw.clone(),
266                allowed: extra_keys,
267            });
268        }
269        Ok(())
270    }
271}
272
273impl EventTopic<Pattern> {
274    #[tracing::instrument(level = "debug")]
275    fn match_topic(&self, topic: &EventTopic<RoutingKey>) -> bool {
276        if self.is_wildcard {
277            true
278        } else if self.segment_count <= topic.segment_count && self.is_tail_matching {
279            let take = self.segment_count - 1;
280            self.segments
281                .iter()
282                .take(take)
283                .zip(topic.segments.iter().take(take))
284                .all(|(a, b)| a.matches(b))
285        } else if self.segment_count == topic.segment_count {
286            self.segments
287                .iter()
288                .zip(topic.segments.iter())
289                .all(|(a, b)| a.matches(b))
290        } else {
291            false
292        }
293    }
294}
295
296impl<S> EventTopic<S>
297where
298    S: StateMarker,
299{
300    pub fn text(&self) -> &str {
301        &self.raw
302    }
303
304    pub fn segments(&self) -> &[TopicSegment] {
305        &self.segments
306    }
307}
308
309impl<S> Display for EventTopic<S>
310where
311    S: StateMarker,
312{
313    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
314        write!(f, "{}", self.raw)
315    }
316}
317
318/// Represents either a single or multiple [EventMessage]s and enables [EventEmitter] implementors
319/// to submit batched events to an [EventBroker].
320#[derive(Debug, PartialEq, Eq)]
321pub enum EventSubmission {
322    Single(EventMessage),
323    Batch(Vec<EventMessage>),
324}
325
326impl From<EventMessage> for EventSubmission {
327    fn from(value: EventMessage) -> Self {
328        Self::Single(value)
329    }
330}
331
332/// Clones the message, which leaves its [TransportHandle] behind.
333impl From<&EventMessage> for EventSubmission {
334    fn from(value: &EventMessage) -> Self {
335        Self::Single(value.clone())
336    }
337}
338
339impl FromIterator<EventMessage> for EventSubmission {
340    fn from_iter<T: IntoIterator<Item = EventMessage>>(iter: T) -> Self {
341        Self::Batch(Vec::from_iter(iter))
342    }
343}
344
345/// Enables implementors to submit one or more [EventMessage]s to a [channel](Sender) connected
346/// to an [EventBroker]. When overriding the [submit_event](EventEmitter::submit_event) default implementation,
347/// implementors should ensure that cancel safety is maintained by using the channels [reserve](mpsc::Sender::reserve)
348/// method. Under normal circumstances, the EventBroker will handle any outstanding [permits](tokio::sync::mpsc::Permit)
349/// when being shut down.
350#[async_trait::async_trait]
351pub trait EventEmitter: std::fmt::Debug {
352    fn get_sender(&self) -> &mpsc::Sender<EventMessage>;
353
354    async fn submit_event(&self, submission: EventSubmission) -> anyhow::Result<()> {
355        match submission {
356            EventSubmission::Single(event_message) => {
357                self.get_sender().reserve().await?.send(event_message)
358            }
359            EventSubmission::Batch(event_messages) => {
360                self.get_sender()
361                    .reserve_many(event_messages.len())
362                    .await?
363                    .zip(event_messages)
364                    .for_each(|(permit, msg)| permit.send(msg));
365            }
366        }
367        Ok(())
368    }
369}
370
371/// Implementors are registered with an [EventBroker] and receive [EventMessage]s on one or more
372/// [Subscriptions]. Every subscription pairs a topic pattern with a value of the consumer's
373/// [Topic](EventConsumer::Topic), which is handed to [handle_event](EventConsumer::handle_event)
374/// along with each [Delivery] it receives, so messages can be handled per subscription. A message
375/// matching several subscriptions of a consumer is delivered once on each of them.
376///
377/// Errors returned by the handler are retried according to the subscription's [RetryPolicy], and
378/// messages the subscription gives up on are handed to its [DeadLetterSink], see [HandlerError].
379/// #### Example
380/// ```
381/// use std::time::Duration;
382/// use topmesys::{Delivery, EventConsumer, HandlerError, RetryPolicy, Subscription, Subscriptions};
383///
384/// #[derive(Debug)]
385/// enum NoteTopic {
386///     Created,
387///     Archived,
388/// }
389///
390/// #[derive(Debug)]
391/// struct AuditLog;
392///
393/// #[async_trait::async_trait]
394/// impl EventConsumer for AuditLog {
395///     type Topic = NoteTopic;
396///
397///     fn subscriptions(&self) -> Subscriptions<NoteTopic> {
398///         Subscriptions::new()
399///             .on(NoteTopic::Created, "notes.*.created")
400///             .on(
401///                 NoteTopic::Archived,
402///                 Subscription::new("notes.*.archived")
403///                     .with_retry_policy(RetryPolicy::exponential(5, Duration::from_millis(100))),
404///             )
405///     }
406///
407///     async fn handle_event(
408///         &self,
409///         topic: &NoteTopic,
410///         delivery: &Delivery,
411///     ) -> Result<(), HandlerError> {
412///         let note = std::str::from_utf8(delivery.message().content())?;
413///         match topic {
414///             NoteTopic::Created => println!("created {note}"),
415///             NoteTopic::Archived => println!("archived {note}"),
416///         }
417///         Ok(())
418///     }
419/// }
420/// ```
421#[async_trait::async_trait]
422pub trait EventConsumer: std::fmt::Debug + Send + Sync + 'static {
423    /// Identifies the consumer's subscriptions, typically an enum. Consumers with a single
424    /// subscription can use `()`.
425    type Topic: std::fmt::Debug + Send + Sync + 'static;
426
427    /// Called once, when the consumer is registered with a broker.
428    fn subscriptions(&self) -> Subscriptions<Self::Topic>;
429
430    async fn handle_event(
431        &self,
432        topic: &Self::Topic,
433        delivery: &Delivery,
434    ) -> Result<(), HandlerError>;
435}
436
437/// Identifies the subscriptions of an [EventConsumer] registered with an [EventBroker]. Returned by
438/// [add_topic_consumer](EventBroker::add_topic_consumer) and consumed by
439/// [remove_topic_consumer](EventBroker::remove_topic_consumer) to unsubscribe the consumer again.
440/// The handle holds [Weak](std::sync::Weak) references, so keeping it around does not keep the
441/// consumer alive.
442#[derive(Debug, Clone)]
443pub struct SubscriptionHandle {
444    subscriptions: Vec<(EventTopic<Pattern>, std::sync::Weak<Subscriber>)>,
445}
446
447/// This is the central bus for all events in the application. It sets up a [channel](tokio::sync::mpsc) and routes
448/// received [EventMessage]s to the [Subscription]s of [EventConsumer]s with matching topic patterns. Every subscription
449/// has its own bounded inbox and worker, which calls the consumer's [handle_event](EventConsumer::handle_event) method,
450/// retries failed deliveries according to the subscription's [RetryPolicy] and hands messages it gives up on to a
451/// [DeadLetterSink]. When an inbox is full, the subscription's [Overflow] policy decides whether routing waits or the
452/// message is dead-lettered. Messages whose topic doesn't match any subscription are dropped silently, and messages
453/// carrying a [TransportHandle] are settled once all of their deliveries finished.
454/// Messages are submitted to the channel using a [Sender<EventMessage>], produced by the [get_sender](EventBroker::get_sender) method.
455/// While the sender can be used for submissions "as is", the [submit_event][EventEmitter::submit_event] from the [EventEmitter] trait provides a safe default
456/// implementation using [channel permits](tokio::sync::mpsc::Permit), conveniently supporting both single and batched [EventSubmission]s.
457#[derive(Debug)]
458pub struct EventBroker<S: StateMarker> {
459    runtime: S,
460    subscriptions: EventSubscriptions,
461    handle_ctrl_c: bool,
462    defaults: Defaults,
463}
464
465impl Default for EventBroker<Stopped> {
466    fn default() -> EventBroker<Stopped> {
467        EventBroker::new(64)
468    }
469}
470
471impl EventBroker<()> {
472    pub fn new(bufsize: usize) -> EventBroker<Stopped> {
473        EventBroker {
474            runtime: Stopped { bufsize },
475            subscriptions: Arc::new(DashMap::new()),
476            handle_ctrl_c: false,
477            defaults: Defaults::default(),
478        }
479    }
480}
481
482impl<S> EventBroker<S>
483where
484    S: StateMarker + 'static,
485{
486    fn register(
487        &self,
488        subscribers: Vec<Arc<Subscriber>>,
489        mut open: impl FnMut(&Arc<Subscriber>) -> Option<Inbox>,
490    ) -> SubscriptionHandle {
491        let mut handle = SubscriptionHandle {
492            subscriptions: Vec::with_capacity(subscribers.len()),
493        };
494        for subscriber in subscribers {
495            let pattern = subscriber.info.pattern().clone();
496            handle
497                .subscriptions
498                .push((pattern.clone(), Arc::downgrade(&subscriber)));
499            let inbox = open(&subscriber);
500            self.subscriptions
501                .entry(pattern)
502                .or_default()
503                .push(Route { subscriber, inbox });
504        }
505        handle
506    }
507
508    /// Removes the subscriptions identified by the given [SubscriptionHandle]. Messages already queued
509    /// for them are still handled. Returns `true` if any subscription was found and removed, `false`
510    /// if they were removed before.
511    pub fn remove_topic_consumer(&self, handle: SubscriptionHandle) -> bool {
512        let mut removed = false;
513        for (pattern, subscriber) in handle.subscriptions {
514            let Some(subscriber) = subscriber.upgrade() else {
515                continue;
516            };
517            if let Some(mut routes) = self.subscriptions.get_mut(&pattern) {
518                let before = routes.len();
519                routes.retain(|route| !Arc::ptr_eq(&route.subscriber, &subscriber));
520                removed |= routes.len() < before;
521            }
522            self.subscriptions
523                .remove_if(&pattern, |_, routes| routes.is_empty());
524        }
525        removed
526    }
527
528    /// All subscriptions registered with the broker.
529    pub fn subscriptions(&self) -> Vec<Arc<SubscriptionInfo>> {
530        self.subscriptions
531            .iter()
532            .flat_map(|entry| {
533                entry
534                    .value()
535                    .iter()
536                    .map(|route| route.subscriber.info.clone())
537                    .collect::<Vec<_>>()
538            })
539            .collect()
540    }
541
542    /// The subscriptions a message with the given topic is delivered to.
543    pub fn find_subscriptions(&self, topic: &EventTopic<RoutingKey>) -> Vec<Arc<SubscriptionInfo>> {
544        self.matching_routes(topic, |route| Some(route.subscriber.info.clone()))
545    }
546
547    fn matching_routes<T>(
548        &self,
549        topic: &EventTopic<RoutingKey>,
550        pick: impl Fn(&Route) -> Option<T>,
551    ) -> Vec<T> {
552        let mut picked = Vec::new();
553        for entry in self.subscriptions.iter() {
554            if entry.key().match_topic(topic) {
555                picked.extend(entry.value().iter().filter_map(&pick));
556            }
557        }
558        picked
559    }
560
561    #[tracing::instrument(skip(self))]
562    async fn run_event_loop(
563        &self,
564        receiver: mpsc::Receiver<EventMessage>,
565        mut stop_rx: watch::Receiver<bool>,
566    ) {
567        let handle_ctrl_c = self.handle_ctrl_c;
568        let stop_signal = async move {
569            if handle_ctrl_c {
570                if let Err(e) = tokio::signal::ctrl_c().await {
571                    tracing::error!("Failed to listen for the ctrl-c signal: {e}");
572                    std::future::pending::<()>().await
573                }
574            } else {
575                std::future::pending::<()>().await
576            }
577        };
578        let stop_call = stop_rx.changed();
579        let mut stop = select(Box::pin(stop_signal), Box::pin(stop_call));
580        let mut event_stream = ReceiverStream::new(receiver);
581        let mut tasks = JoinSet::new();
582        loop {
583            tokio::select! {
584                biased;
585                _ = &mut stop => {
586                    tracing::info!("Stopping event stream processing.");
587                    event_stream.close();
588                    break;
589                }
590                Some(result) = tasks.join_next(), if !tasks.is_empty() => {
591                    if let Err(e) = result {
592                        tracing::error!("Event dispatch task failed: {e}");
593                    }
594                }
595                maybe_msg = event_stream.next() => match maybe_msg {
596                    Some(event_msg) => self.dispatch(event_msg, &mut tasks).await,
597                    None => {
598                        tracing::info!("Event loop processing ended. Shutting down broker.");
599                        break;
600                    }
601                },
602            }
603        }
604        // Route events still buffered in the channel, then close the inboxes, so every subscription
605        // finishes once it processed its queued deliveries and no accepted message is lost.
606        while let Some(event_msg) = event_stream.next().await {
607            self.dispatch(event_msg, &mut tasks).await;
608        }
609        close_inboxes(&self.subscriptions);
610        while let Some(result) = tasks.join_next().await {
611            if let Err(e) = result {
612                tracing::error!("Event dispatch task failed: {e}");
613            }
614        }
615    }
616
617    /// Queues a delivery of the message in the inbox of every matching subscription.
618    async fn dispatch(&self, event_msg: EventMessage, tasks: &mut JoinSet<()>) {
619        let inboxes = self.matching_routes(event_msg.topic(), |route| route.inbox.clone());
620        let message = Arc::new(event_msg);
621        let settler = Settler::new(&message, inboxes.len());
622        let Some(last) = inboxes.len().checked_sub(1) else {
623            if let Some(settler) = settler {
624                tasks.spawn(settler.settle());
625            }
626            return;
627        };
628        let mut settler = settler.map(Arc::new);
629        for (index, inbox) in inboxes.into_iter().enumerate() {
630            // The last delivery takes over the dispatcher's reference, so only deliveries hold the
631            // settler and the one finishing last settles the message.
632            let shared_settler = if index == last {
633                settler.take()
634            } else {
635                settler.clone()
636            };
637            let delivery = Delivery {
638                message: message.clone(),
639                subscription: inbox.subscription().clone(),
640                attempt: 1,
641                settler: shared_settler,
642            };
643            inbox.deliver(delivery, tasks).await;
644        }
645    }
646}
647
648/// Drops the broker's inbox senders, so every subscription worker finishes once its inbox is empty.
649fn close_inboxes(subscriptions: &EventSubscriptions) {
650    for mut routes in subscriptions.iter_mut() {
651        for route in routes.iter_mut() {
652            route.inbox = None;
653        }
654    }
655}
656
657impl EventBroker<Stopped> {
658    /// Additionally stops the running broker when the process receives a ctrl-c signal. This is
659    /// opt-in, as listening for process signals from within a library may conflict with the
660    /// embedding application's own signal handling.
661    pub fn with_ctrl_c_handling(mut self) -> Self {
662        self.handle_ctrl_c = true;
663        self
664    }
665
666    /// Sets the retry policy of subscriptions that don't configure their own. Defaults to
667    /// [RetryPolicy::none].
668    pub fn with_retry_policy(mut self, policy: RetryPolicy) -> Self {
669        self.defaults.retry_policy = policy;
670        self
671    }
672
673    /// Sets the dead letter sink of subscriptions that don't configure their own. Without a sink,
674    /// messages subscriptions give up on are logged and dropped.
675    pub fn with_dead_letter_sink(mut self, sink: Arc<dyn DeadLetterSink>) -> Self {
676        self.defaults.dead_letter_sink = Some(sink);
677        self
678    }
679
680    /// Registers the consumer's subscriptions, which receive messages once the broker runs.
681    pub fn add_topic_consumer(
682        &self,
683        consumer: impl EventConsumer,
684    ) -> Result<SubscriptionHandle, TopicError> {
685        Ok(self.register(Subscriber::from_consumer(consumer)?, |_| None))
686    }
687
688    pub fn run(self) -> Result<EventBroker<Running>, BrokerError> {
689        let rt = tokio::runtime::Handle::try_current()?;
690        let (message_tx, message_rx) = mpsc::channel::<EventMessage>(self.runtime.bufsize);
691        let (stop_tx, stop_rx) = watch::channel(false);
692        let mut workers = JoinSet::new();
693        for mut routes in self.subscriptions.iter_mut() {
694            for route in routes.iter_mut() {
695                route.inbox = Some(route.subscriber.open(
696                    self.runtime.bufsize,
697                    &self.defaults,
698                    &mut workers,
699                    &rt,
700                ));
701            }
702        }
703        let broker = EventBroker {
704            subscriptions: self.subscriptions.clone(),
705            handle_ctrl_c: self.handle_ctrl_c,
706            defaults: self.defaults.clone(),
707            runtime: Running {
708                handle: rt.spawn(async move { self.run_event_loop(message_rx, stop_rx).await }),
709                message_tx,
710                stop_tx,
711                runtime: rt,
712                workers: std::sync::Mutex::new(workers),
713            },
714        };
715
716        Ok(broker)
717    }
718}
719
720impl EventBroker<Running> {
721    /// Registers the consumer's subscriptions, which receive messages right away.
722    pub fn add_topic_consumer(
723        &self,
724        consumer: impl EventConsumer,
725    ) -> Result<SubscriptionHandle, TopicError> {
726        let subscribers = Subscriber::from_consumer(consumer)?;
727        let mut workers = self
728            .runtime
729            .workers
730            .lock()
731            .unwrap_or_else(PoisonError::into_inner);
732        // Clean up after the workers of removed subscriptions.
733        while let Some(result) = workers.try_join_next() {
734            if let Err(e) = result {
735                tracing::error!("Subscription worker failed: {e}");
736            }
737        }
738        let inbox_capacity = self.runtime.message_tx.max_capacity();
739        Ok(self.register(subscribers, |subscriber| {
740            Some(subscriber.open(
741                inbox_capacity,
742                &self.defaults,
743                &mut workers,
744                &self.runtime.runtime,
745            ))
746        }))
747    }
748
749    /// Stops the broker once every accepted message was handled, dead-lettered or dropped, including
750    /// deliveries still waiting for a retry.
751    pub async fn stop(self) -> EventBroker<Stopped> {
752        match self.runtime.stop_tx.send(true) {
753            Err(e) => {
754                tracing::error!("Failed to send stop signal to event loop: {e}");
755                self.runtime.handle.abort();
756            }
757            Ok(_) => {
758                if let Err(e) = self.runtime.handle.await {
759                    tracing::error!("Event loop task failed to stop gracefully: {e}.");
760                }
761            }
762        }
763        // Subscriptions added after the event loop ended still have open inboxes.
764        close_inboxes(&self.subscriptions);
765        let mut workers = self
766            .runtime
767            .workers
768            .into_inner()
769            .unwrap_or_else(PoisonError::into_inner);
770        while let Some(result) = workers.join_next().await {
771            if let Err(e) = result {
772                tracing::error!("Subscription worker failed: {e}");
773            }
774        }
775        EventBroker {
776            runtime: Stopped {
777                bufsize: self.runtime.message_tx.max_capacity(),
778            },
779            subscriptions: self.subscriptions.clone(),
780            handle_ctrl_c: self.handle_ctrl_c,
781            defaults: self.defaults,
782        }
783    }
784
785    pub fn get_sender(&self) -> mpsc::Sender<EventMessage> {
786        self.runtime.message_tx.clone()
787    }
788}
789
790/// A type representing an event handled by the applications event bus, consisting
791/// of a [Into] [String] subject and [Into] [Bytes] content.
792///
793/// Messages received from another messaging system can additionally carry a [TransportHandle],
794/// which the broker settles once the message was delivered. Clones and comparisons only take topic
795/// and content into account: a clone leaves the handle behind, so only the original settles it.
796/// #### Example
797/// ```
798/// use topmesys::{EventMessage, EventTopic};
799///
800/// let content = vec![123, 34, 116, 111, 34, 58, 34, 116, 104, 101, 109, 34, 125];
801///
802/// let first_msg = EventMessage::new("my-message", content).unwrap();
803/// let second_msg = EventMessage::default()
804///     .with_topic(EventTopic::new("my-message").as_routing_key().unwrap())
805///     .with_content(r#"{"to":"them"}"#);
806///
807/// assert_eq!(first_msg.topic(), second_msg.topic());
808/// assert_eq!(first_msg.content(), second_msg.content());
809/// ```
810#[derive(Debug, Default)]
811pub struct EventMessage {
812    topic: EventTopic<RoutingKey>,
813    content: Bytes,
814    transport: Option<Box<dyn TransportHandle>>,
815}
816
817impl Clone for EventMessage {
818    fn clone(&self) -> Self {
819        Self {
820            topic: self.topic.clone(),
821            content: self.content.clone(),
822            transport: None,
823        }
824    }
825}
826
827impl PartialEq for EventMessage {
828    fn eq(&self, other: &Self) -> bool {
829        self.topic == other.topic && self.content == other.content
830    }
831}
832
833impl Eq for EventMessage {}
834
835impl EventMessage {
836    pub fn new(
837        topic_text: impl Into<String>,
838        content: impl Into<Bytes>,
839    ) -> Result<Self, TopicError> {
840        Ok(Self {
841            topic: EventTopic::new(topic_text.into()).as_routing_key()?,
842            content: content.into(),
843            transport: None,
844        })
845    }
846
847    pub fn topic(&self) -> &EventTopic<RoutingKey> {
848        &self.topic
849    }
850
851    pub fn content(&self) -> &Bytes {
852        &self.content
853    }
854
855    /// The message's transport handle, if it carries one of type `T`.
856    pub fn transport<T: TransportHandle>(&self) -> Option<&T> {
857        let handle: &dyn Any = self.transport.as_deref()?;
858        handle.downcast_ref()
859    }
860
861    /// The message's transport handle, regardless of its type.
862    pub fn transport_handle(&self) -> Option<&dyn TransportHandle> {
863        self.transport.as_deref()
864    }
865
866    /// Self-consuming topic setter
867    pub fn with_topic(mut self, topic: EventTopic<RoutingKey>) -> Self {
868        self.topic = topic;
869        self
870    }
871
872    /// Self-consuming content setter
873    pub fn with_content(mut self, content: impl Into<Bytes>) -> Self {
874        self.content = content.into();
875        self
876    }
877
878    /// Self-consuming transport handle setter
879    pub fn with_transport(mut self, handle: impl TransportHandle) -> Self {
880        self.transport = Some(Box::new(handle));
881        self
882    }
883}
884
885#[cfg(test)]
886mod tests {
887    use std::{
888        sync::{
889            Arc, Mutex,
890            atomic::{AtomicUsize, Ordering},
891        },
892        time::Duration,
893    };
894
895    use dashmap::DashMap;
896    use tokio::sync::{Semaphore, mpsc};
897
898    use super::{
899        DeadLetter, DeadLetterReason, DeadLetterSink, Delivery, DeliveryOutcome, EventBroker,
900        EventConsumer, EventEmitter, EventMessage, EventSubmission, EventTopic, HandlerError,
901        Overflow, RetryPolicy, Settlement, Subscription, Subscriptions, TopicSegment,
902        TransportHandle,
903    };
904
905    /// A call of a [ScriptedConsumer]'s handler.
906    #[derive(Debug, Clone)]
907    struct Call {
908        content: String,
909        attempt: u32,
910        transport: Option<&'static str>,
911    }
912
913    type Calls = Arc<Mutex<Vec<Call>>>;
914
915    #[derive(Debug, Clone, Copy)]
916    enum Script {
917        Succeed,
918        FailAttempts(u32),
919        FailPermanently,
920        Panic,
921    }
922
923    /// Consumer following a [Script] and recording every call of its handler.
924    #[derive(Debug)]
925    struct ScriptedConsumer {
926        subscription: Subscription,
927        script: Script,
928        calls: Calls,
929    }
930
931    impl ScriptedConsumer {
932        fn new(subscription: impl Into<Subscription>, script: Script) -> (Self, Calls) {
933            let calls = Calls::default();
934            let consumer = Self {
935                subscription: subscription.into(),
936                script,
937                calls: calls.clone(),
938            };
939            (consumer, calls)
940        }
941    }
942
943    #[async_trait::async_trait]
944    impl EventConsumer for ScriptedConsumer {
945        type Topic = ();
946
947        fn subscriptions(&self) -> Subscriptions<()> {
948            self.subscription.clone().into()
949        }
950
951        async fn handle_event(&self, _topic: &(), delivery: &Delivery) -> Result<(), HandlerError> {
952            let call = Call {
953                content: String::from_utf8(delivery.message().content().to_vec())?,
954                attempt: delivery.attempt(),
955                transport: delivery
956                    .transport::<RecordingHandle>()
957                    .map(|handle| handle.id),
958            };
959            self.calls.lock().unwrap().push(call);
960            match self.script {
961                Script::FailAttempts(failing) if delivery.attempt() <= failing => {
962                    Err(anyhow::anyhow!("attempt {} failed", delivery.attempt()).into())
963                }
964                Script::Succeed | Script::FailAttempts(_) => Ok(()),
965                Script::FailPermanently => Err(HandlerError::permanent(anyhow::anyhow!(
966                    "malformed payload"
967                ))),
968                Script::Panic => panic!("handler bug"),
969            }
970        }
971    }
972
973    type Settlements = Arc<Mutex<Vec<(&'static str, Settlement)>>>;
974
975    /// Transport handle recording its settlement and retry notifications.
976    #[derive(Debug)]
977    struct RecordingHandle {
978        id: &'static str,
979        settlements: Settlements,
980        retries: Arc<AtomicUsize>,
981    }
982
983    impl RecordingHandle {
984        fn new(id: &'static str, settlements: &Settlements, retries: &Arc<AtomicUsize>) -> Self {
985            Self {
986                id,
987                settlements: settlements.clone(),
988                retries: retries.clone(),
989            }
990        }
991    }
992
993    #[async_trait::async_trait]
994    impl TransportHandle for RecordingHandle {
995        async fn settle(&self, settlement: &Settlement) -> anyhow::Result<()> {
996            self.settlements
997                .lock()
998                .unwrap()
999                .push((self.id, settlement.clone()));
1000            Ok(())
1001        }
1002
1003        async fn on_retry(&self, _attempt: u32, _delay: Duration) -> anyhow::Result<()> {
1004            self.retries.fetch_add(1, Ordering::SeqCst);
1005            Ok(())
1006        }
1007    }
1008
1009    /// Dead letter sink keeping every letter it receives.
1010    #[derive(Debug, Default)]
1011    struct CollectingSink {
1012        letters: Mutex<Vec<DeadLetter>>,
1013    }
1014
1015    #[async_trait::async_trait]
1016    impl DeadLetterSink for CollectingSink {
1017        async fn dead_letter(&self, letter: DeadLetter) -> anyhow::Result<()> {
1018            self.letters.lock().unwrap().push(letter);
1019            Ok(())
1020        }
1021    }
1022
1023    async fn wait_until(condition: impl Fn() -> bool) {
1024        tokio::time::timeout(Duration::from_secs(5), async {
1025            while !condition() {
1026                tokio::time::sleep(Duration::from_millis(1)).await;
1027            }
1028        })
1029        .await
1030        .expect("condition was not met in time");
1031    }
1032
1033    #[test]
1034    fn test_event_submission() {
1035        let event_message = EventMessage::new("test", "1234").unwrap();
1036
1037        let sub_a = EventSubmission::from(&event_message);
1038        let sub_b = EventSubmission::from(event_message);
1039        assert_eq!(sub_a, sub_b);
1040
1041        let sub_c = [("test", "1234"), ("foo", "bar")]
1042            .iter()
1043            .map(|(topic, content)| EventMessage::new(*topic, *content).unwrap())
1044            .collect::<EventSubmission>();
1045        if let EventSubmission::Batch(val) = sub_c {
1046            assert_eq!(val.len(), 2);
1047            assert_eq!(val[0], EventMessage::new("test", "1234").unwrap());
1048        }
1049    }
1050
1051    #[test]
1052    fn test_topic_segment_parse() {
1053        let literal = TopicSegment::parse("test").unwrap();
1054        let wildcard = TopicSegment::parse("*").unwrap();
1055        let simple_selection = TopicSegment::parse("[five,four,six]").unwrap();
1056        let unsorted_and_duplicates_selection = TopicSegment::parse("[2, 1, 3, 2]").unwrap();
1057        let selection_with_wildcard = TopicSegment::parse("[one, two, *]").unwrap();
1058        let single_item_selection = TopicSegment::parse("[one]").unwrap();
1059        assert_eq!(TopicSegment::Literal("test".to_string()), literal);
1060        assert_eq!(TopicSegment::Wildcard, wildcard);
1061        assert_eq!(
1062            TopicSegment::Selection(vec![
1063                "five".to_string(),
1064                "four".to_string(),
1065                "six".to_string()
1066            ]),
1067            simple_selection
1068        );
1069        assert_eq!(
1070            TopicSegment::Selection(vec!["1".to_string(), "2".to_string(), "3".to_string(),]),
1071            unsorted_and_duplicates_selection
1072        );
1073        assert_eq!(TopicSegment::Wildcard, selection_with_wildcard);
1074        assert_eq!(
1075            TopicSegment::Literal("one".to_string()),
1076            single_item_selection
1077        );
1078        assert_eq!(
1079            TopicSegment::Literal("one".to_string()),
1080            TopicSegment::parse("[one,one]").unwrap()
1081        );
1082    }
1083
1084    #[test]
1085    fn test_topic_segment_parse_rejects_malformed_segments() {
1086        use super::TopicError;
1087
1088        assert_eq!(TopicSegment::parse(""), Err(TopicError::EmptySegment));
1089        assert_eq!(
1090            TopicSegment::parse("[a,]"),
1091            Err(TopicError::InvalidSelectionValue {
1092                segment: "[a,]".to_string(),
1093                value: String::new(),
1094            })
1095        );
1096        assert_eq!(
1097            TopicSegment::parse("[,]"),
1098            Err(TopicError::InvalidSelectionValue {
1099                segment: "[,]".to_string(),
1100                value: String::new(),
1101            })
1102        );
1103        assert_eq!(
1104            TopicSegment::parse("[]"),
1105            Err(TopicError::InvalidSelectionValue {
1106                segment: "[]".to_string(),
1107                value: String::new(),
1108            })
1109        );
1110        assert_eq!(
1111            TopicSegment::parse("[a[b]"),
1112            Err(TopicError::InvalidSelectionValue {
1113                segment: "[a[b]".to_string(),
1114                value: "a[b".to_string(),
1115            })
1116        );
1117        assert_eq!(
1118            TopicSegment::parse("[a*b,c]"),
1119            Err(TopicError::InvalidSelectionValue {
1120                segment: "[a*b,c]".to_string(),
1121                value: "a*b".to_string(),
1122            })
1123        );
1124        assert_eq!(
1125            TopicSegment::parse("a[b]"),
1126            Err(TopicError::InvalidSelection("a[b]".to_string()))
1127        );
1128        assert_eq!(
1129            TopicSegment::parse("[a"),
1130            Err(TopicError::InvalidSelection("[a".to_string()))
1131        );
1132        assert_eq!(
1133            TopicSegment::parse("a,b"),
1134            Err(TopicError::UnbracketedComma("a,b".to_string()))
1135        );
1136        assert_eq!(
1137            TopicSegment::parse("a*b"),
1138            Err(TopicError::EmbeddedWildcard("a*b".to_string()))
1139        );
1140    }
1141
1142    #[test]
1143    fn test_topic_segment_matching() {
1144        let literal = TopicSegment::parse("test").unwrap();
1145        let wildcard = TopicSegment::parse("*").unwrap();
1146        let selection = TopicSegment::parse("[five,four,six]").unwrap();
1147
1148        assert!(literal.matches(&TopicSegment::Literal("test".to_string())));
1149        assert!(!literal.matches(&TopicSegment::Literal("tset".to_string())));
1150        assert!(wildcard.matches(&TopicSegment::Literal("1234124¶áðfå".to_string())));
1151        assert!(wildcard.matches(&TopicSegment::Literal("fnord".to_string())));
1152        assert!(selection.matches(&TopicSegment::Literal("five".to_string())));
1153        assert!(selection.matches(&TopicSegment::Literal("four".to_string())));
1154        assert!(selection.matches(&TopicSegment::Literal("six".to_string())));
1155        assert!(!selection.matches(&TopicSegment::Literal("test".to_string())));
1156        assert!(!selection.matches(&TopicSegment::Literal("foo".to_string())));
1157    }
1158
1159    #[test]
1160    fn test_event_topic() {
1161        let simple_topic = EventTopic::new("test.topic").as_routing_key().unwrap();
1162        let wildcard_topic = EventTopic::new("*").as_routing_key();
1163        let selection_topic = EventTopic::new("test.[one,two,three]")
1164            .as_subscription()
1165            .unwrap();
1166        let wildcard_tail_topic = EventTopic::new("test.*").as_subscription().unwrap();
1167        let wildcard_tail_topic_multiple = EventTopic::new("test.*.*").as_subscription().unwrap();
1168        let wildcard_tail_topic_selection = EventTopic::new("test.[one,two,*]")
1169            .as_subscription()
1170            .unwrap();
1171        let wildcard_tail_topic_selection_multiple = EventTopic::new("test.[one,two,*].*")
1172            .as_subscription()
1173            .unwrap();
1174        let topic_selection_wildcard_unsorted_duplicates =
1175            EventTopic::new("test.*.[5,2,4,3,5,2,1]")
1176                .as_subscription()
1177                .unwrap();
1178
1179        assert_eq!(simple_topic.text(), "test.topic");
1180        assert!(wildcard_topic.is_err());
1181        assert_eq!(
1182            selection_topic.segments(),
1183            vec![
1184                TopicSegment::Literal("test".to_string()),
1185                TopicSegment::Selection(vec![
1186                    "one".to_string(),
1187                    "three".to_string(),
1188                    "two".to_string(),
1189                ])
1190            ]
1191        );
1192        assert!(wildcard_tail_topic.is_tail_matching);
1193        assert!(wildcard_tail_topic.match_topic(&simple_topic));
1194        assert_eq!(
1195            wildcard_tail_topic_multiple.segments(),
1196            vec![
1197                TopicSegment::Literal("test".to_string()),
1198                TopicSegment::Wildcard
1199            ]
1200        );
1201        assert!(wildcard_tail_topic_multiple.match_topic(&simple_topic));
1202        assert!(wildcard_tail_topic_selection.is_tail_matching);
1203        assert_eq!(
1204            wildcard_tail_topic_selection.segments(),
1205            vec![
1206                TopicSegment::Literal("test".to_string()),
1207                TopicSegment::Wildcard
1208            ]
1209        );
1210        assert!(wildcard_tail_topic_selection_multiple.is_tail_matching);
1211        assert!(
1212            wildcard_tail_topic_selection_multiple
1213                .match_topic(&EventTopic::new("test.foo.bar").as_routing_key().unwrap())
1214        );
1215        assert_eq!(
1216            wildcard_tail_topic_selection_multiple.segments(),
1217            vec![
1218                TopicSegment::Literal("test".to_string()),
1219                TopicSegment::Wildcard
1220            ]
1221        );
1222        assert_eq!(
1223            topic_selection_wildcard_unsorted_duplicates
1224                .segments()
1225                .len(),
1226            3
1227        );
1228        assert!(
1229            topic_selection_wildcard_unsorted_duplicates
1230                .match_topic(&EventTopic::new("test.foo.1").as_routing_key().unwrap())
1231        );
1232        assert!(
1233            topic_selection_wildcard_unsorted_duplicates
1234                .match_topic(&EventTopic::new("test.bar.5").as_routing_key().unwrap())
1235        );
1236        assert!(
1237            !topic_selection_wildcard_unsorted_duplicates
1238                .match_topic(&EventTopic::new("test.bar.fnord").as_routing_key().unwrap())
1239        );
1240        assert_eq!(
1241            topic_selection_wildcard_unsorted_duplicates.segments(),
1242            vec![
1243                TopicSegment::Literal("test".to_string()),
1244                TopicSegment::Wildcard,
1245                TopicSegment::Selection(vec![
1246                    "1".to_string(),
1247                    "2".to_string(),
1248                    "3".to_string(),
1249                    "4".to_string(),
1250                    "5".to_string()
1251                ])
1252            ]
1253        );
1254    }
1255
1256    #[tokio::test]
1257    async fn test_event_broker() {
1258        #[derive(Debug, Default, Clone)]
1259        struct TestConsumer {
1260            name: String,
1261            results: Arc<DashMap<String, Vec<EventMessage>>>,
1262            topic: String,
1263        }
1264
1265        #[async_trait::async_trait]
1266        impl EventConsumer for TestConsumer {
1267            type Topic = ();
1268
1269            fn subscriptions(&self) -> Subscriptions<()> {
1270                self.topic.as_str().into()
1271            }
1272
1273            async fn handle_event(
1274                &self,
1275                _topic: &(),
1276                delivery: &Delivery,
1277            ) -> Result<(), HandlerError> {
1278                self.results
1279                    .entry(self.name.clone())
1280                    .or_default()
1281                    .push(delivery.message().as_ref().clone());
1282                Ok(())
1283            }
1284        }
1285
1286        #[derive(Debug, Clone)]
1287        struct TestEmitter {
1288            sender: mpsc::Sender<EventMessage>,
1289        }
1290
1291        #[async_trait::async_trait]
1292        impl EventEmitter for TestEmitter {
1293            fn get_sender(&self) -> &mpsc::Sender<EventMessage> {
1294                &self.sender
1295            }
1296        }
1297
1298        let consumer_results = Arc::new(DashMap::new());
1299
1300        let broker = EventBroker::new(10).run().unwrap();
1301        let consumer = TestConsumer {
1302            name: "consumer".to_string(),
1303            topic: String::from("test.topics.*"),
1304            results: consumer_results.clone(),
1305        };
1306        let consumer2 = TestConsumer {
1307            name: "consumer2".to_string(),
1308            topic: String::from("test.[one,two,three]"),
1309            results: consumer_results.clone(),
1310        };
1311        let consumer3 = TestConsumer {
1312            name: "consumer3".to_string(),
1313            topic: String::from("test.*.[5,2,4,3,5,2,1]"),
1314            results: consumer_results.clone(),
1315        };
1316        let consumer4 = TestConsumer {
1317            name: "consumer4".to_string(),
1318            topic: String::from("test.[should,fail]"),
1319            results: consumer_results.clone(),
1320        };
1321        broker.add_topic_consumer(consumer).unwrap();
1322        broker.add_topic_consumer(consumer2).unwrap();
1323        broker.add_topic_consumer(consumer3).unwrap();
1324        broker.add_topic_consumer(consumer4).unwrap();
1325
1326        let events = [
1327            EventMessage::new("test.one", "consumer2 stuff test1").unwrap(),
1328            EventMessage::new("test.two", "consumer2 stuff test2").unwrap(),
1329            EventMessage::new("test.three", "consumer2 stuff test3").unwrap(),
1330            EventMessage::new("test.topics.foo", "consumer stuff test1").unwrap(),
1331            EventMessage::new("test.topics.1", "consumer and consumer3 stuff test").unwrap(),
1332            EventMessage::new("test.bar.5", "consumer3 stuff test").unwrap(),
1333            EventMessage::new("test.baz.foo", "non routeable stuff test1").unwrap(),
1334            EventMessage::new("test.four", "non routeable stuff test2").unwrap(),
1335        ];
1336
1337        let emitter = TestEmitter {
1338            sender: broker.get_sender(),
1339        };
1340
1341        emitter
1342            .submit_event(EventSubmission::from_iter(events[..4].iter().cloned()))
1343            .await
1344            .unwrap();
1345        emitter
1346            .submit_event(EventSubmission::Single(events.get(4).unwrap().clone()))
1347            .await
1348            .unwrap();
1349        emitter
1350            .submit_event(EventSubmission::Batch(events[5..7].to_vec()))
1351            .await
1352            .unwrap();
1353        emitter
1354            .submit_event(EventSubmission::from(events.last().unwrap().clone()))
1355            .await
1356            .unwrap();
1357
1358        let _ = broker.stop().await;
1359
1360        let consumer_messages = consumer_results.get("consumer").unwrap();
1361        let consumer2_messages = consumer_results.get("consumer2").unwrap();
1362        let consumer3_messages = consumer_results.get("consumer3").unwrap();
1363
1364        assert!(consumer_messages.contains(events.get(3).unwrap()));
1365        assert!(consumer_messages.contains(events.get(4).unwrap()));
1366        assert!(consumer2_messages.contains(events.first().unwrap()));
1367        assert!(consumer2_messages.contains(events.get(1).unwrap()));
1368        assert!(consumer2_messages.contains(events.get(2).unwrap()));
1369        assert!(consumer3_messages.contains(events.get(4).unwrap()));
1370        assert!(consumer3_messages.contains(events.get(5).unwrap()));
1371
1372        assert!(
1373            consumer_results
1374                .iter()
1375                .flat_map(|entry| entry.value().clone())
1376                .all(|v| v != *events.last().unwrap() && v != *events.get(6).unwrap())
1377        );
1378    }
1379
1380    #[test]
1381    fn test_run_outside_runtime_fails() {
1382        assert!(EventBroker::new(1).run().is_err());
1383    }
1384
1385    #[test]
1386    fn test_remove_topic_consumer() {
1387        #[derive(Debug)]
1388        struct NoopConsumer;
1389
1390        #[async_trait::async_trait]
1391        impl EventConsumer for NoopConsumer {
1392            type Topic = ();
1393
1394            fn subscriptions(&self) -> Subscriptions<()> {
1395                "test.remove".into()
1396            }
1397
1398            async fn handle_event(
1399                &self,
1400                _topic: &(),
1401                _delivery: &Delivery,
1402            ) -> Result<(), HandlerError> {
1403                Ok(())
1404            }
1405        }
1406
1407        let broker = EventBroker::new(1);
1408        let handle = broker.add_topic_consumer(NoopConsumer).unwrap();
1409        let routing_key = EventTopic::new("test.remove").as_routing_key().unwrap();
1410        assert_eq!(broker.find_subscriptions(&routing_key).len(), 1);
1411
1412        assert!(broker.remove_topic_consumer(handle.clone()));
1413        assert!(broker.find_subscriptions(&routing_key).is_empty());
1414        assert!(broker.subscriptions().is_empty());
1415        assert!(!broker.remove_topic_consumer(handle));
1416    }
1417
1418    #[test]
1419    fn test_consumer_with_invalid_pattern_is_not_registered() {
1420        #[derive(Debug)]
1421        struct PartlyValid;
1422
1423        #[async_trait::async_trait]
1424        impl EventConsumer for PartlyValid {
1425            type Topic = u8;
1426
1427            fn subscriptions(&self) -> Subscriptions<u8> {
1428                Subscriptions::new().on(1, "valid.*").on(2, "not valid")
1429            }
1430
1431            async fn handle_event(
1432                &self,
1433                _topic: &u8,
1434                _delivery: &Delivery,
1435            ) -> Result<(), HandlerError> {
1436                Ok(())
1437            }
1438        }
1439
1440        let broker = EventBroker::new(1);
1441        assert!(broker.add_topic_consumer(PartlyValid).is_err());
1442        assert!(broker.subscriptions().is_empty());
1443    }
1444
1445    #[tokio::test]
1446    async fn test_stop_waits_for_in_flight_handlers() {
1447        #[derive(Debug)]
1448        struct SlowConsumer {
1449            results: Arc<DashMap<String, Vec<EventMessage>>>,
1450        }
1451
1452        #[async_trait::async_trait]
1453        impl EventConsumer for SlowConsumer {
1454            type Topic = ();
1455
1456            fn subscriptions(&self) -> Subscriptions<()> {
1457                "slow.*".into()
1458            }
1459
1460            async fn handle_event(
1461                &self,
1462                _topic: &(),
1463                delivery: &Delivery,
1464            ) -> Result<(), HandlerError> {
1465                tokio::time::sleep(Duration::from_millis(50)).await;
1466                self.results
1467                    .entry("slow".to_string())
1468                    .or_default()
1469                    .push(delivery.message().as_ref().clone());
1470                Ok(())
1471            }
1472        }
1473
1474        #[derive(Debug)]
1475        struct TestEmitter {
1476            sender: mpsc::Sender<EventMessage>,
1477        }
1478
1479        #[async_trait::async_trait]
1480        impl EventEmitter for TestEmitter {
1481            fn get_sender(&self) -> &mpsc::Sender<EventMessage> {
1482                &self.sender
1483            }
1484        }
1485
1486        let results = Arc::new(DashMap::new());
1487        let broker = EventBroker::new(10);
1488        broker
1489            .add_topic_consumer(SlowConsumer {
1490                results: results.clone(),
1491            })
1492            .unwrap();
1493        let broker = broker.run().unwrap();
1494        let emitter = TestEmitter {
1495            sender: broker.get_sender(),
1496        };
1497
1498        let events = (0..3)
1499            .map(|i| EventMessage::new(format!("slow.msg{i}"), "payload").unwrap())
1500            .collect::<Vec<_>>();
1501        emitter
1502            .submit_event(events.iter().cloned().collect())
1503            .await
1504            .unwrap();
1505        // Let the event loop pick up the messages so their handlers are in flight when we stop.
1506        tokio::task::yield_now().await;
1507
1508        let _ = broker.stop().await;
1509
1510        assert_eq!(results.get("slow").map(|v| v.len()), Some(events.len()));
1511    }
1512
1513    #[tokio::test]
1514    async fn test_multi_topic_consumer_handles_topics_individually() {
1515        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1516        enum ShopTopic {
1517            Orders,
1518            EuPayments,
1519        }
1520
1521        #[derive(Debug, Default)]
1522        struct Shop {
1523            received: Arc<Mutex<Vec<(ShopTopic, String)>>>,
1524        }
1525
1526        #[async_trait::async_trait]
1527        impl EventConsumer for Shop {
1528            type Topic = ShopTopic;
1529
1530            fn subscriptions(&self) -> Subscriptions<ShopTopic> {
1531                Subscriptions::new()
1532                    .on(ShopTopic::Orders, "orders.*")
1533                    .on(ShopTopic::EuPayments, "[orders,payments].eu.*")
1534            }
1535
1536            async fn handle_event(
1537                &self,
1538                topic: &ShopTopic,
1539                delivery: &Delivery,
1540            ) -> Result<(), HandlerError> {
1541                let routing_key = delivery.message().topic().text().to_string();
1542                self.received.lock().unwrap().push((*topic, routing_key));
1543                Ok(())
1544            }
1545        }
1546
1547        let shop = Shop::default();
1548        let received = shop.received.clone();
1549
1550        // Consumers can be called without a broker.
1551        let delivery = Delivery::new(
1552            EventMessage::new("orders.test", "").unwrap(),
1553            EventTopic::new("orders.*").as_subscription().unwrap(),
1554        );
1555        shop.handle_event(&ShopTopic::Orders, &delivery)
1556            .await
1557            .unwrap();
1558        assert_eq!(received.lock().unwrap().len(), 1);
1559        received.lock().unwrap().clear();
1560
1561        let broker = EventBroker::new(8).run().unwrap();
1562        let handle = broker.add_topic_consumer(shop).unwrap();
1563        let routed = broker.find_subscriptions(
1564            &EventTopic::new("orders.eu.created")
1565                .as_routing_key()
1566                .unwrap(),
1567        );
1568        assert_eq!(routed.len(), 2);
1569        assert!(routed.iter().all(|info| info.consumer().ends_with("Shop")));
1570
1571        for topic in [
1572            "orders.us.created",
1573            "orders.eu.created",
1574            "payments.eu.settled",
1575            "payments.us.settled",
1576        ] {
1577            broker
1578                .get_sender()
1579                .send(EventMessage::new(topic, "").unwrap())
1580                .await
1581                .unwrap();
1582        }
1583        let broker = broker.stop().await;
1584
1585        let mut received = received.lock().unwrap().clone();
1586        received.sort();
1587        assert_eq!(
1588            received,
1589            [
1590                (ShopTopic::Orders, "orders.eu.created".to_string()),
1591                (ShopTopic::Orders, "orders.us.created".to_string()),
1592                (ShopTopic::EuPayments, "orders.eu.created".to_string()),
1593                (ShopTopic::EuPayments, "payments.eu.settled".to_string()),
1594            ]
1595        );
1596
1597        assert!(broker.remove_topic_consumer(handle));
1598        assert!(broker.subscriptions().is_empty());
1599    }
1600
1601    #[tokio::test]
1602    async fn test_retries_until_handled_and_settles_once() {
1603        let settlements = Settlements::default();
1604        let retries = Arc::new(AtomicUsize::new(0));
1605        let (consumer, calls) = ScriptedConsumer::new(
1606            Subscription::new("jobs.*")
1607                .with_retry_policy(RetryPolicy::fixed(3, Duration::from_millis(1))),
1608            Script::FailAttempts(2),
1609        );
1610        let broker = EventBroker::new(4).run().unwrap();
1611        broker.add_topic_consumer(consumer).unwrap();
1612
1613        let message = EventMessage::new("jobs.render", "job")
1614            .unwrap()
1615            .with_transport(RecordingHandle::new("job", &settlements, &retries));
1616        broker.get_sender().send(message).await.unwrap();
1617        let _ = broker.stop().await;
1618
1619        let attempts = calls
1620            .lock()
1621            .unwrap()
1622            .iter()
1623            .map(|call| (call.attempt, call.transport))
1624            .collect::<Vec<_>>();
1625        assert_eq!(
1626            attempts,
1627            [(1, Some("job")), (2, Some("job")), (3, Some("job"))]
1628        );
1629        assert_eq!(retries.load(Ordering::SeqCst), 2);
1630        let settlements = settlements.lock().unwrap();
1631        assert_eq!(settlements.len(), 1);
1632        assert!(settlements[0].1.all_handled());
1633    }
1634
1635    #[tokio::test]
1636    async fn test_settlement_reports_every_subscription() {
1637        #[derive(Debug)]
1638        struct FailingSink;
1639
1640        #[async_trait::async_trait]
1641        impl DeadLetterSink for FailingSink {
1642            async fn dead_letter(&self, _letter: DeadLetter) -> anyhow::Result<()> {
1643                anyhow::bail!("dead letter queue unavailable")
1644            }
1645        }
1646
1647        let settlements = Settlements::default();
1648        let retries = Arc::new(AtomicUsize::new(0));
1649        let sink = Arc::new(CollectingSink::default());
1650        let broker = EventBroker::new(4)
1651            .with_dead_letter_sink(sink.clone())
1652            .run()
1653            .unwrap();
1654        let consumers = [
1655            ScriptedConsumer::new("orders.*", Script::Succeed),
1656            ScriptedConsumer::new("orders.eu.*", Script::FailPermanently),
1657            ScriptedConsumer::new(
1658                Subscription::new("orders.[eu,us].created")
1659                    .with_dead_letter_sink(Arc::new(FailingSink)),
1660                Script::FailPermanently,
1661            ),
1662        ];
1663        for (consumer, _) in consumers {
1664            broker.add_topic_consumer(consumer).unwrap();
1665        }
1666
1667        let sender = broker.get_sender();
1668        let routed = EventMessage::new("orders.eu.created", "{}")
1669            .unwrap()
1670            .with_transport(RecordingHandle::new("routed", &settlements, &retries));
1671        let unrouted = EventMessage::new("invoices.created", "{}")
1672            .unwrap()
1673            .with_transport(RecordingHandle::new("unrouted", &settlements, &retries));
1674        sender.send(routed).await.unwrap();
1675        sender.send(unrouted).await.unwrap();
1676        let _ = broker.stop().await;
1677
1678        let mut settlements = settlements.lock().unwrap().clone();
1679        settlements.sort_by_key(|(id, _)| *id);
1680        let [("routed", routed), ("unrouted", unrouted)] = settlements.as_slice() else {
1681            panic!("expected one settlement per message, got {settlements:?}");
1682        };
1683        let mut outcomes = routed
1684            .outcomes()
1685            .iter()
1686            .map(|outcome| (outcome.subscription().pattern().text(), outcome.outcome()))
1687            .collect::<Vec<_>>();
1688        outcomes.sort_by_key(|(pattern, _)| *pattern);
1689        assert_eq!(
1690            outcomes,
1691            [
1692                ("orders.*", DeliveryOutcome::Handled),
1693                ("orders.[eu,us].created", DeliveryOutcome::Failed),
1694                ("orders.eu.*", DeliveryOutcome::DeadLettered),
1695            ]
1696        );
1697        assert!(!routed.all_handled() && !routed.any(DeliveryOutcome::Aborted));
1698        assert!(unrouted.is_unrouted() && !unrouted.all_handled());
1699        assert_eq!(retries.load(Ordering::SeqCst), 0);
1700
1701        let letters = sink.letters.lock().unwrap();
1702        let [letter] = letters.as_slice() else {
1703            panic!("expected one dead letter, got {letters:?}");
1704        };
1705        assert_eq!(letter.subscription().pattern().text(), "orders.eu.*");
1706        assert_eq!(letter.attempts(), 1);
1707        assert_eq!(
1708            letter
1709                .message()
1710                .transport::<RecordingHandle>()
1711                .map(|handle| handle.id),
1712            Some("routed")
1713        );
1714        assert!(
1715            matches!(letter.reason(), DeadLetterReason::HandlerFailed(error) if error.is_permanent())
1716        );
1717    }
1718
1719    #[tokio::test]
1720    async fn test_failed_deliveries_are_dead_lettered() {
1721        let sink = Arc::new(CollectingSink::default());
1722        let broker = EventBroker::new(4)
1723            .with_retry_policy(RetryPolicy::linear(
1724                2,
1725                Duration::from_millis(1),
1726                Duration::from_millis(1),
1727            ))
1728            .with_dead_letter_sink(sink.clone());
1729        let consumers = [
1730            ScriptedConsumer::new("flaky.*", Script::FailAttempts(u32::MAX)),
1731            ScriptedConsumer::new("broken.*", Script::FailPermanently),
1732            ScriptedConsumer::new("buggy.*", Script::Panic),
1733            ScriptedConsumer::new(
1734                Subscription::new("stubborn.*")
1735                    .with_retry_policy(RetryPolicy::exponential(4, Duration::from_millis(1))),
1736                Script::FailAttempts(u32::MAX),
1737            ),
1738        ];
1739        for (consumer, _) in consumers {
1740            broker.add_topic_consumer(consumer).unwrap();
1741        }
1742        let broker = broker.run().unwrap();
1743
1744        for topic in ["flaky.job", "broken.job", "buggy.job", "stubborn.job"] {
1745            broker
1746                .get_sender()
1747                .send(EventMessage::new(topic, "job").unwrap())
1748                .await
1749                .unwrap();
1750        }
1751        let _ = broker.stop().await;
1752
1753        let mut letters = sink
1754            .letters
1755            .lock()
1756            .unwrap()
1757            .iter()
1758            .map(|letter| {
1759                let DeadLetterReason::HandlerFailed(error) = letter.reason() else {
1760                    panic!("unexpected dead letter reason: {}", letter.reason());
1761                };
1762                (
1763                    letter.subscription().pattern().text().to_string(),
1764                    letter.attempts(),
1765                    error.is_permanent(),
1766                    error.to_string(),
1767                )
1768            })
1769            .collect::<Vec<_>>();
1770        letters.sort();
1771        assert_eq!(
1772            letters,
1773            [
1774                (
1775                    "broken.*".to_string(),
1776                    1,
1777                    true,
1778                    "malformed payload".to_string()
1779                ),
1780                (
1781                    "buggy.*".to_string(),
1782                    1,
1783                    true,
1784                    "event handler panicked: handler bug".to_string()
1785                ),
1786                (
1787                    "flaky.*".to_string(),
1788                    3,
1789                    false,
1790                    "attempt 3 failed".to_string()
1791                ),
1792                (
1793                    "stubborn.*".to_string(),
1794                    5,
1795                    false,
1796                    "attempt 5 failed".to_string()
1797                ),
1798            ]
1799        );
1800    }
1801
1802    #[tokio::test]
1803    async fn test_sequential_subscription_retries_in_order() {
1804        let (consumer, calls) = ScriptedConsumer::new(
1805            Subscription::new("sequence.*")
1806                .with_concurrency(1)
1807                .with_retry_policy(RetryPolicy::fixed(1, Duration::from_millis(2))),
1808            Script::FailAttempts(1),
1809        );
1810        let broker = EventBroker::new(8);
1811        broker.add_topic_consumer(consumer).unwrap();
1812        let broker = broker.run().unwrap();
1813
1814        for step in 0..4 {
1815            broker
1816                .get_sender()
1817                .send(EventMessage::new("sequence.step", step.to_string()).unwrap())
1818                .await
1819                .unwrap();
1820        }
1821        let _ = broker.stop().await;
1822
1823        let calls = calls
1824            .lock()
1825            .unwrap()
1826            .iter()
1827            .map(|call| (call.content.clone(), call.attempt))
1828            .collect::<Vec<_>>();
1829        let expected = (0..4)
1830            .flat_map(|step| [(step.to_string(), 1), (step.to_string(), 2)])
1831            .collect::<Vec<_>>();
1832        assert_eq!(calls, expected);
1833    }
1834
1835    #[tokio::test]
1836    async fn test_overflowing_inbox_is_dead_lettered() {
1837        #[derive(Debug)]
1838        struct GatedConsumer {
1839            gate: Arc<Semaphore>,
1840            handled: Arc<AtomicUsize>,
1841        }
1842
1843        #[async_trait::async_trait]
1844        impl EventConsumer for GatedConsumer {
1845            type Topic = ();
1846
1847            fn subscriptions(&self) -> Subscriptions<()> {
1848                Subscription::new("gated.*")
1849                    .with_inbox_capacity(1)
1850                    .with_concurrency(1)
1851                    .with_overflow(Overflow::DeadLetter)
1852                    .into()
1853            }
1854
1855            async fn handle_event(
1856                &self,
1857                _topic: &(),
1858                _delivery: &Delivery,
1859            ) -> Result<(), HandlerError> {
1860                self.gate.acquire().await?.forget();
1861                self.handled.fetch_add(1, Ordering::SeqCst);
1862                Ok(())
1863            }
1864        }
1865
1866        let gate = Arc::new(Semaphore::new(0));
1867        let handled = Arc::new(AtomicUsize::new(0));
1868        let sink = Arc::new(CollectingSink::default());
1869        let broker = EventBroker::new(8)
1870            .with_dead_letter_sink(sink.clone())
1871            .run()
1872            .unwrap();
1873        broker
1874            .add_topic_consumer(GatedConsumer {
1875                gate: gate.clone(),
1876                handled: handled.clone(),
1877            })
1878            .unwrap();
1879
1880        for job in 0..4 {
1881            broker
1882                .get_sender()
1883                .send(EventMessage::new("gated.job", job.to_string()).unwrap())
1884                .await
1885                .unwrap();
1886        }
1887        // While the handler is blocked, the subscription holds one message in flight and one in its
1888        // inbox, so at least two of them overflow.
1889        wait_until(|| sink.letters.lock().unwrap().len() >= 2).await;
1890        gate.add_permits(4);
1891        let _ = broker.stop().await;
1892
1893        let letters = sink.letters.lock().unwrap();
1894        assert_eq!(handled.load(Ordering::SeqCst) + letters.len(), 4);
1895        assert!(letters.iter().all(|letter| {
1896            matches!(letter.reason(), DeadLetterReason::InboxFull) && letter.attempts() == 0
1897        }));
1898    }
1899
1900    #[tokio::test]
1901    async fn test_subscriptions_survive_restarts_and_change_while_running() {
1902        let (first, first_calls) = ScriptedConsumer::new("lifecycle.*", Script::Succeed);
1903        let (second, second_calls) = ScriptedConsumer::new("lifecycle.*", Script::Succeed);
1904        let contents = |calls: &Calls| {
1905            calls
1906                .lock()
1907                .unwrap()
1908                .iter()
1909                .map(|call| call.content.clone())
1910                .collect::<Vec<_>>()
1911        };
1912
1913        let broker = EventBroker::new(4);
1914        let first_handle = broker.add_topic_consumer(first).unwrap();
1915        let broker = broker.run().unwrap();
1916        broker
1917            .get_sender()
1918            .send(EventMessage::new("lifecycle.event", "1").unwrap())
1919            .await
1920            .unwrap();
1921        let broker = broker.stop().await.run().unwrap();
1922
1923        broker
1924            .get_sender()
1925            .send(EventMessage::new("lifecycle.event", "2").unwrap())
1926            .await
1927            .unwrap();
1928        wait_until(|| first_calls.lock().unwrap().len() == 2).await;
1929        assert!(broker.remove_topic_consumer(first_handle));
1930        broker.add_topic_consumer(second).unwrap();
1931        broker
1932            .get_sender()
1933            .send(EventMessage::new("lifecycle.event", "3").unwrap())
1934            .await
1935            .unwrap();
1936        let _ = broker.stop().await;
1937
1938        assert_eq!(contents(&first_calls), ["1", "2"]);
1939        assert_eq!(contents(&second_calls), ["3"]);
1940    }
1941}