fix(web/api): scope DB-retry sleep so tests don't starve background tasks
test_lifespan_db_retry patched decnet.web.api.asyncio.sleep to skip the DB-retry backoff. Problem: asyncio is a shared module — the patch leaks to every caller that looked up asyncio.sleep via `import asyncio`, including run_health_heartbeat's own sleep loop. That heartbeat task spawns inside the same lifespan; with its sleep mocked, the while-loop spins tight, starves cancellation, and leaves an orphan task that pytest-timeout eventually signals — surfacing as the 'Task exception was never retrieved' warnings the user saw when running the suite. Fix: give decnet.web.api a local binding `_retry_sleep = asyncio.sleep` for the DB-retry wait, and have the test patch that instead. Narrowly scoped, no impact on asyncio.sleep callers elsewhere. Test timing before: 12s with --timeout=10 (interrupted by signal). Test timing after: 0.58s. Full tests/web slice: 27s → 7.1s with the spurious warnings gone.
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
import asyncio
|
||||
# Local binding for the DB-retry sleep so tests can patch it without
|
||||
# affecting `asyncio.sleep` globally (which would otherwise starve the
|
||||
# heartbeat / worker loops that share the interpreter's asyncio module).
|
||||
from asyncio import sleep as _retry_sleep
|
||||
import os
|
||||
import traceback
|
||||
import uuid
|
||||
@@ -75,7 +79,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
log.warning("DB init attempt %d/5 failed: %s", attempt, exc)
|
||||
if attempt == 5:
|
||||
log.error("DB failed to initialize after 5 attempts — startup may be degraded")
|
||||
await asyncio.sleep(0.5)
|
||||
await _retry_sleep(0.5)
|
||||
|
||||
# Conditionally enable OpenTelemetry tracing
|
||||
from decnet.telemetry import setup_tracing
|
||||
|
||||
Reference in New Issue
Block a user