feox_server/pubsub/
registry.rs1use 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 let mut total = 0;
176 for entry in self.pattern_subscriber_counts.iter() {
177 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}