framework/ARCHITECTURE.md
2025-07-21 00:17:46 +02:00

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

  1. Connection Pooling: Share connections for the same URL
  2. Load Balancing: Distribute scripts across multiple connections to the same server
  3. Metrics Collection: Gather connection health and performance metrics
  4. Circuit Breaker: Temporarily disable failing connections
  5. 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.