fix(signalr): add exception handler (sentry-14,26,32,36)

This commit is contained in:
MingxuanGame
2025-08-13 15:31:40 +00:00
parent 858a7e8640
commit e180dcfbd6
2 changed files with 33 additions and 7 deletions

View File

@@ -77,9 +77,13 @@ class Client:
await asyncio.sleep(settings.signalr_ping_interval) await asyncio.sleep(settings.signalr_ping_interval)
except WebSocketDisconnect: except WebSocketDisconnect:
break break
except Exception as e: except RuntimeError as e:
logger.error(f"Error in ping task for {self.connection_id}: {e}") if "disconnect message" in str(e):
break break
else:
logger.error(f"Error in ping task for {self.connection_id}: {e}")
except Exception:
logger.exception(f"Error in client {self.connection_id}")
class Hub[TState: UserState]: class Hub[TState: UserState]:
@@ -158,6 +162,8 @@ class Hub[TState: UserState]:
return client return client
async def remove_client(self, client: Client) -> None: async def remove_client(self, client: Client) -> None:
if client.connection_token not in self.clients:
return
del self.clients[client.connection_token] del self.clients[client.connection_token]
if client._listen_task: if client._listen_task:
client._listen_task.cancel() client._listen_task.cancel()
@@ -186,7 +192,22 @@ class Hub[TState: UserState]:
await method(client) await method(client)
async def send_packet(self, client: Client, packet: Packet) -> None: async def send_packet(self, client: Client, packet: Packet) -> None:
await client.send_packet(packet) try:
await client.send_packet(packet)
except WebSocketDisconnect as e:
logger.info(
f"Client {client.connection_id} disconnected: {e.code}, {e.reason}"
)
await self.remove_client(client)
except RuntimeError as e:
if "disconnect message" in str(e):
logger.info(f"Client {client.connection_id} closed the connection.")
else:
logger.exception(f"RuntimeError in client {client.connection_id}: {e}")
await self.remove_client(client)
except Exception:
logger.exception(f"Error in client {client.connection_id}")
await self.remove_client(client)
async def broadcast_call(self, method: str, *args: Any) -> None: async def broadcast_call(self, method: str, *args: Any) -> None:
tasks = [] tasks = []

View File

@@ -14,6 +14,7 @@ from app.database.score_token import ScoreToken
from app.dependencies.database import engine from app.dependencies.database import engine
from app.dependencies.fetcher import get_fetcher from app.dependencies.fetcher import get_fetcher
from app.dependencies.storage import get_storage_service from app.dependencies.storage import get_storage_service
from app.exception import InvokeException
from app.models.mods import mods_to_int from app.models.mods import mods_to_int
from app.models.score import LegacyReplaySoloScoreInfo, ScoreStatistics from app.models.score import LegacyReplaySoloScoreInfo, ScoreStatistics
from app.models.spectator_hub import ( from app.models.spectator_hub import (
@@ -30,6 +31,7 @@ from app.utils import unix_timestamp_to_windows
from .hub import Client, Hub from .hub import Client, Hub
from httpx import HTTPError
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from sqlmodel import select from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession from sqlmodel.ext.asyncio.session import AsyncSession
@@ -190,9 +192,12 @@ class SpectatorHub(Hub[StoreClientState]):
fetcher = await get_fetcher() fetcher = await get_fetcher()
async with AsyncSession(engine) as session: async with AsyncSession(engine) as session:
async with session.begin(): async with session.begin():
beatmap = await Beatmap.get_or_fetch( try:
session, fetcher, bid=state.beatmap_id beatmap = await Beatmap.get_or_fetch(
) session, fetcher, bid=state.beatmap_id
)
except HTTPError:
raise InvokeException(f"Beatmap {state.beatmap_id} not found.")
user = ( user = (
await session.exec(select(User).where(User.id == user_id)) await session.exec(select(User).where(User.id == user_id))
).first() ).first()