Skip to content

fix(api): non-blocking asyncio.sleep in lifespan db-retry backoff (#140) - #142

Draft
Jovonni wants to merge 1 commit into
masterfrom
fix/async-sleep-lifespan
Draft

fix(api): non-blocking asyncio.sleep in lifespan db-retry backoff (#140)#142
Jovonni wants to merge 1 commit into
masterfrom
fix/async-sleep-lifespan

Conversation

@Jovonni

@Jovonni Jovonni commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

What

The FastAPI startup lifespan retried init_db() with a blocking time.sleep(retry_delay) inside an async def. That blocks the event loop for the full backoff window on every failed attempt, so anything sharing the loop from the first instant (health/readiness probes) can be delayed while the DB is reconnecting.

Swaps it for await asyncio.sleep(retry_delay) (non-blocking), and adds import asyncio at module top.

Why

Flagged in #140 — small, correct one-liner with a real (if narrow) impact on startup responsiveness.

Tests

New regression subsuite core/tests/test_lifespan_async.py:

  • static guard — the lifespan source contains no time.sleep and uses await asyncio.sleep
  • functional — init_db() fails once then succeeds; asserts the retry path awaits the non-blocking sleep exactly once (event loop never blocks)

Runs under the existing core/tests/ pytest suite in CI.

Closes #140

The startup lifespan retried init_db() with a blocking time.sleep()
inside an async function, which stalls the event loop during db
reconnection — delaying readiness/health probes that share the loop.
Switch to await asyncio.sleep() and add a regression test suite.

Closes #140

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR improves FastAPI startup responsiveness by replacing a blocking backoff (time.sleep) inside the async lifespan retry loop with a non-blocking await asyncio.sleep, and adds a regression test to prevent reintroducing blocking sleeps in the future.

Changes:

  • Replace time.sleep(retry_delay) with await asyncio.sleep(retry_delay) in the lifespan DB-init retry backoff.
  • Add import asyncio to support the non-blocking sleep call.
  • Add a new pytest module covering both a static guard and a functional retry/backoff behavior check.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
core/fastapi_app.py Switches lifespan retry backoff to non-blocking await asyncio.sleep and imports asyncio.
core/tests/test_lifespan_async.py Adds regression tests asserting the lifespan retry path uses non-blocking sleep.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +23 to +25
source = inspect.getsource(fastapi_app.lifespan)
assert "time.sleep" not in source, "lifespan must not block the event loop with time.sleep"
assert "await asyncio.sleep" in source, "lifespan backoff must use await asyncio.sleep"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

time.sleep() inside async lifespan — intentional or worth switching to await asyncio.sleep()?

2 participants