1use super::resp_value::Resp;
42use crate::utils::redis_keys::glob_match;
43use bytes::Bytes;
44use std::collections::{BTreeSet, HashMap, HashSet};
45use std::sync::atomic::{AtomicU8, AtomicUsize, Ordering};
46use std::sync::{Arc, Mutex};
47use tokio::sync::{mpsc, oneshot, watch};
48use tracing::warn;
49
50pub const MAX_CHANNEL_BYTES: usize = 1024;
52pub const MAX_PATTERN_BYTES: usize = 256;
54pub const MAX_MESSAGE_BYTES: usize = 1024 * 1024;
56pub const MAX_SUBSCRIPTIONS_PER_CONNECTION: usize = 1024;
58pub const MAX_SUBSCRIPTIONS_PER_SERVER: usize = 65_536;
60pub const MAX_PATTERNS_PER_SERVER: usize = 1024;
62pub const SUBSCRIBER_OUTPUT_LIMIT_BYTES: usize = 8 * 1024 * 1024;
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub enum Kind {
68 Channel,
70 Pattern,
72 Shard,
74}
75
76impl Kind {
77 fn subscribe_word(self) -> &'static str {
78 match self {
79 Kind::Channel => "subscribe",
80 Kind::Pattern => "psubscribe",
81 Kind::Shard => "ssubscribe",
82 }
83 }
84 fn unsubscribe_word(self) -> &'static str {
85 match self {
86 Kind::Channel => "unsubscribe",
87 Kind::Pattern => "punsubscribe",
88 Kind::Shard => "sunsubscribe",
89 }
90 }
91 fn max_name_bytes(self) -> usize {
92 match self {
93 Kind::Pattern => MAX_PATTERN_BYTES,
94 _ => MAX_CHANNEL_BYTES,
95 }
96 }
97}
98
99pub struct OutFrame {
105 pub bytes: Bytes,
106 pub counted: bool,
110 pub ack: Option<oneshot::Sender<std::io::Result<()>>>,
112}
113
114#[derive(Clone)]
116pub struct Outbox {
117 tx: mpsc::UnboundedSender<OutFrame>,
118 queued: Arc<AtomicUsize>,
119 limit: usize,
120 killed: Arc<watch::Sender<bool>>,
121}
122
123pub struct OutboxReceiver {
125 pub rx: mpsc::UnboundedReceiver<OutFrame>,
126 pub queued: Arc<AtomicUsize>,
127}
128
129impl Outbox {
130 pub fn new() -> (Outbox, OutboxReceiver) {
131 Self::with_limit(SUBSCRIBER_OUTPUT_LIMIT_BYTES)
132 }
133
134 pub fn with_limit(limit: usize) -> (Outbox, OutboxReceiver) {
135 let (tx, rx) = mpsc::unbounded_channel();
136 let queued = Arc::new(AtomicUsize::new(0));
137 let (killed, _) = watch::channel(false);
138 (
139 Outbox {
140 tx,
141 queued: queued.clone(),
142 limit,
143 killed: Arc::new(killed),
144 },
145 OutboxReceiver { rx, queued },
146 )
147 }
148
149 pub async fn closing(&self) {
153 let mut rx = self.killed.subscribe();
154 let _ = rx.wait_for(|k| *k).await;
155 }
156
157 pub fn is_closing(&self) -> bool {
158 *self.killed.borrow()
159 }
160
161 pub async fn send_reply(&self, bytes: Vec<u8>) -> std::io::Result<()> {
165 let (ack, done) = oneshot::channel();
166 self.tx
167 .send(OutFrame {
168 bytes: Bytes::from(bytes),
169 counted: false,
170 ack: Some(ack),
171 })
172 .map_err(|_| std::io::Error::other("connection writer has stopped"))?;
173 tokio::select! {
174 written = done => written
175 .unwrap_or_else(|_| Err(std::io::Error::other("connection writer has stopped"))),
176 _ = self.closing() => Err(std::io::Error::other("connection is closing")),
177 }
178 }
179
180 fn queue_reply(&self, bytes: Bytes) {
183 let _ = self.tx.send(OutFrame {
184 bytes,
185 counted: false,
186 ack: None,
187 });
188 }
189
190 fn deliver(&self, bytes: Bytes) -> Delivery {
193 if self.is_closing() {
194 return Delivery::Gone;
195 }
196 let len = bytes.len();
197 let before = self.queued.fetch_add(len, Ordering::AcqRel);
198 if before + len > self.limit {
199 self.queued.fetch_sub(len, Ordering::AcqRel);
200 self.mark_killed();
201 return Delivery::Overflow;
202 }
203 if self
204 .tx
205 .send(OutFrame {
206 bytes,
207 counted: true,
208 ack: None,
209 })
210 .is_err()
211 {
212 self.queued.fetch_sub(len, Ordering::AcqRel);
213 return Delivery::Gone;
214 }
215 Delivery::Queued
216 }
217
218 pub fn mark_killed(&self) {
220 self.killed.send_replace(true);
221 }
222
223 pub fn queued_bytes(&self) -> usize {
225 self.queued.load(Ordering::Acquire)
226 }
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq)]
231enum Delivery {
232 Queued,
233 Overflow,
235 Gone,
237}
238
239struct Subscriber {
244 outbox: Outbox,
245 proto: Arc<AtomicU8>,
248 channels: BTreeSet<Vec<u8>>,
249 patterns: BTreeSet<Vec<u8>>,
250 shard: BTreeSet<Vec<u8>>,
251}
252
253impl Subscriber {
254 fn set(&self, kind: Kind) -> &BTreeSet<Vec<u8>> {
255 match kind {
256 Kind::Channel => &self.channels,
257 Kind::Pattern => &self.patterns,
258 Kind::Shard => &self.shard,
259 }
260 }
261 fn set_mut(&mut self, kind: Kind) -> &mut BTreeSet<Vec<u8>> {
262 match kind {
263 Kind::Channel => &mut self.channels,
264 Kind::Pattern => &mut self.patterns,
265 Kind::Shard => &mut self.shard,
266 }
267 }
268 fn total(&self) -> usize {
269 self.channels.len() + self.patterns.len() + self.shard.len()
270 }
271 fn reported(&self, kind: Kind) -> i64 {
274 match kind {
275 Kind::Shard => self.shard.len() as i64,
276 _ => (self.channels.len() + self.patterns.len()) as i64,
277 }
278 }
279}
280
281#[derive(Default)]
282struct HubInner {
283 subscribers: HashMap<u32, Subscriber>,
284 channels: HashMap<Vec<u8>, BTreeSet<u32>>,
285 patterns: HashMap<Vec<u8>, BTreeSet<u32>>,
286 shard: HashMap<Vec<u8>, BTreeSet<u32>>,
287 total: usize,
288}
289
290impl HubInner {
291 fn index_mut(&mut self, kind: Kind) -> &mut HashMap<Vec<u8>, BTreeSet<u32>> {
292 match kind {
293 Kind::Channel => &mut self.channels,
294 Kind::Pattern => &mut self.patterns,
295 Kind::Shard => &mut self.shard,
296 }
297 }
298 fn index(&self, kind: Kind) -> &HashMap<Vec<u8>, BTreeSet<u32>> {
299 match kind {
300 Kind::Channel => &self.channels,
301 Kind::Pattern => &self.patterns,
302 Kind::Shard => &self.shard,
303 }
304 }
305
306 fn drop_subscriber(&mut self, conn: u32) {
307 let Some(sub) = self.subscribers.remove(&conn) else {
308 return;
309 };
310 for kind in [Kind::Channel, Kind::Pattern, Kind::Shard] {
311 for name in sub.set(kind) {
312 let index = self.index_mut(kind);
313 if let Some(set) = index.get_mut(name) {
314 set.remove(&conn);
315 if set.is_empty() {
316 index.remove(name);
317 }
318 }
319 }
320 }
321 self.total -= sub.total();
322 }
323}
324
325#[derive(Default)]
327pub struct PubSubHub {
328 inner: Mutex<HubInner>,
329}
330
331fn message_frame(
333 kind: Kind,
334 pattern: Option<&[u8]>,
335 channel: &[u8],
336 message: &[u8],
337 proto: u8,
338) -> Bytes {
339 let mut items = vec![Resp::bulk(match (kind, pattern) {
340 (Kind::Shard, _) => "smessage",
341 (_, Some(_)) => "pmessage",
342 _ => "message",
343 })];
344 if let Some(p) = pattern {
345 items.push(Resp::bulk(p));
346 }
347 items.push(Resp::bulk(channel));
348 items.push(Resp::bulk(message));
349 Bytes::from(Resp::Push(items).encode(proto))
350}
351
352fn confirmation(word: &str, name: Option<&[u8]>, count: i64, proto: u8) -> Bytes {
353 Bytes::from(
354 Resp::Push(vec![
355 Resp::bulk(word),
356 name.map(Resp::bulk).unwrap_or(Resp::Null),
357 Resp::Int(count),
358 ])
359 .encode(proto),
360 )
361}
362
363pub fn check_name(kind: Kind, name: &[u8]) -> Result<(), String> {
365 if name.len() > kind.max_name_bytes() {
366 let what = if kind == Kind::Pattern {
367 "pattern"
368 } else {
369 "channel name"
370 };
371 return Err(format!(
372 "ERR {what} longer than {} bytes",
373 kind.max_name_bytes()
374 ));
375 }
376 Ok(())
377}
378
379pub fn check_message(message: &[u8]) -> Result<(), String> {
381 if message.len() > MAX_MESSAGE_BYTES {
382 return Err(format!("ERR message longer than {MAX_MESSAGE_BYTES} bytes"));
383 }
384 Ok(())
385}
386
387impl PubSubHub {
388 fn lock(&self) -> std::sync::MutexGuard<'_, HubInner> {
389 self.inner
390 .lock()
391 .unwrap_or_else(|poisoned| poisoned.into_inner())
392 }
393
394 pub fn check_subscribe(&self, conn: u32, kind: Kind, names: &[Vec<u8>]) -> Result<(), String> {
398 let inner = self.lock();
399 Self::check_locked(&inner, conn, kind, names)
400 }
401
402 fn check_locked(
403 inner: &HubInner,
404 conn: u32,
405 kind: Kind,
406 names: &[Vec<u8>],
407 ) -> Result<(), String> {
408 for name in names {
409 check_name(kind, name)?;
410 }
411 let existing = inner.subscribers.get(&conn);
412 let new: HashSet<&Vec<u8>> = names
413 .iter()
414 .filter(|n| existing.is_none_or(|s| !s.set(kind).contains(*n)))
415 .collect();
416 let held = existing.map(Subscriber::total).unwrap_or(0);
417 if held + new.len() > MAX_SUBSCRIPTIONS_PER_CONNECTION {
418 return Err(format!(
419 "ERR max subscriptions per connection reached ({MAX_SUBSCRIPTIONS_PER_CONNECTION})"
420 ));
421 }
422 if inner.total + new.len() > MAX_SUBSCRIPTIONS_PER_SERVER {
423 return Err(format!(
424 "ERR max subscriptions per server reached ({MAX_SUBSCRIPTIONS_PER_SERVER})"
425 ));
426 }
427 if kind == Kind::Pattern {
428 let distinct_new = new
429 .iter()
430 .filter(|p| !inner.patterns.contains_key(**p))
431 .count();
432 if inner.patterns.len() + distinct_new > MAX_PATTERNS_PER_SERVER {
433 return Err(format!(
434 "ERR max distinct patterns per server reached ({MAX_PATTERNS_PER_SERVER})"
435 ));
436 }
437 }
438 Ok(())
439 }
440
441 pub fn subscribe(
445 &self,
446 conn: u32,
447 outbox: &Outbox,
448 proto: &Arc<AtomicU8>,
449 kind: Kind,
450 names: &[Vec<u8>],
451 ) -> Result<(), String> {
452 let mut inner = self.lock();
453 Self::check_locked(&inner, conn, kind, names)?;
454 let version = proto.load(Ordering::Relaxed);
455 inner.subscribers.entry(conn).or_insert_with(|| Subscriber {
456 outbox: outbox.clone(),
457 proto: proto.clone(),
458 channels: BTreeSet::new(),
459 patterns: BTreeSet::new(),
460 shard: BTreeSet::new(),
461 });
462 for name in names {
463 let added = inner
464 .subscribers
465 .get_mut(&conn)
466 .map(|s| s.set_mut(kind).insert(name.clone()))
467 .unwrap_or(false);
468 if added {
469 inner.total += 1;
470 inner
471 .index_mut(kind)
472 .entry(name.clone())
473 .or_default()
474 .insert(conn);
475 }
476 let count = inner
477 .subscribers
478 .get(&conn)
479 .map(|s| s.reported(kind))
480 .unwrap_or(0);
481 outbox.queue_reply(confirmation(
482 kind.subscribe_word(),
483 Some(name),
484 count,
485 version,
486 ));
487 }
488 Ok(())
489 }
490
491 pub fn unsubscribe(
495 &self,
496 conn: u32,
497 outbox: &Outbox,
498 proto: u8,
499 kind: Kind,
500 names: &[Vec<u8>],
501 ) {
502 let mut inner = self.lock();
503 let targets: Vec<Vec<u8>> = if names.is_empty() {
504 inner
505 .subscribers
506 .get(&conn)
507 .map(|s| s.set(kind).iter().cloned().collect())
508 .unwrap_or_default()
509 } else {
510 names.to_vec()
511 };
512 if targets.is_empty() {
513 let count = inner
514 .subscribers
515 .get(&conn)
516 .map(|s| s.reported(kind))
517 .unwrap_or(0);
518 outbox.queue_reply(confirmation(kind.unsubscribe_word(), None, count, proto));
519 return;
520 }
521 for name in targets {
522 let removed = inner
523 .subscribers
524 .get_mut(&conn)
525 .map(|s| s.set_mut(kind).remove(&name))
526 .unwrap_or(false);
527 if removed {
528 inner.total -= 1;
529 let index = inner.index_mut(kind);
530 if let Some(set) = index.get_mut(&name) {
531 set.remove(&conn);
532 if set.is_empty() {
533 index.remove(&name);
534 }
535 }
536 }
537 let count = inner
538 .subscribers
539 .get(&conn)
540 .map(|s| s.reported(kind))
541 .unwrap_or(0);
542 outbox.queue_reply(confirmation(
543 kind.unsubscribe_word(),
544 Some(&name),
545 count,
546 proto,
547 ));
548 }
549 if inner.subscribers.get(&conn).is_some_and(|s| s.total() == 0) {
550 inner.subscribers.remove(&conn);
551 }
552 }
553
554 pub fn remove_connection(&self, conn: u32) {
557 self.lock().drop_subscriber(conn);
558 }
559
560 pub fn subscription_count(&self, conn: u32) -> usize {
563 self.lock()
564 .subscribers
565 .get(&conn)
566 .map(Subscriber::total)
567 .unwrap_or(0)
568 }
569
570 pub fn receivers(&self, channel: &[u8], shard: bool) -> usize {
572 let inner = self.lock();
573 if shard {
574 return inner.shard.get(channel).map(BTreeSet::len).unwrap_or(0);
575 }
576 inner.channels.get(channel).map(BTreeSet::len).unwrap_or(0)
577 + inner
578 .patterns
579 .iter()
580 .filter(|(p, _)| glob_match(p, channel))
581 .map(|(_, subs)| subs.len())
582 .sum::<usize>()
583 }
584
585 pub fn publish(&self, channel: &[u8], message: &[u8], shard: bool) -> usize {
592 let mut inner = self.lock();
593 let mut delivered = 0;
594 let mut slow: Vec<u32> = Vec::new();
595 let mut gone: Vec<u32> = Vec::new();
596 let mut cache: HashMap<(Option<Vec<u8>>, u8), Bytes> = HashMap::new();
598 let kind = if shard { Kind::Shard } else { Kind::Channel };
599 let mut targets: Vec<(u32, Option<Vec<u8>>)> = inner
600 .index(kind)
601 .get(channel)
602 .map(|subs| subs.iter().map(|c| (*c, None)).collect())
603 .unwrap_or_default();
604 if !shard {
605 let mut matched: Vec<(&Vec<u8>, &BTreeSet<u32>)> = inner
606 .patterns
607 .iter()
608 .filter(|(p, _)| glob_match(p, channel))
609 .collect();
610 matched.sort_by(|a, b| a.0.cmp(b.0));
611 for (pattern, subs) in matched {
612 targets.extend(subs.iter().map(|c| (*c, Some(pattern.clone()))));
613 }
614 }
615 for (conn, pattern) in targets {
616 let Some(sub) = inner.subscribers.get(&conn) else {
617 continue;
618 };
619 let proto = sub.proto.load(Ordering::Relaxed);
620 let frame = cache
621 .entry((pattern.clone(), proto))
622 .or_insert_with(|| message_frame(kind, pattern.as_deref(), channel, message, proto))
623 .clone();
624 match sub.outbox.deliver(frame) {
625 Delivery::Queued => delivered += 1,
626 Delivery::Overflow if !slow.contains(&conn) => slow.push(conn),
627 Delivery::Gone if !gone.contains(&conn) => gone.push(conn),
628 _ => {}
629 }
630 }
631 for conn in gone {
632 inner.drop_subscriber(conn);
633 }
634 for conn in slow {
635 warn!(
636 "Redis connection {} decision=slow_consumer_disconnected: more than {} bytes of \
637 pub/sub messages queued unread; dropping its subscriptions and closing it \
638 (Redis's client-output-buffer-limit pubsub)",
639 conn, SUBSCRIBER_OUTPUT_LIMIT_BYTES
640 );
641 inner.drop_subscriber(conn);
642 }
643 delivered
644 }
645
646 pub fn active_channels(&self, shard: bool, pattern: Option<&[u8]>) -> Vec<Vec<u8>> {
648 let inner = self.lock();
649 let index = if shard { &inner.shard } else { &inner.channels };
650 let mut names: Vec<Vec<u8>> = index
651 .keys()
652 .filter(|c| pattern.is_none_or(|p| glob_match(p, c)))
653 .cloned()
654 .collect();
655 names.sort();
656 names
657 }
658
659 pub fn numsub(&self, shard: bool, channels: &[Vec<u8>]) -> Vec<(Vec<u8>, usize)> {
661 let inner = self.lock();
662 let index = if shard { &inner.shard } else { &inner.channels };
663 channels
664 .iter()
665 .map(|c| (c.clone(), index.get(c).map(BTreeSet::len).unwrap_or(0)))
666 .collect()
667 }
668
669 pub fn numpat(&self) -> usize {
671 self.lock().patterns.len()
672 }
673
674 pub fn snapshot(&self) -> serde_json::Value {
676 let inner = self.lock();
677 let names = |index: &HashMap<Vec<u8>, BTreeSet<u32>>| {
678 let mut v: Vec<serde_json::Value> = index
679 .iter()
680 .map(|(name, subs)| {
681 serde_json::json!({
682 "name": String::from_utf8_lossy(name),
683 "subscribers": subs.len(),
684 })
685 })
686 .collect();
687 v.sort_by(|a, b| a["name"].as_str().cmp(&b["name"].as_str()));
688 v
689 };
690 serde_json::json!({
691 "channels": names(&inner.channels),
692 "patterns": names(&inner.patterns),
693 "shard_channels": names(&inner.shard),
694 "subscribed_connections": inner.subscribers.len(),
695 "total_subscriptions": inner.total,
696 })
697 }
698}