The Problem
Messages published to Redis channels don't reach subscribers. Applications relying on Pub/Sub for real-time notifications miss critical events. Subscribers appear connected but receive no messages, or messages arrive sporadically.
Understanding Redis Pub/Sub
Redis Pub/Sub features: - Publish/Subscribe pattern - One publisher, multiple subscribers - Channels - Named message destinations - Patterns - Subscribe to channel patterns (PSUBSCRIBE) - No persistence - Messages not stored, only delivered to active subscribers - Fire-and-forget - If no subscribers, message is dropped
Key commands:
- PUBLISH channel message - Send message
- SUBSCRIBE channel [channel...] - Subscribe to channels
- PSUBSCRIBE pattern [pattern...] - Subscribe to patterns
- PUBSUB CHANNELS - List active channels
- PUBSUB NUMSUB channel - Count subscribers
Diagnosis Commands
Check Active Channels
redis-cli PUBSUB CHANNELSOutput shows channels with active subscribers:
notifications
alerts
events:user1Check Subscriber Count
redis-cli PUBSUB NUMSUB mychannelOutput:
mychannel 5Shows channel name and subscriber count.
Check Pattern Subscriptions
redis-cli PUBSUB NUMPATReturns count of pattern subscriptions across all clients.
List Connected Clients
redis-cli CLIENT LIST | grep -E 'sub=[1-9]|psub=[1-9]'Shows clients with subscriptions. Key fields:
- sub - Number of channel subscriptions
- psub - Number of pattern subscriptions
- flags - Includes P for pub/sub client
Test Message Delivery
```bash # Terminal 1: Subscribe redis-cli SUBSCRIBE testchannel
# Terminal 2: Publish redis-cli PUBLISH testchannel "hello"
# Terminal 1 should receive: # 1) "message" # 2) "testchannel" # 3) "hello" ```
Common Pub/Sub Issues
Issue 1: Subscriber Connected Too Late
Messages published before subscription are lost.
Problem:
```bash # Publisher sends message redis-cli PUBLISH alerts "system_down"
# Subscriber connects AFTER message sent redis-cli SUBSCRIBE alerts # Never receives "system_down" message ```
Solution:
Redis Pub/Sub is real-time only. Use Redis Streams for persistence:
```bash # Use streams for persisted messages redis-cli XADD alerts * message system_down
# Consumer reads missed messages redis-cli XREAD STREAMS alerts 0 ```
Issue 2: Wrong Channel Name
Channel names must match exactly.
Problem:
```bash # Publisher redis-cli PUBLISH user:123:notifications "new_message"
# Subscriber subscribes to wrong pattern redis-cli SUBSCRIBE user:notifications # Doesn't match - no message received ```
Solution:
Verify channel names:
```bash # Check active channels redis-cli PUBSUB CHANNELS "user:*"
# Subscribe to exact channel redis-cli SUBSCRIBE user:123:notifications
# Or use pattern subscription redis-cli PSUBSCRIBE user:*:notifications ```
Issue 3: Pattern Subscription Mismatch
Pattern subscriptions have different message format.
Problem:
```bash # Subscriber uses pattern redis-cli PSUBSCRIBE user:*
# Message format is different: # 1) "pmessage" <- Pattern message type # 2) "user:*" <- Pattern matched # 3) "user:123" <- Actual channel # 4) "hello" <- Message
# Application expecting 3-element format fails ```
Solution:
Handle both message formats:
```python import redis
r = redis.Redis()
pubsub = r.pubsub() pubsub.psubscribe('user:*')
for message in pubsub.listen(): if message['type'] == 'pmessage': pattern = message['pattern'] # user:* channel = message['channel'] # user:123 data = message['data'] # hello elif message['type'] == 'message': channel = message['channel'] data = message['data'] ```
Issue 4: Blocked Client Can't Issue Commands
Subscribed clients can only receive, not execute other commands.
Problem:
```bash redis-cli SUBSCRIBE mychannel # Now blocked - can't issue GET, SET, etc.
redis-cli GET somekey (error) ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context ```
Solution:
Use separate connections for pub/sub and commands:
```python import redis
# Connection for regular commands r = redis.Redis()
# Separate connection for pub/sub pubsub_client = redis.Redis() pubsub = pubsub_client.pubsub() pubsub.subscribe('mychannel')
# Can use r for commands while pubsub_client receives messages r.set('key', 'value')
for message in pubsub.listen(): if message['type'] == 'message': # Use r connection for database operations r.set('last_message', message['data']) ```
Issue 5: Client Disconnected Silently
Network issues drop pub/sub connections without notification.
Diagnosis:
# Check if client still subscribed
redis-cli CLIENT LIST | grep -E 'sub=[1-9]'Solution:
Implement health checks and reconnect:
```python import redis import time import threading
class ReliablePubSub: def __init__(self, host='localhost', port=6379): self.host = host self.port = port self.channels = [] self.patterns = [] self._stop = threading.Event()
def subscribe(self, *channels): self.channels.extend(channels)
def psubscribe(self, *patterns): self.patterns.extend(patterns)
def run(self, handler): while not self._stop.is_set(): try: client = redis.Redis(host=self.host, port=self.port) pubsub = client.pubsub()
if self.channels: pubsub.subscribe(*self.channels) if self.patterns: pubsub.psubscribe(*self.patterns)
# Health check thread def health_check(): while not self._stop.is_set(): try: client.ping() except redis.ConnectionError: break time.sleep(30)
health_thread = threading.Thread(target=health_check) health_thread.start()
for message in pubsub.listen(): if message['type'] in ('message', 'pmessage'): handler(message)
except redis.ConnectionError: print("Connection lost, reconnecting...") time.sleep(5)
def stop(self): self._stop.set() ```
Issue 6: High Message Volume Overwhelms Subscriber
Fast publishing exceeds subscriber processing capacity.
Diagnosis:
# Check publish rate
redis-cli INFO stats | grep total_commands_processedSolution:
- 1.Use batch processing:
```python import redis
r = redis.Redis() pubsub = r.pubsub() pubsub.subscribe('high_volume_channel')
# Process in batches batch = [] batch_size = 100
for message in pubsub.listen(): if message['type'] == 'message': batch.append(message['data'])
if len(batch) >= batch_size: process_batch(batch) batch = [] ```
- 1.Use Redis Streams instead:
# Streams support consumer groups for parallel processing
redis-cli XADD events * data message_contentIssue 7: Buffer Overflow Disconnection
Pub/sub clients with high output buffers get disconnected.
Diagnosis:
```bash # Check client output buffer redis-cli CLIENT LIST | grep -E 'sub=[1-9]'
# Check pub/sub buffer limits redis-cli CONFIG GET client-output-buffer-limit | grep pubsub ```
Solution:
Increase buffer limits:
redis-cli CONFIG SET client-output-buffer-limit "pubsub 256mb 64mb 60"Or in redis.conf:
client-output-buffer-limit pubsub 256mb 64mb 60Testing Pub/Sub Reliability
Comprehensive Test
```bash #!/bin/bash # test_pubsub.sh
CHANNEL="test_channel_$(date +%s)"
# Start subscriber in background (redis-cli SUBSCRIBE $CHANNEL > /tmp/sub_output.txt &) & SUB_PID=$!
sleep 1
# Publish test message redis-cli PUBLISH $CHANNEL "test_message_1"
sleep 1
# Check subscriber received message if grep -q "test_message_1" /tmp/sub_output.txt; then echo "SUCCESS: Message received" else echo "FAIL: Message not received" fi
# Cleanup redis-cli CLIENT KILL SKIPME yes kill $SUB_PID 2>/dev/null rm /tmp/sub_output.txt ```
Pattern Test
```bash # Subscribe to pattern redis-cli PSUBSCRIBE "events:*" & SUB_PID=$!
sleep 1
# Publish to matching channel redis-cli PUBLISH "events:user1" "hello" redis-cli PUBLISH "events:system" "alert"
sleep 1
# Verify pattern matching works # Messages should appear with "pmessage" type ```
Monitoring Pub/Sub Health
Monitor Script
```bash #!/bin/bash # Monitor pub/sub metrics
while true; do CHANNELS=$(redis-cli PUBSUB CHANNELS | wc -l) NUMPAT=$(redis-cli PUBSUB NUMPAT) PUB_CLIENTS=$(redis-cli CLIENT LIST | grep -c 'flags=.*P')
echo "$(date): Channels=$CHANNELS Patterns=$NUMPAT PubClients=$PUB_CLIENTS"
# Check for clients with no subscriptions ORPHAN=$(redis-cli CLIENT LIST | grep 'flags=.*P' | grep -E 'sub=0' | grep -E 'psub=0') if [ -n "$ORPHAN" ]; then echo "WARNING: Pub/sub clients with no subscriptions" fi
sleep 60 done ```
Configuration Best Practices
```conf # redis.conf
# Pub/sub buffer limits client-output-buffer-limit pubsub 32mb 8mb 60
# TCP keepalive for connection health tcp-keepalive 300
# Client timeout (0 = no timeout for pub/sub) timeout 0 ```
Alternative: Redis Streams
For reliable message delivery, consider Redis Streams:
```bash # Streams provide persistence and consumer groups redis-cli XADD notifications * message content
# Multiple consumers can process reliably redis-cli XGROUP CREATE notifications mygroup 0 MKSTREAM redis-cli XREADGROUP GROUP mygroup consumer1 STREAMS notifications > redis-cli XACK notifications mygroup <message_id> ```
Benefits: - Messages persist until explicitly deleted - Consumer groups support multiple workers - Can recover missed messages - Track processing state
Verification
After fixing issues:
```bash # Test basic pub/sub redis-cli SUBSCRIBE verification_test & sleep 1 redis-cli PUBLISH verification_test "verify" sleep 1
# Check subscriber count redis-cli PUBSUB NUMSUB verification_test # Should show 1 subscriber
# Cleanup redis-cli UNSUBSCRIBE verification_test ```