This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
backendcritical PriorityID: fastapi-async-lifespan-deterministic-drain-standard

FastAPI Async Lifespan Deterministic Connection Pool Drain Standard

Enforces deterministic cancellation tokens and socket draining across async HTTP and database client pools during FastAPI ASGI lifespan shutdown. Guarantees in-flight transactions finish within bounded grace periods and prevents socket leakage and RST dropouts during rolling Kubernetes pod evictions.

Prohibited Anti-Pattern: Leaking sockets and severing active transactions via uncoordinated async pool teardown on SIGTERM

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: FastAPI Async Lifespan Deterministic Connection Pool Drain Standard - Enforces deterministic cancellation tokens and socket draining across async HTTP and database client pools during FastAPI ASGI lifespan shutdown. Guarantees in-flight transactions finish within bounded grace periods and prevents socket leakage and RST dropouts during rolling Kubernetes pod evictions.
globs: **/main.py, **/lifespan.py, **/core/lifespan.py, **/core/database.py, **/app/**/*.py
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/fastapi-async-lifespan-deterministic-drain-standard"
---

# FastAPI Async Lifespan Deterministic Connection Pool Drain Standard

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Leaking sockets and severing active transactions via uncoordinated async pool teardown on SIGTERM

## System Constraints
You are a Principal Software Architect enforcing the FastAPI Async Lifespan Connection Pool Deterministic Drain Standard. You mandate structured async lifecycle management using contextlib.asynccontextmanager over legacy on_event hooks. Every async client pool (asyncpg, SQLAlchemy AsyncEngine, httpx.AsyncClient, Redis) must register with a coordinated shutdown token, shield inflight transactions against premature cancellation, and enforce explicit bounded timeouts on socket disposal during ASGI shutdown phases.

## Strict Guidelines
- Engineers MUST ALWAYS implement lifespan handlers using `@contextlib.asynccontextmanager` passed to `FastAPI(lifespan=...)` and strictly avoid deprecated `@app.on_event('startup')` or `@app.on_event('shutdown')`.
- Engineers MUST ALWAYS coordinate application teardown with a process-level `asyncio.Event` cancellation token exposed via `app.state.shutdown_event`.
- Engineers MUST wrap all pool drain and close operations in `asyncio.wait_for` with bounded grace timeouts strictly smaller than the orchestrator `terminationGracePeriodSeconds`.
- Engineers MUST ALWAYS protect pool teardown routines with `asyncio.shield` to prevent immediate SIGINT/SIGTERM cancellation from leaving underlying TCP sockets in undefined states.
- Engineers MUST execute pool draining in strict topological order: stop incoming ingestion tokens first, wait for pending background task completion, drain outbound HTTP clients, and finally close relational/caching database pools.
- Services MUST catch `asyncio.CancelledError` inside background tasks, clean up local resources deterministically, and re-raise or acknowledge task completion.

## Bad vs Good Implementations
### Async Pool Lifespan Draining Architecture

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Deprecated on_event shutdown lacks structured timeout boundaries, fails to signal worker loops to stop accepting work, and leaves async pools vulnerable to unhandled CancelledError during orchestrator pod termination, causing silent data corruption and client 502s.
```python
import httpx
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import create_async_engine

app = FastAPI()
engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
http_client = httpx.AsyncClient()

# Anti-pattern: Deprecated event handlers with unshielded, uncoordinated closes
@app.on_event("shutdown")
async def shutdown_event():
    # If SIGTERM sends cancellation, these calls are aborted immediately mid-flight,
    # dropping active database transactions and severing open HTTP sockets without drain.
    await http_client.aclose()
    await engine.dispose()
```

