7.7 KiB
Framework Architecture
This document describes the simplified architecture of the WebSocket connection manager framework, built on top of the circle_client_ws
library.
Overview
The framework provides a clean, builder-pattern API for managing multiple self-managing WebSocket connections. The key architectural principle is delegation of responsibility - each CircleWsClient
is completely autonomous and handles its own lifecycle.
Core Components
1. WsManagerBuilder
The builder provides a fluent API for configuring WebSocket connections:
let manager = ws_manager()
.private_key("hex_private_key".to_string())
.add_server_url("ws://server1.com".to_string())
.add_server_url("ws://server2.com".to_string())
.build();
Responsibilities:
- Validate configuration parameters (private key format, URL format)
- Collect server URLs and authentication settings
- Build the final
WsManager
instance
2. WsManager
A simplified connection manager that acts as a container for self-managing clients:
pub struct WsManager {
clients: Rc<RefCell<HashMap<String, CircleWsClient>>>,
private_key: Option<String>,
server_urls: Vec<String>,
}
Responsibilities:
- Store and organize multiple
CircleWsClient
instances - Provide API for script execution across connections
- Coordinate connection establishment (but not maintenance)
- Provide connection status and management utilities
What it does NOT do:
- Keep-alive monitoring (delegated to individual clients)
- Reconnection logic (delegated to individual clients)
- Complex connection state management (delegated to individual clients)
3. Self-Managing CircleWsClient
Each client is completely autonomous and handles its own lifecycle:
Internal Responsibilities:
- WebSocket connection establishment and maintenance
- secp256k1 authentication flow (when private keys are provided)
- Periodic keep-alive health checks
- Automatic reconnection with exponential backoff
- Connection status tracking
- Resource cleanup when dropped
Architectural Flow
sequenceDiagram
participant User as User Code
participant Builder as WsManagerBuilder
participant Manager as WsManager
participant Client1 as CircleWsClient 1
participant Client2 as CircleWsClient 2
participant Server1 as WebSocket Server 1
participant Server2 as WebSocket Server 2
User->>+Builder: ws_manager()
User->>Builder: .private_key("key")
User->>Builder: .add_server_url("ws://server1")
User->>Builder: .add_server_url("ws://server2")
User->>Builder: .build()
Builder->>-Manager: WsManager instance
User->>+Manager: connect()
Manager->>+Client1: new() + connect()
Client1->>Client1: Self-manage connection
Client1->>+Server1: WebSocket connection
Client1->>Client1: Start keep-alive loop
Client1->>Client1: Start reconnection handler
Server1-->>-Client1: Connected
Client1-->>-Manager: Connection established
Manager->>+Client2: new() + connect()
Client2->>Client2: Self-manage connection
Client2->>+Server2: WebSocket connection
Client2->>Client2: Start keep-alive loop
Client2->>Client2: Start reconnection handler
Server2-->>-Client2: Connected
Client2-->>-Manager: Connection established
Manager-->>-User: All connections established
Note over Client1, Server1: Client1 autonomously maintains connection
Note over Client2, Server2: Client2 autonomously maintains connection
User->>+Manager: execute_script("ws://server1", script)
Manager->>+Client1: play(script)
Client1->>+Server1: Execute script
Server1-->>-Client1: Script result
Client1-->>-Manager: Result
Manager-->>-User: Script result
Key Architectural Benefits
1. Simplified Complexity
- Before: Complex WsManager with external keep-alive and reconnection logic
- After: Simple WsManager that delegates lifecycle management to individual clients
2. Autonomous Clients
- Each client is self-contained and manages its own state
- No external coordination required for connection health
- Clients can be used independently outside of WsManager
3. Clean Separation of Concerns
- WsManagerBuilder: Configuration and validation
- WsManager: Organization and coordination
- CircleWsClient: Connection lifecycle and maintenance
4. Improved Reliability
- Connection failures in one client don't affect others
- Each client has its own reconnection strategy
- No single point of failure in connection management
Connection Lifecycle
1. Initialization Phase
// Builder validates configuration
let manager = ws_manager()
.private_key("valid_hex_key") // Validates 64-char hex
.add_server_url("ws://valid") // Validates WebSocket URL format
.build(); // Creates WsManager with validated config
2. Connection Phase
// Manager creates and connects individual clients
manager.connect().await?;
// For each configured URL:
// 1. Create CircleWsClient with URL and optional private key
// 2. Call client.connect() which handles:
// - WebSocket connection establishment
// - Authentication flow (if private key provided)
// - Start internal keep-alive monitoring
// - Start internal reconnection handling
// 3. Store connected client in manager's HashMap
3. Operation Phase
// Execute scripts on specific servers
let result = manager.execute_script("ws://server1", script).await?;
// Manager simply forwards to the appropriate client
// Client handles the actual script execution over its maintained connection
4. Maintenance Phase (Automatic)
// Each client autonomously:
// - Sends periodic keep-alive pings
// - Detects connection failures
// - Attempts reconnection with exponential backoff
// - Re-authenticates after successful reconnection
// - Updates internal connection status
5. Cleanup Phase
// Explicit cleanup (optional)
manager.disconnect_all().await;
// Or automatic cleanup when manager is dropped
// Each client cleans up its own resources
Error Handling Strategy
Builder Validation
- Invalid private key: Panic during build (fail-fast)
- Invalid URL format: Panic during build (fail-fast)
Connection Errors
- Individual connection failures: Logged but don't prevent other connections
- All connections fail: Return
CircleWsClientError::NotConnected
- Partial failures: Log summary, continue with successful connections
Runtime Errors
- Script execution errors: Return specific error for that client
- Connection loss: Handled automatically by individual client reconnection
- Authentication failures: Logged and retried by individual clients
Platform Considerations
WASM (Browser)
- Uses
gloo-net
for WebSocket connections - Uses
gloo-timers
for keep-alive timing - Uses
spawn_local
for async task management - Each client manages its own async tasks
Native (Tokio)
- Uses
tokio-tungstenite
for WebSocket connections - Uses
tokio::time
for keep-alive timing - Uses
tokio::spawn
for async task management - Each client manages its own async tasks
Future Enhancements
Potential Improvements
- Connection Pooling: Share connections for the same URL
- Load Balancing: Distribute scripts across multiple connections to the same server
- Metrics Collection: Gather connection health and performance metrics
- Circuit Breaker: Temporarily disable failing connections
- Connection Prioritization: Prefer certain connections over others
Backward Compatibility
The current architecture maintains backward compatibility while providing a foundation for future enhancements. The self-managing client approach makes it easy to add new features without disrupting the core architecture.