- Add 25s heartbeat ping from backend to prevent Apache/proxy idle-timeout killing the connection during 1-3 min analysis runs - Handle heartbeat silently in both analyzeProof and analyzeWIPProof frontend handlers - Run PDF rasterization via asyncio.to_thread so heartbeats aren't blocked - Wrap analyze_proof with asyncio.wait_for(timeout=300) for a hard 5-min cap - Log dropped send_message calls in ConnectionManager instead of swallowing silently - cloudrun.yaml: add sessionAffinity, startup probe, raise containerConcurrency 4→10, document DISABLE_AUTH option Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
53 lines
1.6 KiB
Python
Executable file
53 lines
1.6 KiB
Python
Executable file
import logging
|
|
|
|
from fastapi import WebSocket
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ConnectionManager:
|
|
"""Manages WebSocket connections for real-time updates."""
|
|
|
|
def __init__(self):
|
|
"""Initialize the connection manager."""
|
|
self.active_connections: dict[str, WebSocket] = {}
|
|
|
|
async def connect(self, websocket: WebSocket, client_id: str) -> None:
|
|
"""
|
|
Accept a new WebSocket connection.
|
|
|
|
Args:
|
|
websocket: The WebSocket connection
|
|
client_id: Unique identifier for this client
|
|
"""
|
|
await websocket.accept()
|
|
self.active_connections[client_id] = websocket
|
|
|
|
def disconnect(self, client_id: str) -> None:
|
|
"""
|
|
Remove a client connection.
|
|
|
|
Args:
|
|
client_id: The client to disconnect
|
|
"""
|
|
if client_id in self.active_connections:
|
|
del self.active_connections[client_id]
|
|
|
|
async def send_message(self, client_id: str, message: dict) -> None:
|
|
"""
|
|
Send a JSON message to a specific client.
|
|
|
|
Args:
|
|
client_id: The target client
|
|
message: Dictionary to send as JSON
|
|
"""
|
|
if client_id in self.active_connections:
|
|
try:
|
|
await self.active_connections[client_id].send_json(message)
|
|
except Exception as e:
|
|
logger.warning(f"[MANAGER] Failed to send message to client {client_id}: {e}")
|
|
self.disconnect(client_id)
|
|
|
|
def is_connected(self, client_id: str) -> bool:
|
|
"""Check if a client is still connected."""
|
|
return client_id in self.active_connections
|