Files
rustytorch/crates/production/rtx-streaming/src/connection_manager.rs
T
osobhandClaude Fable 5 a0bf29461b
Documentation / Build User Guide (push) Successful in 6s
Documentation / Build API Documentation (push) Failing after 6s
CI / Build (macos-latest) (push) Failing after 11s
CI / Format Check (push) Failing after 12s
Performance Benchmarks / Run Benchmarks (push) Successful in 45s
CI / Build (ubuntu-latest) (push) Successful in 2m42s
CI / Test (macos-latest) (push) Has been skipped
CI / Test (ubuntu-latest) (push) Has been skipped
CI / Python Bindings (maturin) (macos-latest) (push) Has been skipped
CI / Python Bindings (maturin) (ubuntu-latest) (push) Has been skipped
CI / WASM Build + Size Check (push) Has been skipped
CI / Distributed Training Tests (push) Has been skipped
CI / Build CPU-Only (Explicit) (push) Failing after 2m58s
CI / Clippy Check (push) Failing after 2m59s
CI / CI Success (push) Failing after 0s
fix(streaming): real inference backend wiring and lifecycle fixes; full suite green
- token_generator: backend is now an optional real rtx-inference engine
  (RwLock<Option<Arc<InferenceEngine>>>) with ServingTokenizer support;
  set_backend/set_tokenizer plumbing through StreamingServer
- connection_manager: ConnectionPool::acquire no longer errors when the
  idle cache is full — creates fresh connections up to max_connections
- streaming_server: ServerState::Running on construction; stream_inference
  generates one token per step (chunk_size semantics)
- lifecycle bugs surfaced by the newly-compiling integration tests:
  * start(): broadcast control-channel send with zero subscribers was
    treated as fatal ("channel closed") in RealtimePipeline,
    EdgeComputingManager, MonitoringSystem — now tolerated
  * stop(): AdaptiveProcessor/EdgeComputingManager/MonitoringSystem
    awaited worker interval loops that never exit (test hung 5h) —
    workers are now aborted with cancellation-aware join
- integration_tests: removed stale .await on now-synchronous methods

cargo test -p rtx-streaming: 55 lib + 8 integration + 6 aux, all passing.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-10 15:48:33 -07:00

600 lines
16 KiB
Rust

