1#![deny(missing_docs)]
2
3use std::{
10 collections::{BTreeMap, VecDeque},
11 sync::mpsc,
12 sync::{Arc, Mutex, MutexGuard, Weak},
13};
14
15#[derive(Clone, Debug)]
22struct SubscriberBuffer<T> {
23 events: SubscriberEvents<T>,
24}
25
26#[derive(Clone, Debug)]
27enum SubscriberEvents<T> {
28 Unbounded(Vec<T>),
29 Bounded {
30 events: VecDeque<T>,
31 capacity: usize,
32 },
33}
34
35impl<T> Default for SubscriberBuffer<T> {
36 fn default() -> Self {
37 Self {
38 events: SubscriberEvents::Unbounded(Vec::new()),
39 }
40 }
41}
42
43impl<T> SubscriberBuffer<T> {
44 fn push(&mut self, event: T) {
45 match &mut self.events {
46 SubscriberEvents::Unbounded(events) => events.push(event),
47 SubscriberEvents::Bounded { events, capacity } => {
48 if *capacity == 0 {
49 return;
51 }
52 if events.len() == *capacity {
53 events.pop_front();
55 }
56 events.push_back(event);
57 }
58 }
59 }
60
61 fn drain(&mut self) -> Vec<T> {
62 match &mut self.events {
63 SubscriberEvents::Unbounded(events) => std::mem::take(events),
64 SubscriberEvents::Bounded { events, .. } => std::mem::take(events).into(),
65 }
66 }
67}
68
69type SubscriberQueue<T> = Arc<Mutex<SubscriberBuffer<T>>>;
70type SubscriberHandle<T> = Weak<Mutex<SubscriberBuffer<T>>>;
71type SubscriberList<T> = Arc<Mutex<Vec<SubscriberHandle<T>>>>;
72
73struct LiveSubscribers<T> {
74 first: Option<SubscriberQueue<T>>,
75 additional: Vec<SubscriberQueue<T>>,
76}
77
78impl<T> LiveSubscribers<T> {
79 fn new() -> Self {
80 Self {
81 first: None,
82 additional: Vec::new(),
83 }
84 }
85
86 fn push(&mut self, subscriber: SubscriberQueue<T>) {
87 if self.first.is_none() {
88 self.first = Some(subscriber);
89 } else {
90 self.additional.push(subscriber);
94 }
95 }
96}
97
98#[derive(Clone, Debug)]
118pub struct EventBus<T> {
119 subscribers: SubscriberList<T>,
120}
121
122#[derive(Clone, Debug, Eq, PartialEq)]
129pub struct ObservedEventContext {
130 operation_id: String,
131 event_name: String,
132 attributes: Arc<BTreeMap<String, String>>,
133}
134
135impl ObservedEventContext {
136 pub fn new(operation_id: impl Into<String>, event_name: impl Into<String>) -> Self {
138 Self {
139 operation_id: operation_id.into(),
140 event_name: event_name.into(),
141 attributes: Arc::new(BTreeMap::new()),
142 }
143 }
144
145 pub fn with_attribute(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
147 Arc::make_mut(&mut self.attributes).insert(key.into(), value.into());
148 self
149 }
150
151 pub fn operation_id(&self) -> &str {
153 &self.operation_id
154 }
155
156 pub fn event_name(&self) -> &str {
158 &self.event_name
159 }
160
161 pub fn attributes(&self) -> &BTreeMap<String, String> {
163 &self.attributes
164 }
165}
166
167pub trait EventObserver<T>: Clone + Send + Sync + 'static
173where
174 T: Clone + Send + Sync + 'static,
175{
176 fn on_event_published(&self, context: &ObservedEventContext);
178}
179
180impl<T> EventObserver<T> for ()
181where
182 T: Clone + Send + Sync + 'static,
183{
184 fn on_event_published(&self, _context: &ObservedEventContext) {}
185}
186
187#[derive(Clone)]
194pub struct EventObserverChannel {
195 sender: mpsc::Sender<ObservedEventContext>,
196}
197
198impl EventObserverChannel {
199 pub fn new(sender: mpsc::Sender<ObservedEventContext>) -> Self {
201 Self { sender }
202 }
203}
204
205impl<T> EventObserver<T> for EventObserverChannel
206where
207 T: Clone + Send + Sync + 'static,
208{
209 fn on_event_published(&self, context: &ObservedEventContext) {
210 let _ = self.sender.send(context.clone());
211 }
212}
213
214pub fn event_observer_channel() -> (EventObserverChannel, mpsc::Receiver<ObservedEventContext>) {
220 let (sender, receiver) = mpsc::channel();
221 (EventObserverChannel::new(sender), receiver)
222}
223
224#[derive(Clone)]
254pub struct ObservedEventBus<T, O = ()>
255where
256 T: Clone + Send + Sync + 'static,
257 O: EventObserver<T>,
258{
259 bus: EventBus<T>,
260 observer: O,
261 attributes: Arc<BTreeMap<String, String>>,
262 operation_id_generator: Arc<dyn Fn() -> String + Send + Sync>,
263}
264
265impl<T, O> ObservedEventBus<T, O>
266where
267 T: Clone + Send + Sync + 'static,
268 O: EventObserver<T>,
269{
270 pub fn new(bus: EventBus<T>, observer: O) -> Self {
272 Self {
273 bus,
274 observer,
275 attributes: Arc::new(BTreeMap::new()),
276 operation_id_generator: Arc::new(|| uuid::Uuid::new_v4().to_string()),
277 }
278 }
279
280 pub fn context(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
282 Arc::make_mut(&mut self.attributes).insert(key.into(), value.into());
285 self
286 }
287
288 pub fn operation_id_generator(
290 mut self,
291 generator: impl Fn() -> String + Send + Sync + 'static,
292 ) -> Self {
293 self.operation_id_generator = Arc::new(generator);
294 self
295 }
296
297 pub fn publish_named(&self, event_name: impl Into<String>, event: T) {
302 let event_name = event_name.into();
303 let context = self.context_for(event_name);
304 let span = tracing::info_span!(
305 "event.publish",
306 event.name = %context.event_name(),
307 event.operation_id = %context.operation_id()
308 );
309 let _entered = span.enter();
310 self.bus.publish(event);
311 self.observer.on_event_published(&context);
312 }
313
314 pub fn bus(&self) -> &EventBus<T> {
316 &self.bus
317 }
318
319 fn context_for(&self, event_name: String) -> ObservedEventContext {
320 ObservedEventContext {
321 operation_id: (self.operation_id_generator)(),
322 event_name,
323 attributes: Arc::clone(&self.attributes),
326 }
327 }
328}
329
330impl<T> EventBus<T>
331where
332 T: Clone,
333{
334 pub fn new() -> Self {
336 Self {
337 subscribers: Arc::new(Mutex::new(Vec::new())),
338 }
339 }
340
341 pub fn subscribe(&self) -> EventSubscriber<T> {
347 self.subscribe_with_buffer(SubscriberBuffer::default())
348 }
349
350 pub fn subscribe_with_capacity(&self, capacity: usize) -> EventSubscriber<T> {
358 self.subscribe_with_buffer(SubscriberBuffer {
359 events: SubscriberEvents::Bounded {
360 events: VecDeque::new(),
363 capacity,
364 },
365 })
366 }
367
368 fn subscribe_with_buffer(&self, buffer: SubscriberBuffer<T>) -> EventSubscriber<T> {
369 let queue = Arc::new(Mutex::new(buffer));
370 lock_unpoisoned(&self.subscribers).push(Arc::downgrade(&queue));
371 EventSubscriber { queue }
372 }
373
374 pub fn publish(&self, event: T) {
380 let LiveSubscribers {
381 first,
382 mut additional,
383 } = self.live_subscribers();
384 let Some(first) = first else {
385 return;
386 };
387
388 let Some(last) = additional.pop() else {
389 lock_unpoisoned(&first).push(event);
390 return;
391 };
392
393 lock_unpoisoned(&first).push(event.clone());
394 for subscriber in additional {
395 lock_unpoisoned(&subscriber).push(event.clone());
396 }
397 lock_unpoisoned(&last).push(event);
398 }
399
400 pub fn observed<O>(self, observer: O) -> ObservedEventBus<T, O>
402 where
403 T: Send + Sync + 'static,
404 O: EventObserver<T>,
405 {
406 ObservedEventBus::new(self, observer)
407 }
408
409 pub fn subscriber_count(&self) -> usize {
411 let mut subscribers = lock_unpoisoned(&self.subscribers);
412 subscribers.retain(|subscriber| subscriber.upgrade().is_some());
413 subscribers.len()
414 }
415
416 fn live_subscribers(&self) -> LiveSubscribers<T> {
417 let mut subscribers = lock_unpoisoned(&self.subscribers);
418 let mut live = LiveSubscribers::new();
419 subscribers.retain(|subscriber| {
420 if let Some(queue) = subscriber.upgrade() {
421 live.push(queue);
422 true
423 } else {
424 false
425 }
426 });
427 live
428 }
429}
430
431impl<T> Default for EventBus<T>
432where
433 T: Clone,
434{
435 fn default() -> Self {
436 Self::new()
437 }
438}
439
440#[derive(Clone, Debug)]
442pub struct EventSubscriber<T> {
443 queue: SubscriberQueue<T>,
444}
445
446impl<T> EventSubscriber<T> {
447 pub fn drain(&self) -> Vec<T> {
449 lock_unpoisoned(&self.queue).drain()
450 }
451}
452
453fn lock_unpoisoned<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
454 mutex.lock().unwrap_or_else(|poisoned| {
455 tracing::warn!("event bus mutex poisoned; recovering inner state");
456 poisoned.into_inner()
457 })
458}
459
460#[cfg(test)]
461mod tests {
462 use std::{sync::Arc, thread};
463
464 use super::*;
465 use tracing::Level;
466 use tracing_subscriber::{Layer, fmt::MakeWriter, layer::SubscriberExt};
467
468 #[derive(Clone, Default)]
469 struct SharedLogWriter {
470 output: Arc<Mutex<Vec<u8>>>,
471 }
472
473 impl SharedLogWriter {
474 fn contents(&self) -> String {
475 String::from_utf8(self.output.lock().unwrap().clone()).unwrap()
476 }
477
478 fn clear(&self) {
479 self.output.lock().unwrap().clear();
480 }
481 }
482
483 impl<'writer> MakeWriter<'writer> for SharedLogWriter {
484 type Writer = SharedLogGuard;
485
486 fn make_writer(&'writer self) -> Self::Writer {
487 SharedLogGuard {
488 output: Arc::clone(&self.output),
489 }
490 }
491 }
492
493 struct SharedLogGuard {
494 output: Arc<Mutex<Vec<u8>>>,
495 }
496
497 impl std::io::Write for SharedLogGuard {
498 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
499 self.output.lock().unwrap().extend_from_slice(buf);
500 Ok(buf.len())
501 }
502
503 fn flush(&mut self) -> std::io::Result<()> {
504 Ok(())
505 }
506 }
507
508 #[derive(Clone, Debug, PartialEq, Eq)]
509 struct UserCreated(u64);
510
511 #[test]
512 fn event_bus_recovers_from_poisoned_subscriber_list() {
513 let bus = EventBus::<UserCreated>::new();
514 let subscribers = Arc::clone(&bus.subscribers);
515
516 let panic = thread::spawn(move || {
517 let _subscribers = subscribers.lock().unwrap();
518 panic!("poison subscriber list");
519 });
520 assert!(panic.join().is_err());
521
522 let subscriber = bus.subscribe();
523 bus.publish(UserCreated(42));
524
525 assert_eq!(subscriber.drain(), vec![UserCreated(42)]);
526 }
527
528 #[test]
529 fn event_bus_warns_when_recovering_from_poisoned_subscriber_list() {
530 let bus = EventBus::<UserCreated>::new();
531 let subscribers = Arc::clone(&bus.subscribers);
532 let panic = thread::spawn(move || {
533 let _subscribers = subscribers.lock().unwrap();
534 panic!("poison subscriber list");
535 });
536 assert!(panic.join().is_err());
537
538 let writer = SharedLogWriter::default();
539 let subscriber = tracing_subscriber::registry().with(
540 tracing_subscriber::fmt::layer()
541 .with_writer(writer.clone())
542 .with_ansi(false)
543 .with_target(false)
544 .with_filter(tracing_subscriber::filter::LevelFilter::from_level(
545 Level::WARN,
546 )),
547 );
548
549 tracing::subscriber::with_default(subscriber, || {
550 for _ in 0..16 {
551 writer.clear();
552 tracing_core::callsite::rebuild_interest_cache();
553 let _subscriber = bus.subscribe();
554 let logs = writer.contents();
555 if logs.contains("event bus mutex poisoned") {
556 return;
557 }
558 std::thread::yield_now();
559 }
560 });
561
562 let logs = writer.contents();
563 assert!(logs.contains("event bus mutex poisoned"), "{logs}");
564 }
565
566 #[test]
567 fn event_bus_recovers_from_poisoned_subscriber_queue() {
568 let bus = EventBus::<UserCreated>::new();
569 let subscriber = bus.subscribe();
570 let queue = Arc::clone(&subscriber.queue);
571
572 let panic = thread::spawn(move || {
573 let _queue = queue.lock().unwrap();
574 panic!("poison subscriber queue");
575 });
576 assert!(panic.join().is_err());
577
578 bus.publish(UserCreated(42));
579
580 assert_eq!(subscriber.drain(), vec![UserCreated(42)]);
581 }
582
583 #[test]
584 fn bounded_subscriber_drops_oldest_events_beyond_capacity() {
585 let bus = EventBus::<UserCreated>::new();
586 let bounded = bus.subscribe_with_capacity(2);
587
588 bus.publish(UserCreated(1));
589 bus.publish(UserCreated(2));
590 bus.publish(UserCreated(3));
591
592 assert_eq!(bounded.drain(), vec![UserCreated(2), UserCreated(3)]);
595
596 bus.publish(UserCreated(4));
598 bus.publish(UserCreated(5));
599 bus.publish(UserCreated(6));
600 assert_eq!(bounded.drain(), vec![UserCreated(5), UserCreated(6)]);
601 }
602
603 #[test]
604 fn zero_capacity_subscriber_never_retains_events() {
605 let bus = EventBus::<UserCreated>::new();
606 let subscriber = bus.subscribe_with_capacity(0);
607
608 bus.publish(UserCreated(1));
609 bus.publish(UserCreated(2));
610
611 assert!(subscriber.drain().is_empty());
612 }
613
614 #[test]
615 fn unbounded_subscriber_keeps_all_events_by_default() {
616 let bus = EventBus::<UserCreated>::new();
617 let subscriber = bus.subscribe();
618
619 for id in 1..=50u64 {
620 bus.publish(UserCreated(id));
621 }
622
623 let drained: Vec<u64> = subscriber
624 .drain()
625 .into_iter()
626 .map(|event| event.0)
627 .collect();
628 assert_eq!(drained, (1..=50).collect::<Vec<_>>());
629 }
630
631 #[test]
632 fn observed_context_shares_configured_attributes_until_enriched() {
633 let observed = EventBus::<UserCreated>::new()
634 .observed(())
635 .operation_id_generator(|| "event-run".to_owned())
636 .context("service", "users-api");
637
638 let enriched_observed = observed.clone().context("region", "sa-east-1");
639 assert!(!Arc::ptr_eq(
640 &enriched_observed.attributes,
641 &observed.attributes
642 ));
643 assert!(!observed.attributes.contains_key("region"));
644 assert_eq!(
645 enriched_observed.attributes.get("region").unwrap(),
646 "sa-east-1"
647 );
648
649 let context = observed.context_for("user.created".to_owned());
650 assert!(Arc::ptr_eq(&context.attributes, &observed.attributes));
651
652 let enriched = context.clone().with_attribute("request_id", "request-42");
653 assert!(!Arc::ptr_eq(&enriched.attributes, &context.attributes));
654 assert_eq!(context.attributes().get("service").unwrap(), "users-api");
655 assert!(!context.attributes().contains_key("request_id"));
656 assert_eq!(
657 enriched.attributes().get("request_id").unwrap(),
658 "request-42"
659 );
660 }
661}