feox_server/pubsub/
registry.rs

1use super::{BroadcastMsg, PubSubStats, ThreadId};
2use crossbeam_channel::{bounded, Receiver, Sender};
3use dashmap::DashMap;
4use std::collections::HashSet;
5use std::sync::atomic::Ordering;
6use std::sync::Arc;
7
8pub struct GlobalRegistry {
9    channel_to_threads: DashMap<Vec<u8>, HashSet<ThreadId>>,
10    pattern_to_threads: DashMap<Vec<u8>, HashSet<ThreadId>>,
11    channel_subscriber_counts: DashMap<Vec<u8>, usize>,
12    pattern_subscriber_counts: DashMap<Vec<u8>, usize>,
13    thread_channels: Vec<Sender<BroadcastMsg>>,
14    pub stats: Arc<PubSubStats>,
15}
16
17impl GlobalRegistry {
18    pub fn new(num_threads: usize) -> (Arc<Self>, Vec<Receiver<BroadcastMsg>>) {
19        let mut senders = Vec::with_capacity(num_threads);
20        let mut receivers = Vec::with_capacity(num_threads);
21
22        for _ in 0..num_threads {
23            let (tx, rx) = bounded(1024);
24            senders.push(tx);
25            receivers.push(rx);
26        }
27
28        let registry = Arc::new(Self {
29            channel_to_threads: DashMap::new(),
30            pattern_to_threads: DashMap::new(),
31            channel_subscriber_counts: DashMap::new(),
32            pattern_subscriber_counts: DashMap::new(),
33            thread_channels: senders,
34            stats: Arc::new(PubSubStats::new()),
35        });
36
37        (registry, receivers)
38    }
39
40    pub fn add_channel_interest(&self, channel: Vec<u8>, thread_id: ThreadId) {
41        let mut entry = self.channel_to_threads.entry(channel).or_default();
42        if entry.insert(thread_id) {
43            self.stats.total_channels.fetch_add(1, Ordering::Relaxed);
44        }
45    }
46
47    pub fn remove_channel_interest(&self, channel: &[u8], thread_id: ThreadId) {
48        if let Some(mut entry) = self.channel_to_threads.get_mut(channel) {
49            if entry.remove(&thread_id) {
50                self.stats.total_channels.fetch_sub(1, Ordering::Relaxed);
51            }
52            if entry.is_empty() {
53                drop(entry);
54                self.channel_to_threads.remove(channel);
55            }
56        }
57    }
58
59    pub fn add_pattern_interest(&self, pattern: Vec<u8>, thread_id: ThreadId) {
60        let mut entry = self.pattern_to_threads.entry(pattern).or_default();
61        if entry.insert(thread_id) {
62            self.stats.total_patterns.fetch_add(1, Ordering::Relaxed);
63        }
64    }
65
66    pub fn remove_pattern_interest(&self, pattern: &[u8], thread_id: ThreadId) {
67        if let Some(mut entry) = self.pattern_to_threads.get_mut(pattern) {
68            if entry.remove(&thread_id) {
69                self.stats.total_patterns.fetch_sub(1, Ordering::Relaxed);
70            }
71            if entry.is_empty() {
72                drop(entry);
73                self.pattern_to_threads.remove(pattern);
74            }
75        }
76    }
77
78    pub fn get_channel_threads(&self, channel: &[u8]) -> Vec<ThreadId> {
79        self.channel_to_threads
80            .get(channel)
81            .map(|entry| entry.iter().copied().collect())
82            .unwrap_or_default()
83    }
84
85    pub fn get_all_pattern_threads(&self) -> HashSet<ThreadId> {
86        let mut threads = HashSet::new();
87        for entry in self.pattern_to_threads.iter() {
88            threads.extend(entry.value().iter().copied());
89        }
90        threads
91    }
92
93    pub fn broadcast_to_threads(&self, msg: BroadcastMsg, thread_ids: &[ThreadId]) {
94        self.stats.total_messages.fetch_add(1, Ordering::Relaxed);
95
96        for &thread_id in thread_ids {
97            if thread_id < self.thread_channels.len() {
98                let _ = self.thread_channels[thread_id].try_send(msg.clone());
99            }
100        }
101    }
102
103    pub fn broadcast_to_all_threads(&self, msg: BroadcastMsg, exclude: Option<ThreadId>) {
104        self.stats.total_messages.fetch_add(1, Ordering::Relaxed);
105
106        for (thread_id, sender) in self.thread_channels.iter().enumerate() {
107            if Some(thread_id) != exclude {
108                let _ = sender.try_send(msg.clone());
109            }
110        }
111    }
112
113    pub fn get_all_channels(&self) -> Vec<Vec<u8>> {
114        self.channel_to_threads
115            .iter()
116            .map(|entry| entry.key().clone())
117            .collect()
118    }
119
120    pub fn get_channel_subscriber_count(&self, channel: &[u8]) -> usize {
121        self.channel_subscriber_counts
122            .get(channel)
123            .map(|entry| *entry)
124            .unwrap_or(0)
125    }
126
127    pub fn increment_channel_subscribers(&self, channel: &[u8]) {
128        *self
129            .channel_subscriber_counts
130            .entry(channel.to_vec())
131            .or_insert(0) += 1;
132    }
133
134    pub fn decrement_channel_subscribers(&self, channel: &[u8]) {
135        if let Some(mut count) = self.channel_subscriber_counts.get_mut(channel) {
136            if *count > 0 {
137                *count -= 1;
138            }
139            if *count == 0 {
140                drop(count);
141                self.channel_subscriber_counts.remove(channel);
142            }
143        }
144    }
145
146    pub fn increment_pattern_subscribers(&self, pattern: &[u8]) {
147        *self
148            .pattern_subscriber_counts
149            .entry(pattern.to_vec())
150            .or_insert(0) += 1;
151    }
152
153    pub fn decrement_pattern_subscribers(&self, pattern: &[u8]) {
154        if let Some(mut count) = self.pattern_subscriber_counts.get_mut(pattern) {
155            if *count > 0 {
156                *count -= 1;
157            }
158            if *count == 0 {
159                drop(count);
160                self.pattern_subscriber_counts.remove(pattern);
161            }
162        }
163    }
164
165    pub fn get_pattern_subscriber_count(&self, pattern: &[u8]) -> usize {
166        self.pattern_subscriber_counts
167            .get(pattern)
168            .map(|entry| *entry)
169            .unwrap_or(0)
170    }
171
172    pub fn get_total_pattern_matches(&self, channel: &[u8]) -> usize {
173        // Count all pattern subscribers that might match this channel
174        // For simplicity, count all pattern subscribers since we can't check patterns here
175        let mut total = 0;
176        for entry in self.pattern_subscriber_counts.iter() {
177            // Check if pattern matches channel using glob-style matching
178            if Self::glob_match(entry.key(), channel) {
179                total += *entry.value();
180            }
181        }
182        total
183    }
184
185    pub fn glob_match(pattern: &[u8], text: &[u8]) -> bool {
186        let mut p = 0;
187        let mut t = 0;
188        let mut star_idx = None;
189        let mut match_idx = 0;
190
191        while t < text.len() {
192            if p < pattern.len() && (pattern[p] == text[t] || pattern[p] == b'?') {
193                p += 1;
194                t += 1;
195            } else if p < pattern.len() && pattern[p] == b'*' {
196                star_idx = Some(p);
197                match_idx = t;
198                p += 1;
199            } else if let Some(idx) = star_idx {
200                p = idx + 1;
201                match_idx += 1;
202                t = match_idx;
203            } else {
204                return false;
205            }
206        }
207
208        while p < pattern.len() && pattern[p] == b'*' {
209            p += 1;
210        }
211
212        p == pattern.len()
213    }
214
215    pub fn get_pattern_count(&self) -> usize {
216        self.pattern_to_threads.len()
217    }
218}