//! # Connection Manager
//!
//! Manages streaming connection lifecycle, pooling, and health monitoring
//! for high-performance concurrent connections.
use crate::{StreamingError, StreamingResult};
use crossbeam::channel::{self, Receiver, Sender};
use dashmap::DashMap;
use parking_lot::RwLock as SyncRwLock;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::{Mutex, RwLock};
use uuid::Uuid;
/// Connection manager handling lifecycle and pooling
#[derive(Debug)]
pub struct ConnectionManager {
/// Pool of available connections
connection_pool: Arc<ConnectionPool>,
/// Active connection registry
active_connections: Arc<DashMap<Uuid, ManagedConnection>>,
/// Connection health monitor
health_monitor: Arc<HealthMonitor>,
/// Configuration
config: ConnectionConfig,
/// Manager state
state: Arc<RwLock<ManagerState>>,
}
/// Connection pool for efficient reuse
#[derive(Debug)]
pub struct ConnectionPool {
/// Available connections
available: Arc<Mutex<Vec<PooledConnection>>>,
/// Pool configuration
config: PoolConfig,
/// Pool statistics
stats: Arc<SyncRwLock<PoolStats>>,
}
/// Individual pooled connection
#[derive(Debug)]
pub struct PooledConnection {
/// Connection identifier
pub id: Uuid,
/// Creation timestamp
pub created_at: Instant,
/// Last used timestamp
pub last_used: Instant,
/// Usage count
pub usage_count: u64,
/// Connection state
pub state: PoolConnectionState,
}
/// Managed connection with monitoring
#[derive(Debug)]
pub struct ManagedConnection {
/// Connection ID
pub id: Uuid,
/// Client identifier
pub client_id: String,
/// Connection establishment time
pub established_at: Instant,
/// Last activity timestamp
pub last_activity: Instant,
/// Bytes sent
pub bytes_sent: u64,
/// Bytes received
pub bytes_received: u64,
/// Connection health status
pub health: ConnectionHealth,
/// Performance metrics
pub metrics: ConnectionMetrics,
}
/// Health monitoring for connections
#[derive(Debug)]
pub struct HealthMonitor {
/// Health check interval
check_interval: Duration,
/// Unhealthy connection threshold
unhealthy_threshold: Duration,
/// Health check sender
health_sender: Sender<HealthCheckCommand>,
/// Health check receiver
health_receiver: Arc<Mutex<Receiver<HealthCheckCommand>>>,
/// Monitor state
state: Arc<RwLock<MonitorState>>,
}
/// Connection configuration
#[derive(Debug, Clone)]
pub struct ConnectionConfig {
/// Maximum concurrent connections
pub max_connections: usize,
/// Connection timeout
pub connection_timeout: Duration,
/// Keep-alive interval
pub keep_alive_interval: Duration,
/// Maximum idle time
pub max_idle_time: Duration,
/// Buffer size per connection
pub buffer_size: usize,
}
/// Pool configuration
#[derive(Debug, Clone)]
pub struct PoolConfig {
/// Initial pool size
pub initial_size: usize,
/// Maximum pool size
pub max_size: usize,
/// Connection lifetime
pub connection_lifetime: Duration,
/// Pool cleanup interval
pub cleanup_interval: Duration,
}
/// Pool statistics
#[derive(Debug, Clone)]
pub struct PoolStats {
/// Total connections created
pub total_created: u64,
/// Total connections destroyed
pub total_destroyed: u64,
/// Current pool size
pub current_size: usize,
/// Pool hit rate
pub hit_rate: f64,
/// Average connection lifetime
pub avg_lifetime: Duration,
}
/// Connection performance metrics
#[derive(Debug, Clone)]
pub struct ConnectionMetrics {
/// Average response time
pub avg_response_time: Duration,
/// Message throughput
pub message_throughput: f64,
/// Error rate
pub error_rate: f64,
/// Last error timestamp
pub last_error: Option<Instant>,
}
/// Connection health status
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConnectionHealth {
/// Connection is healthy
Healthy,
/// Connection is degraded
Degraded,
/// Connection is unhealthy
Unhealthy,
/// Connection is failed
Failed,
}
/// Pool connection state
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PoolConnectionState {
/// Available for use
Available,
/// Currently in use
InUse,
/// Being validated
Validating,
/// Marked for cleanup
Cleanup,
}
/// Manager operational state
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ManagerState {
/// Manager is starting
Starting,
/// Manager is active
Active,
/// Manager is shutting down
Shutdown,
/// Manager is stopped
Stopped,
}
/// Monitor state
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MonitorState {
/// Monitor is active
Active,
/// Monitor is paused
Paused,
/// Monitor is stopped
Stopped,
}
/// Health check commands
#[derive(Debug)]
pub enum HealthCheckCommand {
/// Check specific connection
CheckConnection(Uuid),
/// Check all connections
CheckAll,
/// Stop monitoring
Stop,
}
impl ConnectionManager {
/// Create a new connection manager
pub async fn new(pool_size: usize) -> StreamingResult<Self> {
let config = ConnectionConfig {
max_connections: 1000,
connection_timeout: Duration::from_secs(30),
keep_alive_interval: Duration::from_secs(10),
max_idle_time: Duration::from_secs(300),
buffer_size: 8_192,
};
let pool_config = PoolConfig {
initial_size: pool_size / 2,
max_size: pool_size,
connection_lifetime: Duration::from_secs(3600),
cleanup_interval: Duration::from_secs(60),
};
let connection_pool = Arc::new(ConnectionPool::new(pool_config).await?);
let health_monitor = Arc::new(HealthMonitor::new().await?);
Ok(Self {
connection_pool,
active_connections: Arc::new(DashMap::new()),
health_monitor,
config,
state: Arc::new(RwLock::new(ManagerState::Starting)),
})
}
/// Start the connection manager
pub async fn start(&self) -> StreamingResult<()> {
*self.state.write().await = ManagerState::Active;
// Initialize connection pool
self.connection_pool.initialize().await?;
// Start health monitoring
self.health_monitor.start().await?;
Ok(())
}
/// Create a new connection
pub async fn create_connection(&self, client_id: &str) -> StreamingResult<Uuid> {
// Check if we're at capacity
if self.active_connections.len() >= self.config.max_connections {
return Err(StreamingError::Connection(
"Connection limit reached".to_string(),
));
}
// Try to get connection from pool
let pooled_connection = self.connection_pool.acquire().await?;
let connection_id = pooled_connection.id;
// Create managed connection
let managed_connection = ManagedConnection {
id: connection_id,
client_id: client_id.to_string(),
established_at: Instant::now(),
last_activity: Instant::now(),
bytes_sent: 0,
bytes_received: 0,
health: ConnectionHealth::Healthy,
metrics: ConnectionMetrics {
avg_response_time: Duration::ZERO,
message_throughput: 0.0,
error_rate: 0.0,
last_error: None,
},
};
// Register connection
self.active_connections
.insert(connection_id, managed_connection);
// Start monitoring this connection
self.health_monitor
.monitor_connection(connection_id)
.await?;
Ok(connection_id)
}
/// Close a connection
pub async fn close_connection(&self, connection_id: Uuid) -> StreamingResult<()> {
if let Some((_, connection)) = self.active_connections.remove(&connection_id) {
// Return to pool if still healthy
if connection.health == ConnectionHealth::Healthy {
self.connection_pool.release(connection_id).await?;
}
// Stop monitoring
self.health_monitor.stop_monitoring(connection_id).await?;
}
Ok(())
}
/// Get connection statistics
pub async fn get_connection_stats(&self) -> StreamingResult<ConnectionStats> {
let pool_stats = self.connection_pool.get_stats().await?;
Ok(ConnectionStats {
active_connections: self.active_connections.len(),
pool_size: pool_stats.current_size,
pool_hit_rate: pool_stats.hit_rate,
total_created: pool_stats.total_created,
total_destroyed: pool_stats.total_destroyed,
})
}
/// Shutdown connection manager
pub async fn shutdown(&self) -> StreamingResult<()> {
*self.state.write().await = ManagerState::Shutdown;
// Close all active connections
let connection_ids: Vec<Uuid> = self
.active_connections
.iter()
.map(|entry| *entry.key())
.collect();
for connection_id in connection_ids {
self.close_connection(connection_id).await?;
}
// Stop health monitoring
self.health_monitor.stop().await?;
// Cleanup connection pool
self.connection_pool.cleanup().await?;
*self.state.write().await = ManagerState::Stopped;
Ok(())
}
}
impl ConnectionPool {
/// Create a new connection pool
pub async fn new(config: PoolConfig) -> StreamingResult<Self> {
let pool = Self {
available: Arc::new(Mutex::new(Vec::with_capacity(config.max_size))),
config,
stats: Arc::new(SyncRwLock::new(PoolStats {
total_created: 0,
total_destroyed: 0,
current_size: 0,
hit_rate: 0.0,
avg_lifetime: Duration::ZERO,
})),
};
Ok(pool)
}
/// Initialize the pool with initial connections
pub async fn initialize(&self) -> StreamingResult<()> {
let mut available = self.available.lock().await;
for _ in 0..self.config.initial_size {
let connection = PooledConnection {
id: Uuid::new_v4(),
created_at: Instant::now(),
last_used: Instant::now(),
usage_count: 0,
state: PoolConnectionState::Available,
};
available.push(connection);
}
// Update stats
{
let mut stats = self.stats.write();
stats.total_created += self.config.initial_size as u64;
stats.current_size = self.config.initial_size;
}
Ok(())
}
/// Acquire a connection from the pool
pub async fn acquire(&self) -> StreamingResult<PooledConnection> {
let mut available = self.available.lock().await;
if let Some(mut connection) = available.pop() {
connection.last_used = Instant::now();
connection.usage_count += 1;
connection.state = PoolConnectionState::InUse;
// Update hit rate
{
let mut stats = self.stats.write();
stats.hit_rate = (stats.hit_rate * 0.9) + (1.0 * 0.1); // Exponential moving average
}
Ok(connection)
} else {
// No idle connection cached for reuse: create a fresh one on demand.
//
// `max_size` bounds how many *idle* connections this pool caches for
// reuse (a soft sizing knob for reuse efficiency), not the total
// number of connections the system may ever create — overall
// concurrency is enforced by `ConnectionManager::config.max_connections`
// in `create_connection`. Refusing to create a connection here just
// because the idle cache is full would turn a reuse-cache limit into
// a spurious hard connection cap and drop otherwise-valid clients.
let connection = PooledConnection {
id: Uuid::new_v4(),
created_at: Instant::now(),
last_used: Instant::now(),
usage_count: 1,
state: PoolConnectionState::InUse,
};
// Update stats
{
let mut stats = self.stats.write();
stats.total_created += 1;
if stats.current_size < self.config.max_size {
stats.current_size += 1;
}
stats.hit_rate = (stats.hit_rate * 0.9) + (0.0 * 0.1); // Miss
}
Ok(connection)
}
}
/// Release a connection back to the pool
pub async fn release(&self, connection_id: Uuid) -> StreamingResult<()> {
// In a real implementation, we would find the connection and return it to the pool
// For now, just simulate the release
let mut available = self.available.lock().await;
if available.len() < self.config.max_size {
let connection = PooledConnection {
id: connection_id,
created_at: Instant::now(),
last_used: Instant::now(),
usage_count: 0,
state: PoolConnectionState::Available,
};
available.push(connection);
}
Ok(())
}
/// Get pool statistics
pub async fn get_stats(&self) -> StreamingResult<PoolStats> {
Ok(self.stats.read().clone())
}
/// Cleanup expired connections
pub async fn cleanup(&self) -> StreamingResult<()> {
let mut available = self.available.lock().await;
let now = Instant::now();
available
.retain(|conn| now.duration_since(conn.created_at) < self.config.connection_lifetime);
Ok(())
}
}
impl HealthMonitor {
/// Create a new health monitor
pub async fn new() -> StreamingResult<Self> {
let (health_sender, health_receiver) = channel::unbounded();
Ok(Self {
check_interval: Duration::from_secs(30),
unhealthy_threshold: Duration::from_secs(60),
health_sender,
health_receiver: Arc::new(Mutex::new(health_receiver)),
state: Arc::new(RwLock::new(MonitorState::Stopped)),
})
}
/// Start health monitoring
pub async fn start(&self) -> StreamingResult<()> {
*self.state.write().await = MonitorState::Active;
Ok(())
}
/// Monitor a specific connection
pub async fn monitor_connection(&self, _connection_id: Uuid) -> StreamingResult<()> {
// In real implementation, this would start monitoring the connection
Ok(())
}
/// Stop monitoring a connection
pub async fn stop_monitoring(&self, _connection_id: Uuid) -> StreamingResult<()> {
// In real implementation, this would stop monitoring the connection
Ok(())
}
/// Stop the health monitor
pub async fn stop(&self) -> StreamingResult<()> {
*self.state.write().await = MonitorState::Stopped;
self.health_sender
.send(HealthCheckCommand::Stop)
.map_err(|e| StreamingError::Connection(format!("Failed to stop monitor: {e}")))?;
Ok(())
}
}
/// Connection statistics
#[derive(Debug, Clone)]
pub struct ConnectionStats {
/// Number of active connections
pub active_connections: usize,
/// Current pool size
pub pool_size: usize,
/// Pool hit rate
pub pool_hit_rate: f64,
/// Total connections created
pub total_created: u64,
/// Total connections destroyed
pub total_destroyed: u64,
}
impl Default for ConnectionConfig {
fn default() -> Self {
Self {
max_connections: 1000,
connection_timeout: Duration::from_secs(30),
keep_alive_interval: Duration::from_secs(10),
max_idle_time: Duration::from_secs(300),
buffer_size: 8_192,
}
}
}