Skip to content

Commit 9e46844

Browse files
authored
🐛 Handles socketio exception on disconnect and fixes double logging of monitoring middleware (#8067)
1 parent 0480725 commit 9e46844

File tree

2 files changed

+58
-50
lines changed
  • packages/service-library/src/servicelib/aiohttp
  • services/web/server/src/simcore_service_webserver/socketio

2 files changed

+58
-50
lines changed

packages/service-library/src/servicelib/aiohttp/monitoring.py

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
)
2626
from .typing_extension import Handler
2727

28-
log = logging.getLogger(__name__)
28+
_logger = logging.getLogger(__name__)
2929

3030
_PROMETHEUS_METRICS: Final[str] = f"{__name__}.prometheus_metrics" # noqa: N816
3131

@@ -59,7 +59,6 @@ def middleware_factory(
5959
async def middleware_handler(request: web.Request, handler: Handler):
6060
# See https://prometheus.io/docs/concepts/metric_types
6161

62-
log_exception: BaseException | None = None
6362
response: web.StreamResponse = web.HTTPInternalServerError()
6463

6564
canonical_endpoint = request.path
@@ -68,7 +67,7 @@ async def middleware_handler(request: web.Request, handler: Handler):
6867
start_time = perf_counter()
6968
try:
7069
if enter_middleware_cb:
71-
with log_catch(logger=log, reraise=False):
70+
with log_catch(logger=_logger, reraise=False):
7271
await enter_middleware_cb(request)
7372

7473
metrics = request.app[_PROMETHEUS_METRICS]
@@ -92,16 +91,10 @@ async def middleware_handler(request: web.Request, handler: Handler):
9291

9392
except web.HTTPServerError as exc:
9493
response = exc
95-
log_exception = exc
9694
raise
9795

9896
except web.HTTPException as exc:
9997
response = exc
100-
log_exception = None
101-
raise
102-
103-
except Exception as exc: # pylint: disable=broad-except
104-
log_exception = exc
10598
raise
10699

107100
finally:
@@ -117,23 +110,9 @@ async def middleware_handler(request: web.Request, handler: Handler):
117110
)
118111

119112
if exit_middleware_cb:
120-
with log_catch(logger=log, reraise=False):
113+
with log_catch(logger=_logger, reraise=False):
121114
await exit_middleware_cb(request, response)
122115

123-
if log_exception:
124-
log.error(
125-
'Unexpected server error "%s" from access: %s "%s %s" done '
126-
"in %3.2f secs. Responding with status %s",
127-
type(log_exception),
128-
request.remote,
129-
request.method,
130-
request.path,
131-
response_latency_seconds,
132-
response.status,
133-
exc_info=log_exception,
134-
stack_info=True,
135-
)
136-
137116
return response
138117

139118
setattr( # noqa: B010

services/web/server/src/simcore_service_webserver/socketio/_handlers.py

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
SEE http://python-socketio.readthedocs.io/en/latest/
55
"""
66

7+
import contextlib
78
import logging
89
from typing import Any
910

@@ -14,6 +15,7 @@
1415
from models_library.socketio import SocketMessageDict
1516
from models_library.users import UserID
1617
from servicelib.aiohttp.observer import emit
18+
from servicelib.logging_errors import create_troubleshootting_log_kwargs
1719
from servicelib.logging_utils import get_log_record_extra, log_context
1820
from servicelib.request_keys import RQT_USERID_KEY
1921

@@ -162,37 +164,64 @@ async def connect(
162164
@register_socketio_handler
163165
async def disconnect(socket_id: SocketID, app: web.Application) -> None:
164166
"""socketio reserved handler for when the socket.io connection is disconnected."""
165-
sio = get_socket_server(app)
166-
async with sio.session(socket_id) as socketio_session:
167-
if user_id := socketio_session.get("user_id"):
167+
async with contextlib.AsyncExitStack() as stack:
168+
169+
# retrieve the socket session
170+
try:
171+
socketio_session = await stack.enter_async_context(
172+
get_socket_server(app).session(socket_id)
173+
)
174+
175+
except KeyError as err:
176+
_logger.warning(
177+
**create_troubleshootting_log_kwargs(
178+
f"Socket session {socket_id} not found during disconnect, already cleaned up",
179+
error=err,
180+
error_context={"socket_id": socket_id},
181+
)
182+
)
183+
return
184+
185+
# session is wel formed, we can access its data
186+
try:
187+
188+
user_id = socketio_session["user_id"]
168189
client_session_id = socketio_session["client_session_id"]
169190
product_name = socketio_session["product_name"]
170191

171-
with log_context(
172-
_logger,
173-
logging.INFO,
174-
"disconnection of %s with %s",
175-
f"{user_id=}",
176-
f"{client_session_id=}",
177-
):
178-
with managed_resource(user_id, client_session_id, app) as user_session:
179-
await user_session.remove_socket_id()
180-
# signal same user other clients if available
181-
await emit(
182-
app,
183-
"SIGNAL_USER_DISCONNECTED",
184-
user_id,
185-
client_session_id,
186-
app,
187-
product_name,
192+
except KeyError as err:
193+
_logger.exception(
194+
**create_troubleshootting_log_kwargs(
195+
f"Socket session {socket_id} does not have user_id or client_session_id during disconnect",
196+
error=err,
197+
error_context={
198+
"socket_id": socket_id,
199+
"socketio_session": socketio_session,
200+
},
201+
tip="Check if session is corrupted",
188202
)
203+
)
204+
return
189205

190-
else:
191-
# this should not happen!!
192-
_logger.error(
193-
"Unknown client diconnected sid: %s, session %s",
194-
socket_id,
195-
f"{socketio_session}",
206+
# Disconnecting
207+
with log_context(
208+
_logger,
209+
logging.INFO,
210+
"disconnection of %s with %s",
211+
f"{user_id=}",
212+
f"{client_session_id=}",
213+
):
214+
with managed_resource(user_id, client_session_id, app) as user_session:
215+
await user_session.remove_socket_id()
216+
217+
# signal same user other clients if available
218+
await emit(
219+
app,
220+
"SIGNAL_USER_DISCONNECTED",
221+
user_id,
222+
client_session_id,
223+
app,
224+
product_name,
196225
)
197226

198227

0 commit comments

Comments
 (0)