- Remove structlog dependency from backend/pyproject.toml - Add app.utils.logging_compat shim for keyword-arg logging API - Add app.utils.json_formatter for JSON log output with extra fields - Update all backend modules to use logging_compat.get_logger() - Update docstrings in log_sanitizer.py and json_formatter.py - Update test comment in test_async_utils.py - Record 406 failing tests in Docs/Tasks.md for tracking
63 lines
1.7 KiB
Python
63 lines
1.7 KiB
Python
"""Timeout protection utilities for background tasks.
|
|
|
|
Provides helpers to wrap async task functions with asyncio.wait_for() timeout
|
|
protection. Ensures tasks complete within bounded time or fail gracefully with
|
|
proper logging and error handling.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import time
|
|
from collections.abc import Awaitable
|
|
from typing import TypeVar
|
|
|
|
from app.utils.logging_compat import get_logger
|
|
|
|
log = get_logger(__name__)
|
|
|
|
T = TypeVar("T")
|
|
|
|
|
|
async def run_with_timeout(
|
|
task_name: str,
|
|
coro: Awaitable[T],
|
|
timeout_seconds: int,
|
|
) -> T:
|
|
"""Run an async coroutine with timeout protection.
|
|
|
|
Args:
|
|
task_name: Human-readable name of the task for logging.
|
|
coro: The coroutine to execute.
|
|
timeout_seconds: Maximum seconds to wait before timeout.
|
|
|
|
Raises:
|
|
asyncio.TimeoutError: If the task exceeds the timeout.
|
|
|
|
Returns:
|
|
The return value of the coroutine.
|
|
"""
|
|
start_time = time.monotonic()
|
|
try:
|
|
result: T = await asyncio.wait_for(coro, timeout=timeout_seconds)
|
|
elapsed = time.monotonic() - start_time
|
|
if elapsed > timeout_seconds * 0.8:
|
|
log.warning(
|
|
"task_approaching_timeout",
|
|
task_name=task_name,
|
|
timeout_seconds=timeout_seconds,
|
|
elapsed_seconds=round(elapsed, 2),
|
|
usage_percent=round((elapsed / timeout_seconds) * 100, 1),
|
|
)
|
|
return result
|
|
except TimeoutError:
|
|
elapsed = time.monotonic() - start_time
|
|
log.warning(
|
|
"task_timeout",
|
|
task_name=task_name,
|
|
timeout_seconds=timeout_seconds,
|
|
elapsed_seconds=round(elapsed, 2),
|
|
)
|
|
raise
|
|
|