""" WebSocket Connection Pool Usage Examples This file demonstrates how to use the WebSocket connection pooling system in the Delphi Database application. Examples include: - Basic WebSocket endpoint with pooling - Custom message handling - Topic-based broadcasting - Connection monitoring - Admin management integration """ import asyncio from datetime import datetime, timezone from typing import Set, Optional, Dict, Any from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends from fastapi.responses import HTMLResponse from app.middleware.websocket_middleware import ( get_websocket_manager, websocket_endpoint, websocket_auth_dependency, WebSocketManager ) from app.services.websocket_pool import ( WebSocketMessage, MessageType, websocket_connection ) from app.models.user import User # Example 1: Basic WebSocket endpoint with automatic pooling @websocket_endpoint(topics={"notifications"}, require_auth=True) async def basic_websocket_handler( websocket: WebSocket, connection_id: str, manager: WebSocketManager, message: WebSocketMessage ): """ Basic WebSocket handler using the pooling decorator. Automatically handles connection management, authentication, and cleanup. """ if message.type == "user_action": # Handle user actions response = WebSocketMessage( type="action_response", data={"status": "received", "action": message.data.get("action")} ) await manager.pool._send_to_connection(connection_id, response) # Example 2: Manual WebSocket management with custom logic async def manual_websocket_handler(websocket: WebSocket, topic: str): """ Manual WebSocket handling with direct pool management. Provides more control over connection lifecycle and message handling. """ manager = get_websocket_manager() # Custom message handler async def handle_custom_message(connection_id: str, message: WebSocketMessage): if message.type == "chat_message": # Broadcast chat message to all subscribers await manager.broadcast_to_topic( topic=topic, message_type="chat_broadcast", data={ "user": message.data.get("user", "Anonymous"), "message": message.data.get("message", ""), "timestamp": datetime.now(timezone.utc).isoformat() }, exclude_connection_id=connection_id ) elif message.type == "typing": # Broadcast typing indicator await manager.broadcast_to_topic( topic=topic, message_type="user_typing", data={ "user": message.data.get("user", "Anonymous"), "typing": message.data.get("typing", False) }, exclude_connection_id=connection_id ) # Handle the connection await manager.handle_connection( websocket=websocket, topics={topic}, require_auth=True, metadata={"chat_room": topic}, message_handler=handle_custom_message ) # Example 3: Low-level pool usage with context manager async def low_level_websocket_example(websocket: WebSocket, user_id: int): """ Low-level WebSocket handling using the connection context manager directly. Provides maximum control over the connection lifecycle. """ await websocket.accept() async with websocket_connection( websocket=websocket, user_id=user_id, topics={"user_updates"}, metadata={"example": "low_level"} ) as (connection_id, pool): # Send welcome message welcome = WebSocketMessage( type="welcome", data={ "connection_id": connection_id, "message": "Connected to low-level example" } ) await pool._send_to_connection(connection_id, welcome) # Handle messages manually try: while True: try: data = await websocket.receive_text() # Parse and handle message import json message_dict = json.loads(data) message = WebSocketMessage(**message_dict) if message.type == "ping": pong = WebSocketMessage(type="pong", data={"timestamp": message.timestamp}) await pool._send_to_connection(connection_id, pong) elif message.type == "echo": echo = WebSocketMessage( type="echo_response", data={"original": message.data, "echoed_at": datetime.now(timezone.utc).isoformat()} ) await pool._send_to_connection(connection_id, echo) except WebSocketDisconnect: break except Exception as e: print(f"Error handling message: {e}") break except Exception as e: print(f"Connection error: {e}") # Example 4: Broadcasting service class NotificationBroadcaster: """ Service for broadcasting notifications to different user groups. Demonstrates how to use the pool for system-wide notifications. """ def __init__(self): self.manager = get_websocket_manager() async def broadcast_system_announcement(self, message: str, priority: str = "info"): """Broadcast system announcement to all connected users""" sent_count = await self.manager.broadcast_to_topic( topic="system_announcements", message_type="system_announcement", data={ "message": message, "priority": priority, "timestamp": datetime.now(timezone.utc).isoformat() } ) return sent_count async def notify_user_group(self, group: str, notification_type: str, data: Dict[str, Any]): """Send notification to a specific user group""" topic = f"group_{group}" sent_count = await self.manager.broadcast_to_topic( topic=topic, message_type=notification_type, data=data ) return sent_count async def send_personal_notification(self, user_id: int, notification_type: str, data: Dict[str, Any]): """Send personal notification to a specific user""" sent_count = await self.manager.send_to_user( user_id=user_id, message_type=notification_type, data=data ) return sent_count # Example 5: Connection monitoring and health checks class ConnectionMonitor: """ Service for monitoring WebSocket connections and health. Demonstrates how to use the pool for system monitoring. """ def __init__(self): self.manager = get_websocket_manager() async def get_connection_stats(self) -> Dict[str, Any]: """Get comprehensive connection statistics""" return await self.manager.get_stats() async def health_check_all_connections(self) -> Dict[str, Any]: """Perform health check on all connections""" pool = self.manager.pool async with pool._connections_lock: connection_ids = list(pool._connections.keys()) healthy = 0 stale = 0 total = len(connection_ids) for connection_id in connection_ids: connection_info = await pool.get_connection_info(connection_id) if connection_info: if connection_info.is_alive(): healthy += 1 if connection_info.is_stale(): stale += 1 return { "total_connections": total, "healthy_connections": healthy, "stale_connections": stale, "health_percentage": (healthy / total * 100) if total > 0 else 100 } async def cleanup_stale_connections(self) -> int: """Manually cleanup stale connections""" pool = self.manager.pool stats_before = await pool.get_stats() await pool._cleanup_stale_connections() stats_after = await pool.get_stats() return stats_before["active_connections"] - stats_after["active_connections"] # Example 6: Real-time data streaming class RealTimeDataStreamer: """ Service for streaming real-time data to WebSocket clients. Demonstrates how to use the pool for continuous data updates. """ def __init__(self): self.manager = get_websocket_manager() self._streaming_tasks: Dict[str, asyncio.Task] = {} async def start_data_stream(self, topic: str, data_source: callable, interval: float = 1.0): """Start streaming data to a topic""" if topic in self._streaming_tasks: return False # Already streaming async def stream_data(): while True: try: # Get data from source data = await data_source() if asyncio.iscoroutinefunction(data_source) else data_source() # Broadcast to subscribers await self.manager.broadcast_to_topic( topic=topic, message_type="data_update", data={ "data": data, "timestamp": datetime.now(timezone.utc).isoformat() } ) await asyncio.sleep(interval) except asyncio.CancelledError: break except Exception as e: print(f"Error in data stream {topic}: {e}") await asyncio.sleep(interval * 2) # Back off on error task = asyncio.create_task(stream_data()) self._streaming_tasks[topic] = task return True async def stop_data_stream(self, topic: str): """Stop streaming data to a topic""" if topic in self._streaming_tasks: self._streaming_tasks[topic].cancel() del self._streaming_tasks[topic] return True return False async def stop_all_streams(self): """Stop all data streams""" for task in self._streaming_tasks.values(): task.cancel() self._streaming_tasks.clear() # Example FastAPI application demonstrating usage def create_example_app() -> FastAPI: """Create example FastAPI application with WebSocket endpoints""" app = FastAPI(title="WebSocket Pool Example") # Initialize services broadcaster = NotificationBroadcaster() monitor = ConnectionMonitor() streamer = RealTimeDataStreamer() @app.websocket("/ws/basic") async def basic_endpoint(websocket: WebSocket): """Basic WebSocket endpoint with automatic pooling""" await basic_websocket_handler(websocket, "basic", get_websocket_manager(), None) @app.websocket("/ws/chat/{room}") async def chat_endpoint(websocket: WebSocket, room: str): """Chat room WebSocket endpoint""" await manual_websocket_handler(websocket, f"chat_{room}") @app.websocket("/ws/user/{user_id}") async def user_endpoint(websocket: WebSocket, user_id: int): """User-specific WebSocket endpoint""" await low_level_websocket_example(websocket, user_id) @app.get("/") async def index(): """Simple HTML page for testing WebSocket connections""" return HTMLResponse(""" WebSocket Pool Example

WebSocket Pool Example

""") @app.post("/api/broadcast/system") async def broadcast_system(message: str, priority: str = "info"): """Broadcast system message to all users""" sent_count = await broadcaster.broadcast_system_announcement(message, priority) return {"sent_count": sent_count} @app.get("/api/monitor/stats") async def get_monitor_stats(): """Get connection monitoring statistics""" return await monitor.get_connection_stats() @app.get("/api/monitor/health") async def get_health_status(): """Get connection health status""" return await monitor.health_check_all_connections() @app.post("/api/monitor/cleanup") async def cleanup_connections(): """Manually cleanup stale connections""" cleaned = await monitor.cleanup_stale_connections() return {"cleaned_connections": cleaned} return app if __name__ == "__main__": # Run the example application import uvicorn app = create_example_app() uvicorn.run(app, host="0.0.0.0", port=8000)