Repository navigation
cluster adapter
This document summarizes the implementation of the ClusterAdapter for the Pusher Protocol Server Rust Implementation. The ClusterAdapter provides multi-instance deployment support using UDP-based node discovery, enabling horizontal scaling across multiple server instances.
- Requirement 4.2: Support a cluster adapter for multi-instance deployments using UDP discovery
- Requirement 4.5: Broadcast messages across all instances when using horizontal adapters
- Requirement 4.6: Synchronize channel membership across instances
- Requirement 17.1: Discover other nodes using UDP broadcast
- Requirement 17.2: Support master election in cluster mode
- Requirement 17.3: Handle node promotion and demotion events
- Requirement 17.4: Track active nodes in the cluster
- Requirement 17.5: Support configurable discovery intervals and timeouts
The main adapter that extends LocalAdapter with cluster-wide capabilities:
- Local Adapter Integration: Wraps a LocalAdapter for managing local socket connections
- Discovery Manager: Manages UDP-based node discovery and master election
- Broadcast Channel: Tokio broadcast channel for distributing cluster messages
- Configuration: Uses ClusterAdapterConfig for port, multicast address, and timeout settings
Implements UDP multicast-based node discovery:
- Hello Messages: Sent when a node joins the cluster
- Heartbeat Messages: Sent periodically (every 5 seconds) to maintain presence
- Goodbye Messages: Sent when a node leaves the cluster gracefully
- Node Timeout Detection: Removes nodes that haven't sent heartbeats for 15 seconds
- Master Election: Deterministic election based on node weight and ID
Four types of cluster messages for synchronization:
- Broadcast: Distribute messages to channel subscribers across all nodes
- AddMember: Synchronize presence channel member additions
- RemoveMember: Synchronize presence channel member removals
- TerminateUser: Terminate user connections across all nodes
The master election uses a simple but effective algorithm:
- Each node calculates a weight (based on uptime)
- Nodes are sorted by weight (descending), then by node ID (ascending)
- The node with the highest weight becomes master
- In case of equal weights, the lexicographically smallest ID wins
- Election is triggered when:
- A new node joins
- A node leaves
- The current master times out
When a message is sent to a channel:
- The message is delivered to local subscribers via LocalAdapter
- A ClusterMessage::Broadcast is created
- The message is sent to the local broadcast channel
- The message is sent via UDP to all known nodes
- Each node receives the message and delivers it to their local subscribers
The ClusterAdapter is configured via ClusterAdapterConfig:
pub struct ClusterAdapterConfig {
pub port: u16, // UDP port for discovery (default: 11002)
pub multicast_address: String, // Multicast address (default: "239.1.1.1")
pub request_timeout_ms: u64, // Request timeout (default: 5000)
}The AppState initialization was updated to support cluster mode:
- Parse adapter driver from options (
adapter_driverfield) - Create appropriate adapter based on driver type
- Initialize adapter asynchronously via
init_async()method - For cluster mode, also initialize ClusterRateLimiter
The implementation includes graceful degradation for environments where multicast is not available:
- Multicast join failures are logged as warnings but don't prevent initialization
- Discovery message send failures are logged at debug level
- The adapter continues to function as a LocalAdapter if multicast is unavailable
Comprehensive tests were added in tests/cluster_adapter_test.rs:
- Creation Test: Verifies ClusterAdapter can be created and initialized
- Socket Management Test: Tests adding and removing sockets
- Channel Management Test: Tests channel subscription and unsubscription
- Presence Management Test: Tests presence member tracking
- Master Status Test: Verifies master election works correctly
- Send Message Test: Tests message broadcasting to subscribers
- Disconnect Test: Tests graceful shutdown and cleanup
All tests pass successfully.
To use the ClusterAdapter, set the adapter_driver option to "cluster":
./soketi-rs --adapter-driver clusteruse soketi_rs::adapters::cluster::ClusterAdapter;
use soketi_rs::config::ClusterAdapterConfig;
let config = ClusterAdapterConfig {
port: 11002,
multicast_address: "239.1.1.1".to_string(),
request_timeout_ms: 5000,
};
let adapter = ClusterAdapter::new(config).await?;
adapter.init().await?;
// Use adapter for socket management, message broadcasting, etc.- src/adapters/cluster.rs (NEW): Main ClusterAdapter implementation
- src/adapters/mod.rs: Added cluster module export
- src/state.rs: Updated to support async adapter initialization
- src/main.rs: Updated to call async initialization
- src/ws_handler.rs: Updated close_all_local_sockets to support ClusterAdapter
- tests/cluster_adapter_test.rs (NEW): Comprehensive test suite
Potential improvements for future iterations:
- Cluster-wide Queries: Implement methods to query socket counts and channel membership across all nodes
- Request-Response Pattern: Add request-response mechanism for querying remote nodes
- Encryption: Add encryption for cluster messages
- Authentication: Add authentication for node discovery
- TCP Fallback: Add TCP-based communication as fallback when UDP multicast is unavailable
- Metrics: Add cluster-specific metrics (node count, message distribution, etc.)
- Health Checks: Add health check endpoints for cluster status
The ClusterAdapter implementation successfully provides multi-instance deployment support with UDP-based node discovery. It extends the LocalAdapter with cluster-wide message broadcasting and synchronization capabilities, enabling horizontal scaling of the Pusher protocol server.
The implementation follows Rust best practices, includes comprehensive error handling, and provides graceful degradation for environments where multicast is not available. All tests pass successfully, demonstrating the correctness of the implementation.