Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 3 additions & 16 deletions livekit-agents/livekit/agents/utils/aio/debug.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,9 @@
from __future__ import annotations

import asyncio
import time
from asyncio.base_events import _format_handle # type: ignore
from typing import Any

from ...log import logger


def hook_slow_callbacks(slow_duration: float) -> None:
_run = asyncio.events.Handle._run

def instrumented(self: Any) -> Any:
start = time.monotonic()
val = _run(self)
dt = time.monotonic() - start
if dt >= slow_duration:
logger.warning("Running %s took too long: %.2f seconds", _format_handle(self), dt)
return val

asyncio.events.Handle._run = instrumented # type: ignore
loop = asyncio.get_event_loop()
loop.slow_callback_duration = slow_duration
loop.set_debug(True)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 set_debug(True) unconditionally enables full asyncio debug mode, adding significant overhead beyond slow callback detection

The old implementation monkey-patched asyncio.events.Handle._run to precisely detect only slow callbacks, with no other side effects. The new implementation calls loop.set_debug(True) which is required for slow_callback_duration to take effect (CPython only checks slow callbacks inside _run_once when self._debug is True), but this also enables the full asyncio debug mode. This includes: storing creation tracebacks for all tasks/transports (memory overhead), logging destroyed pending tasks, extra resource-cleanup checks, and other diagnostics. The function is named hook_slow_callbacks suggesting it should be safe for production performance monitoring, but full debug mode introduces non-trivial overhead. The old implementation achieved targeted slow callback detection without any of these side effects.

Note the contrast with proc_client.py:63 where set_debug() is conditional on an explicit asyncio_debug flag, indicating the codebase treats debug mode as an opt-in concern separate from slow callback duration.

Prompt for agents
The problem is that loop.set_debug(True) is needed for slow_callback_duration to work via the built-in asyncio mechanism, but it enables the full asyncio debug mode with significant overhead (task creation tracebacks, resource tracking, etc.). The old monkey-patching approach in this function was specifically designed to detect slow callbacks without enabling full debug mode.

Possible approaches:
1. Revert to the targeted monkey-patching approach from the old code, which only wrapped Handle._run with timing logic and logged via the agents logger. This gives slow callback detection without debug mode overhead.
2. If the built-in mechanism is preferred, document clearly that this function enables full asyncio debug mode as a side effect, and possibly rename the function to reflect its broader impact.
3. Consider a hybrid: keep the monkey-patching for slow callback detection but use the agents own logger instead of relying on asyncio's built-in debug logging.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Loading