#### Production Standard (Correct)
> **Standard**: Uses the modern lifespan context manager with a cooperative shutdown event, wraps teardown routines in asyncio.shield to prevent mid-operation SIGTERM cancellation, and enforces a strict asyncio.wait_for timeout to ensure all client sockets are deterministically closed before pod eviction.
```python
import asyncio
import logging
from contextlib import asynccontextmanager
from typing import AsyncGenerator
import httpx
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine

logger = logging.getLogger("uvicorn.lifespan")
SHUTDOWN_TIMEOUT_SECONDS: float = 10.0

@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
    # 1. Initialize shared resources and cancellation token
    shutdown_event = asyncio.Event()
    engine: AsyncEngine = create_async_engine(
        "postgresql+asyncpg://user:pass@localhost/db",
        pool_pre_ping=True,
        pool_recycle=300,
    )
    client = httpx.AsyncClient(timeout=httpx.Timeout(5.0))
    
    app.state.shutdown_event = shutdown_event
    app.state.db_engine = engine
    app.state.http_client = client
    
    yield
    
    # 2. Teardown: Signal all consumer tasks to halt new operations
    logger.info("Starting deterministic connection pool drain...")
    shutdown_event.set()
    
    async def _drain_pools() -> None:
        # Phase A: Drain and close outbound HTTP clients
        try:
            await client.aclose()
        except Exception as exc:
            logger.error(f"Error closing HTTP client: {exc}")
            
        # Phase B: Drain relational database pool
        try:
            await engine.dispose()
        except Exception as exc:
            logger.error(f"Error disposing database engine: {exc}")

    # 3. Shield teardown from external cancellation within strict timeout
    try:
        await asyncio.wait_for(
            asyncio.shield(_drain_pools()),
            timeout=SHUTDOWN_TIMEOUT_SECONDS,
        )
        logger.info("Connection pools drained successfully.")
    except asyncio.TimeoutError:
        logger.critical("Drain timed out; forcing abrupt socket closure.")

app = FastAPI(lifespan=lifespan)
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Recommended Agent Skills (askills.dev)

Ready-to-use executable skill functions tailored to this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: FastAPI Async Lifespan Deterministic Connection Pool Drain Standard

FastAPI Async Lifespan Deterministic Connection Pool Drain Standard defines an industrial-grade behavioral standard for backend engineering. Enforces deterministic cancellation tokens and socket draining across async HTTP and database client pools during FastAPI ASGI lifespan shutdown. Guarantees in-flight transactions finish within bounded grace periods and prevents socket leakage and RST dropouts during rolling Kubernetes pod evictions. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Leaking sockets and severing active transactions via uncoordinated async pool teardown on SIGTERM Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftLeaking sockets and severing active transactions via uncoordinated async pool teardown on SIGTERMCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS implement lifespan handlers using `@contextlib.asynccontextmanager` passed to `FastAPI(lifespan=...)` and strictly avoid deprecated `@app.on_event('startup')` or `@app.on_event('shutdown')`.

Engineers MUST ALWAYS implement lifespan handlers using `@contextlib.asynccontextmanager` passed to `FastAPI(lifespan=...)` and strictly avoid deprecated `@app.on_event('startup')` or `@app.on_event('shutdown')`.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST ALWAYS coordinate application teardown with a process-level `asyncio.Event` cancellation token exposed via `app.state.shutdown_event`.

Engineers MUST ALWAYS coordinate application teardown with a process-level `asyncio.Event` cancellation token exposed via `app.state.shutdown_event`.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Engineers MUST wrap all pool drain and close operations in `asyncio.wait_for` with bounded grace timeouts strictly smaller than the orchestrator `terminationGracePeriodSeconds`.

Engineers MUST wrap all pool drain and close operations in `asyncio.wait_for` with bounded grace timeouts strictly smaller than the orchestrator `terminationGracePeriodSeconds`.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Engineers MUST ALWAYS protect pool teardown routines with `asyncio.shield` to prevent immediate SIGINT/SIGTERM cancellation from leaving underlying TCP sockets in undefined states.

Engineers MUST ALWAYS protect pool teardown routines with `asyncio.shield` to prevent immediate SIGINT/SIGTERM cancellation from leaving underlying TCP sockets in undefined states.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Engineers MUST execute pool draining in strict topological order

Engineers MUST execute pool draining in strict topological order: stop incoming ingestion tokens first, wait for pending background task completion, drain outbound HTTP clients, and finally close relational/caching database pools.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
6

Services MUST catch `asyncio.CancelledError` inside background tasks, clean up local resources deterministically, and re-raise or acknowledge task completion.

Services MUST catch `asyncio.CancelledError` inside background tasks, clean up local resources deterministically, and re-raise or acknowledge task completion.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is FastAPI Async Lifespan Deterministic Connection Pool Drain Standard critical for AI coding assistants?

Enforces deterministic cancellation tokens and socket draining across async HTTP and database client pools during FastAPI ASGI lifespan shutdown. Guarantees in-flight transactions finish within bounded grace periods and prevents socket leakage and RST dropouts during rolling Kubernetes pod evictions. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Leaking sockets and severing active transactions via uncoordinated async pool teardown on SIGTERM) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

1 Rule in Stack

FastAPI