How Buzz Uses Redis for Pub/Sub: Architecture and Implementation Guide

Buzz implements real-time messaging through a dedicated buzz-pubsub crate that wraps Redis Pub/Sub, providing publishers, subscribers, presence tracking, rate limiting, and cache invalidation through a shared connection pool managed by PubSubManager.

The block/buzz repository relies on Redis as the backbone for its real-time event distribution. Rather than scattering Redis client code throughout the codebase, the project centralizes all pub/sub logic in the buzz-pubsub crate. This design allows the relay service (buzz-relay) and other components to broadcast Nostr events, track user presence, and enforce rate limits without managing raw Redis connections directly.

Core Pub/Sub Architecture

PubSubManager

The entry point for all Redis operations is PubSubManager, defined in crates/buzz-pubsub/src/lib.rs. This struct maintains a shared Redis connection pool and acts as a factory for publishers and subscribers. It reads configuration from the environment variable REDIS_URL (e.g., redis://:password@redis:6379/0) through the PubSubConfig struct, which supports optional authentication and database selection.

use buzz_pubsub::PubSubManager;

// Initialize from environment (reads REDIS_URL)
let manager = PubSubManager::new_from_env()?;

// Create publisher and subscriber instances on demand
let publisher = manager.publisher(topic_key);
let subscriber = manager.subscriber(topic_key);

Connection Lifecycle

The manager lazily initializes connections from the pool, ensuring that services only hold Redis connections when actively publishing or subscribing. This approach keeps the relay lightweight during idle periods while maintaining low-latency access when event throughput spikes.

Publishing and Subscribing to Events

The Publisher Component

Located in crates/buzz-pubsub/src/publisher.rs, the Publisher struct serializes Nostr events into JSON payloads and broadcasts them to Redis channels. The channel name is deterministic, generated by EventTopicKey::redis_channel() in crates/buzz-pubsub/src/topic.rs based on the event's topic (such as a channel UUID).

use buzz_pubsub::{PubSubManager, Publisher};
use buzz_core::event::Event;

let manager = PubSubManager::new_from_env()?;
let topic_key = EventTopicKey::for_channel(channel_uuid);
let publisher: Publisher = manager.publisher(topic_key);

// Serialize and publish
let nostr_event = Event::new(...);
publisher.publish(&nostr_event)?;

The Subscriber Component

The Subscriber struct in crates/buzz-pubsub/src/subscriber.rs connects to the same Redis channel, deserializes incoming JSON into Nostr events, and forwards them to the relay's event-handling pipeline. It implements a streaming interface compatible with asynchronous Rust patterns.

use buzz_pubsub::{PubSubManager, Subscriber};

let manager = PubSubManager::new_from_env()?;
let topic_key = EventTopicKey::for_channel(channel_uuid);
let mut subscriber: Subscriber = manager.subscriber(topic_key);

tokio::spawn(async move {
    while let Some(msg) = subscriber.next().await {
        match msg {
            Ok(event) => handle_event(event),
            Err(e) => eprintln!("PubSub error: {}", e),
        }
    }
});

Topic Key Generation

EventTopicKey in crates/buzz-pubsub/src/topic.rs ensures that channels follow a consistent naming convention, preventing collisions between different event types while enabling efficient pattern-based subscriptions where needed.

Advanced Redis Patterns in Buzz

Presence Tracking

The Presence service in crates/buzz-pubsub/src/presence.rs uses a separate Redis key-space to track online users per community. It publishes lightweight heartbeat events and maintains an in-memory cache that the UI queries for real-time user lists.

use buzz_pubsub::Presence;

let presence = Presence::new_from_env()?;
presence.heartbeat(user_id, community_id)?;

Sliding-Window Rate Limiting

RedisRateLimiter in crates/buzz-pubsub/src/rate_limiter.rs implements distributed rate limiting using Redis sorted sets. It stores timestamps in a sliding window keyed by user and action, atomically checking whether new requests exceed configured limits before allowing execution.

use buzz_pubsub::RedisRateLimiter;
use std::time::Duration;

let limiter = RedisRateLimiter::new_from_env()?;
let key = format!("user:{}:send_message", user_id);

if limiter.allow(&key, 5, Duration::from_secs(60))? {
    // Proceed - user under limit
} else {
    // Reject - rate limit exceeded
}

Cache Invalidation

ScopedCacheInvalidation in crates/buzz-pubsub/src/cache_invalidation.rs publishes invalidation messages across the cluster. Downstream services listen to these messages to clear stale data from local caches, ensuring consistency in distributed deployments.

Configuration and Deployment

Buzz expects a single Redis instance configured via the REDIS_URL environment variable. For local development, docker-compose.yml declares a redis:7-alpine service, while production deployments use deploy/compose/compose.yml with password protection and persistence enabled. The buzz-pubsub crate ships with a mock implementation for unit testing, allowing services to test pub/sub logic without running a live Redis server.

Summary

  • Centralized Abstraction: All Redis pub/sub logic lives in the buzz-pubsub crate, exposing PubSubManager, Publisher, and Subscriber types.

  • Event-Driven Architecture: Nostr events serialize to JSON and publish to deterministic channels via crates/buzz-pubsub/src/publisher.rs, with deserialization handled by crates/buzz-pubsub/src/subscriber.rs.

  • Topic Management: EventTopicKey::redis_channel() in crates/buzz-pubsub/src/topic.rs generates consistent channel names for routing.

  • Auxiliary Services: Redis supports presence tracking (presence.rs), sliding-window rate limiting (rate_limiter.rs), and distributed cache invalidation (cache_invalidation.rs).

  • Configuration: The system reads REDIS_URL for connection details, with Docker Compose files providing development and production Redis deployments.

Frequently Asked Questions

How does Buzz handle Redis connection pooling?

PubSubManager in crates/buzz-pubsub/src/lib.rs maintains a shared connection pool that it lazily initializes from the REDIS_URL environment variable. Publishers and subscribers borrow connections from this pool on demand, ensuring efficient resource usage across the relay and supporting services.

What Redis data structures does Buzz use beyond pub/sub channels?

Beyond standard pub/sub, Buzz uses Redis sorted sets for rate limiting (RedisRateLimiter in crates/buzz-pubsub/src/rate_limiter.rs) and simple key-value storage for presence tracking (Presence in crates/buzz-pubsub/src/presence.rs). Cache invalidation uses the pub/sub mechanism to broadcast clear commands across the cluster.

Can Buzz work with Redis Sentinel or Cluster mode?

The current implementation in buzz-pubsub targets a single Redis instance as configured in docker-compose.yml and deploy/compose/compose.yml. While the abstraction layer could theoretically support Sentinel or Cluster configurations through connection string updates, the source code shows connections established via standard REDIS_URL pointing to a single primary instance.

How does Buzz ensure message ordering in Redis pub/sub?

Redis guarantees that messages arrive to subscribers in the order published to a specific channel. Buzz leverages this by publishing serialized Nostr events atomically through Publisher::publish() in crates/buzz-pubsub/src/publisher.rs. The Subscriber processes messages sequentially as they stream from Redis, maintaining event order for clients connected to the same relay instance.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →