Files
qdrant/lib/common/issues/src/broker.rs
Tim Visée 3e536347e1 Bump Rust edition to 2024 (#6042)
* Bump Rust edition to 2024

* gen is a reserved keyword now

* Remove ref mut on references

* Mark extern C as unsafe

* Wrap unsafe function bodies in unsafe block

* Geo hash implements Copy, don't reference but pass by value instead

* Replace secluded self import with parent

* Update execute_cluster_read_operation with new match semantics

* Fix lifetime issue

* Replace map_or with is_none_or

* set_var is unsafe now

* Reformat
2025-02-25 11:21:25 +01:00

150 lines
3.8 KiB
Rust

use std::sync::{Arc, OnceLock, RwLock};
use crate::typemap::TypeMap;
pub trait Subscriber<E> {
fn notify(&self, event: Arc<E>);
}
struct SubscriberMap(TypeMap);
type DynSubscriber<E> = Box<dyn Subscriber<E> + Send + Sync>;
type SubscriVec<E> = Vec<Arc<DynSubscriber<E>>>;
impl SubscriberMap {
fn new() -> Self {
Self(TypeMap::new())
}
fn push<E: 'static>(&mut self, subscriber: DynSubscriber<E>) {
if !self.0.has::<SubscriVec<E>>() {
self.0.insert(SubscriVec::<E>::new());
}
let sub = Arc::new(subscriber);
self.0.get_mut::<SubscriVec<E>>().unwrap().push(sub);
}
fn get<E: 'static>(&self) -> Option<&SubscriVec<E>> {
self.0.get()
}
}
pub struct EventBroker {
subscriptions: SubscriberMap,
}
impl EventBroker {
pub fn new() -> Self {
Self {
subscriptions: SubscriberMap::new(),
}
}
pub fn add_subscriber<E: 'static>(&mut self, subscriber: DynSubscriber<E>) {
self.subscriptions.push(subscriber);
}
/// Notify all subscribers of the event. This method will block until all subscribers have handled the event, subscribers can choose if they want to handle the event in the background (non-blocking).
pub fn publish<E: 'static>(&self, event: E) {
if let Some(subscribers) = self.subscriptions.get::<E>() {
let event = Arc::new(event);
for sub in subscribers {
sub.notify(event.clone());
}
}
}
}
impl Default for EventBroker {
fn default() -> Self {
Self::new()
}
}
fn broker() -> Arc<RwLock<EventBroker>> {
static BROKER: OnceLock<Arc<RwLock<EventBroker>>> = OnceLock::new();
BROKER
.get_or_init(|| Arc::new(RwLock::new(EventBroker::default())))
.clone()
}
pub fn publish<E: 'static>(event: E) {
// This will only read if the lock is not poisoned
if let Ok(guard) = broker().read() {
guard.publish(event)
}
}
pub fn add_subscriber<E: 'static>(subscriber: Box<dyn Subscriber<E> + Send + Sync>) {
// This will only write if the lock is not poisoned
if let Ok(mut guard) = broker().write() {
guard.add_subscriber(subscriber);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dashboard::Dashboard;
use crate::issue::DummyIssue;
#[derive(Clone)]
struct DummySubscriber {
dashboard: Arc<Dashboard>,
}
struct DummyEvent {
pub collection_id: String,
}
impl Subscriber<DummyEvent> for DummySubscriber {
fn notify(&self, event: Arc<DummyEvent>) {
let issue = DummyIssue::new(event.collection_id.clone());
self.dashboard.add_issue(issue);
}
}
struct ClearAllIssuesEvent;
impl Subscriber<ClearAllIssuesEvent> for DummySubscriber {
fn notify(&self, _event: Arc<ClearAllIssuesEvent>) {
self.dashboard.issues.clear();
}
}
#[test]
fn test_basic_use() {
let mut broker = EventBroker::new();
let test_dashboard = Arc::new(Dashboard::default());
let subscriber = DummySubscriber {
dashboard: test_dashboard.clone(),
};
broker.add_subscriber::<DummyEvent>(Box::new(subscriber.clone()));
broker.add_subscriber::<ClearAllIssuesEvent>(Box::new(subscriber));
broker.publish(DummyEvent {
collection_id: "dummy".to_string(),
});
assert!(
test_dashboard
.get_all_issues()
.iter()
.any(|issue| issue.id == "DUMMY/dummy"),
);
broker.publish(ClearAllIssuesEvent);
assert!(
test_dashboard
.get_all_issues()
.iter()
.all(|issue| issue.id != "DUMMY/dummy"),
"{:?}",
test_dashboard.get_all_issues()
);
}
}