How Buzz Implements Its Subscription System: A Deep Dive into the Relay Registry
Buzz implements its subscription system as a thread-safe, lock-free registry in crates/buzz-relay/src/subscription.rs that uses specialized DashMap indexes to achieve sub-linear fan-out of Nostr events to matching WebSocket connections.
The subscription system is the core message routing engine of block/buzz, a Nostr relay implementation written in Rust. It maintains every client-side REQ subscription in memory and determines precisely which WebSocket connections should receive each incoming event without scanning the entire connection pool. This architecture enables the relay to handle high-throughput workloads while respecting NIP-01 subscription semantics and strict scoping invariants.
Core Data Structures and Identifiers
The registry centers on two primary identifiers defined at lines 11-14: ConnId, a UUID that uniquely identifies a WebSocket connection, and SubId, a client-provided string that identifies a specific subscription within that connection.
Every subscription operates within a defined SubscriptionScope, an enum at lines 18-25 that distinguishes between Global subscriptions (no channel restriction) and Channels subscriptions (limited to specific channel UUIDs). This scope determines which indexes the registry queries during event fan-out.
To enable O(1) look-ups, the system uses an IndexKey struct (lines 50-57) composed of a channel_id: Uuid and a kind: Kind. These keys populate multiple concurrent hash maps that index subscriptions by their filter constraints.
Registry Architecture and Indexing Strategy
The SubscriptionRegistry struct (lines 84-100) contains six DashMap instances that store the subscription tree and its various indexes:
subs: The authoritative store mapping(ConnId, SubId)to the subscription recordchannel_kind_index: Maps(channel_id, kind)to matching subscriptionschannel_wildcard_index: Captures channel-scoped subscriptions with no kind constraintglobal_kind_indexandglobal_p_kind_index: Handle global subscriptions by kind or by p-tag combinationsglobal_wildcard_index: Catches global subscriptions with no specific constraints
This multi-map design avoids O(N) scans by pre-sorting subscriptions into granular buckets based on their filter parameters.
Registering Subscriptions with NIP-01 Semantics
New subscriptions enter the registry through register_scoped or register_channels_scoped (lines 108-140). These methods implement NIP-01 replacement semantics: if a SubId already exists for a given ConnId, the old subscription is atomically removed before the new one is inserted.
The index population logic (lines 158-200) inspects each filter's constraints to determine optimal placement:
- Channel-scoped filters without a
kindsconstraint enter thechannel_wildcard_index - Channel-scoped filters with specific kinds create entries in
channel_kind_indexfor each kind - Global filters utilizing p-tag constraints use the specialized
global_p_kind_indexwhen possible - Remaining global filters fall back to
global_kind_indexorglobal_wildcard_index
This classification ensures that fan_out_scoped can narrow candidate matches immediately without examining unrelated subscriptions.
Event Fan-Out and Delivery
When an event arrives, the registry invokes fan_out_scoped (lines 780-886) to determine routing. The method first checks whether the event is channel-scoped or global, then walks only the relevant indexes to collect candidate (ConnId, SubId) pairs.
Candidates undergo final validation in push_match (lines 635-661), which re-checks the authoritative subscription record in the subs map. This guards against stale snapshots: because DashMap provides lock-free concurrent access, a subscription might be modified between index traversal and event delivery. The double-check ensures that filters_match (defined in crates/buzz-core/src/filter.rs) and scope constraints are satisfied before the event is enqueued.
The implementation guarantees strict scoping invariants at lines 887-892: global subscriptions never receive channel-scoped events, and channel subscriptions never receive global events, even if their filters would technically match.
Subscription Lifecycle Management
Removal operations clean up both the primary store and secondary indexes. The remove_subscription method (lines 237-285) deletes a single subscription and invokes remove_from_index (lines 662-755) to purge its entries from all relevant indexes. For connection teardown, remove_connection wipes all subscriptions associated with a ConnId and adjusts the active-subscription gauge.
Both methods maintain consistency by using the same atomic operations available to the registration path, ensuring that removal does not block ongoing fan-out operations.
Concurrency and Thread Safety
All registry state uses DashMap, a lock-free concurrent hash map that allows register, fan-out, and removal operations to run in parallel without mutex contention. The architecture treats index snapshots as merely "hints"—the authoritative check in push_match means stale candidate lists are harmless and never result in incorrect delivery.
The multithreaded test case at lines 538-605 verifies these guarantees under concurrent subscription replacement and high-volume event fan-out.
use buzz_relay::subscription::SubscriptionRegistry;
use nostr::{Filter, Kind};
use uuid::Uuid;
// Initialize the registry
let registry = SubscriptionRegistry::new();
let conn_id = Uuid::new_v4();
let channel_id = Uuid::new_v4();
let sub_id = "sub_001".to_string();
// Register a channel-scoped subscription for TextNote events
let filters = vec![Filter::new().kind(Kind::TextNote)];
registry.register_scoped(
buzz_core::CommunityId::from_uuid(Uuid::nil()),
conn_id,
sub_id.clone(),
filters,
Some(channel_id),
);
// Simulate an incoming event
let event = buzz_core::StoredEvent::with_received_at(
nostr::EventBuilder::new(Kind::TextNote, "hello world", [])
.sign_with_keys(&nostr::Keys::generate())
.unwrap(),
chrono::Utc::now(),
Some(channel_id),
true,
);
// Retrieve matching connections
let matches = registry.fan_out_scoped(
buzz_core::CommunityId::from_uuid(Uuid::nil()),
&event
);
assert_eq!(matches, vec![(conn_id, sub_id.clone())]);
// Cleanup on disconnect
registry.remove_subscription(conn_id, &sub_id);
Summary
- Core Location: The subscription system resides in
crates/buzz-relay/src/subscription.rsas theSubscriptionRegistrystruct. - Identifiers: Uses
ConnId(UUID) for connections andSubId(String) for client subscriptions, scoped via theSubscriptionScopeenum. - Indexing Strategy: Six separate
DashMapindexes categorize subscriptions by(channel, kind),(community, kind, #p), or wildcard status to eliminate full-table scans. - Registration:
register_scopedimplements NIP-01 replacement semantics and populates indexes based on filter constraints at lines 108-200. - Fan-Out:
fan_out_scopedqueries scoped indexes and validates candidates viapush_matchto prevent stale data delivery (lines 635-886). - Concurrency: Lock-free
DashMapstructures enable parallel registration, removal, and fan-out without blocking.
Frequently Asked Questions
How does Buzz handle concurrent subscription updates?
Buzz uses DashMap lock-free concurrent hash maps for all registry state, allowing registration, fan-out, and removal to execute in parallel. When a subscription is updated, the register_scoped method atomically replaces the old entry (per NIP-01 semantics), and the push_match validation step during fan-out filters out any stale candidates that might have been collected from index snapshots.
What is the difference between channel-scoped and global subscriptions?
Channel-scoped subscriptions, defined by the SubscriptionScope::Channels variant, restrict event delivery to specific channel UUIDs and are indexed separately from global subscriptions. Global subscriptions (SubscriptionScope::Global) receive only non-channel events. The fan_out_scoped method enforces this boundary at lines 887-892, ensuring events never cross between these domains even if filter criteria overlap.
Why does the system use multiple indexes instead of a single map?
Multiple indexes provide sub-linear lookup performance by pre-sorting subscriptions into buckets based on their filter constraints. Instead of scanning all subscriptions for every event, fan_out_scoped queries only the relevant index—such as channel_kind_index for a specific event kind—reducing lookup time from O(N) to O(1) relative to the subscription count.
How are subscriptions cleaned up when a client disconnects?
The remove_connection method efficiently removes all subscriptions associated with a ConnId and purges their entries from every secondary index using remove_from_index. This bulk cleanup operation adjusts internal gauges and ensures no orphaned entries remain in the wildcard or kind-specific indexes.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →