Files
2026-07-13 12:24:33 +08:00

67 lines
2.2 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""Observability-aware timeout error.
Raise :class:`LMCacheTimeoutError` instead of the builtin ``TimeoutError`` so
each timeout is reported to MP observability. The ``ban-raw-timeout-error``
pre-commit hook enforces this across ``lmcache/``.
"""
# Future
from __future__ import annotations
# Standard
import traceback
# First Party
from lmcache.logging import init_logger
from lmcache.v1.mp_observability.event import Event, EventType
from lmcache.v1.mp_observability.event_bus import (
get_event_bus,
is_observability_enabled,
)
logger = init_logger(__name__)
class LMCacheTimeoutError(TimeoutError):
"""``TimeoutError`` that publishes a ``TIMEOUT_RAISED`` event on construction.
Subclasses the builtin, so existing ``except TimeoutError`` handlers still
catch it. Publishing is skipped (no event, no stack capture) when
observability is disabled, and a publish failure never propagates out of
``__init__``.
Args:
message: Timeout description; also the event's ``message`` field.
session_id: Request id for correlating the timeout with its originating
request; empty when unavailable.
"""
def __init__(self, message: str, *, session_id: str = "") -> None:
super().__init__(message)
if not is_observability_enabled():
return
# Stack at the raise site, minus this __init__ frame.
stacktrace = "".join(traceback.format_stack()[:-1])
self._publish_timeout_event(message, stacktrace, session_id)
def _publish_timeout_event(
self, message: str, stacktrace: str, session_id: str
) -> None:
"""Publish a ``TIMEOUT_RAISED`` event; swallow any failure."""
try:
get_event_bus().publish(
Event(
event_type=EventType.TIMEOUT_RAISED,
metadata={
"message": message,
"exception_type": type(self).__name__,
"stacktrace": stacktrace,
},
session_id=session_id,
)
)
except Exception:
logger.debug("Failed to publish TIMEOUT_RAISED event", exc_info=True)