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

FastAPI Lifespan State Pinning and In-Flight Request Drain Guardrail

Enforces deterministic lifecycle management for shared resources within FastAPI ASGI applications to guarantee zero-downtime rolling deployments. Prevents premature connection pool termination during SIGTERM drain cycles by pinning resource lifecycles strictly to app.state and executing non-destructive drain barriers before client disconnections.

Prohibited Anti-Pattern: Tearing down shared connection pools inside ASGI lifespan context before in-flight requests complete

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: FastAPI Lifespan State Pinning and In-Flight Request Drain Guardrail - Enforces deterministic lifecycle management for shared resources within FastAPI ASGI applications to guarantee zero-downtime rolling deployments. Prevents premature connection pool termination during SIGTERM drain cycles by pinning resource lifecycles strictly to app.state and executing non-destructive drain barriers before client disconnections.
globs: **/app/**/*.py, **/src/**/*.py, **/main.py, **/core/**/*.py
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/fastapi-lifespan-state-pinning-worker-drain-standard"
---

# FastAPI Lifespan State Pinning and In-Flight Request Drain Guardrail

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Tearing down shared connection pools inside ASGI lifespan context before in-flight requests complete

## System Constraints
You are a Principal Backend Architect enforcing zero-downtime ASGI lifecycle safety in FastAPI applications. Your objective is to ensure that database connection pools, HTTP client sessions, and worker threads pinned to `app.state` are never torn down prematurely while asynchronous HTTP requests remain in-flight during SIGTERM/SIGQUIT signal handling.

## Strict Guidelines
- Engineers MUST ALWAYS initialize shared stateful resources (database pools, HTTP clients, caches) exclusively within an `@asynccontextmanager` lifespan handler and bind them directly to `app.state`.
- Engineers MUST NOT initialize unpinned global singletons for connection pools or network clients outside the lifespan lifecycle.
- Services MUST implement an active request tracking barrier or drain gate within the lifespan teardown phase (post-yield) to allow in-flight coroutines to complete before executing resource closure routines like `engine.dispose()` or `client.aclose()`.
- Code SHOULD rely on FastAPI dependency injection referencing `request.app.state` rather than importing global state references across module boundaries.
- Components NEVER terminate network adapters abruptly without allowing a configurable grace period matching the container orchestrator termination grace period (e.g., Kubernetes `terminationGracePeriodSeconds`).

## Bad vs Good Implementations
### Lifespan State Pinning with Asynchronous Request Drain Barrier

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Global state leaks connection ownership outside FastAPI's ASGI lifecycle. Crucially, the teardown code immediately disposes of the database engine and HTTP client the moment SIGTERM arrives, throwing connection reset errors on active in-flight requests before the ASGI server finishes draining workers.
```python
from contextlib import asynccontextmanager
import httpx
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import create_async_engine

# Anti-pattern: Global unmanaged client and reckless immediate teardown
global_client: httpx.AsyncClient | None = None
engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")

@asynccontextmanager
async def lifespan(app: FastAPI):
    global global_client
    global_client = httpx.AsyncClient()
    yield
    # Flaw: Drops open connections instantly upon SIGTERM while requests are actively executing
    await global_client.aclose()
    await engine.dispose()

app = FastAPI(lifespan=lifespan)

@app.get("/data")
async def get_data():
    # In-flight execution fails with RuntimeError or ClosedPoolError during deployment
    resp = await global_client.get("https://api.internal/resource")
    return resp.json()
```

#### Production Standard (Correct)
> **Standard**: Resources are pinned exclusively to `app.state`, decoupling request dependencies from module globals. An ASGI middleware-backed drain tracker monitors active in-flight requests; during shutdown, lifespan teardown pauses until pending coroutines complete before safely closing external network pools.
```python
import asyncio
from contextlib import asynccontextmanager
from dataclasses import dataclass
import httpx
from fastapi import FastAPI, Request
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine

@dataclass
class DrainTracker:
    active_requests: int = 0
    drain_event: asyncio.Event = None

@asynccontextmanager
async def lifespan(app: FastAPI):
    # 1. State Pinning: Bind resources strictly to app.state
    app.state.http_client = httpx.AsyncClient(timeout=httpx.Timeout(10.0))
    app.state.db_engine: AsyncEngine = create_async_engine(
        "postgresql+asyncpg://user:pass@localhost/db",
        pool_pre_ping=True,
        pool_size=20
    )
    app.state.drain_tracker = DrainTracker(active_requests=0, drain_event=asyncio.Event())
    app.state.is_shutting_down = False
    app.state.drain_tracker.drain_event.set()

    yield

    # 2. Worker Graceful Drain Barrier: Signal shutdown and await in-flight requests
    app.state.is_shutting_down = True
    if app.state.drain_tracker.active_requests > 0:
        app.state.drain_tracker.drain_event.clear()
        try:
            # Wait up to 25s (aligned with container 30s SIGKILL grace period)
            await asyncio.wait_for(app.state.drain_tracker.drain_event.wait(), timeout=25.0)
        except asyncio.TimeoutError:
            pass

    # 3. Deterministic Teardown: Safely close resources after drain completes
    await app.state.http_client.aclose()
    await app.state.db_engine.dispose()

app = FastAPI(lifespan=lifespan)

@app.middleware("http")
async def track_in_flight_requests(request: Request, call_next):
    tracker: DrainTracker = request.app.state.drain_tracker
    tracker.active_requests += 1
    try:
        return await call_next(request)
    finally:
        tracker.active_requests -= 1
        if request.app.state.is_shutting_down and tracker.active_requests == 0:
            tracker.drain_event.set()

@app.get("/data")
async def get_data(request: Request):
    client: httpx.AsyncClient = request.app.state.http_client
    resp = await client.get("https://api.internal/resource")
    return resp.json()
```

Recommended MCP Servers (mcp-serverlist.com)

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

Architectural Specification & Invariant Rationale

Architectural Invariant Design: FastAPI Lifespan State Pinning and In-Flight Request Drain Guardrail

FastAPI Lifespan State Pinning and In-Flight Request Drain Guardrail defines an industrial-grade behavioral standard for backend engineering. Enforces deterministic lifecycle management for shared resources within FastAPI ASGI applications to guarantee zero-downtime rolling deployments. Prevents premature connection pool termination during SIGTERM drain cycles by pinning resource lifecycles strictly to app.state and executing non-destructive drain barriers before client disconnections. 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: Tearing down shared connection pools inside ASGI lifespan context before in-flight requests complete 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 DriftTearing down shared connection pools inside ASGI lifespan context before in-flight requests completeCritical
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 initialize shared stateful resources (database pools, HTTP clients, caches) exclusively within an `@asynccontextmanager` lifespan handler and bind them directly to `app.state`.

Engineers MUST ALWAYS initialize shared stateful resources (database pools, HTTP clients, caches) exclusively within an `@asynccontextmanager` lifespan handler and bind them directly to `app.state`.

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 NOT initialize unpinned global singletons for connection pools or network clients outside the lifespan lifecycle.

Engineers MUST NOT initialize unpinned global singletons for connection pools or network clients outside the lifespan lifecycle.

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

Services MUST implement an active request tracking barrier or drain gate within the lifespan teardown phase (post-yield) to allow in-flight coroutines to complete before executing resource closure routines like `engine.dispose()` or `client.aclose()`.

Services MUST implement an active request tracking barrier or drain gate within the lifespan teardown phase (post-yield) to allow in-flight coroutines to complete before executing resource closure routines like `engine.dispose()` or `client.aclose()`.

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

Code SHOULD rely on FastAPI dependency injection referencing `request.app.state` rather than importing global state references across module boundaries.

Code SHOULD rely on FastAPI dependency injection referencing `request.app.state` rather than importing global state references across module boundaries.

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

Components NEVER terminate network adapters abruptly without allowing a configurable grace period matching the container orchestrator termination grace period (e.g., Kubernetes `terminationGracePeriodSeconds`).

Components NEVER terminate network adapters abruptly without allowing a configurable grace period matching the container orchestrator termination grace period (e.g., Kubernetes `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.
Technical Questions & Answers

Frequently Asked Technical Questions

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

Q:Why is FastAPI Lifespan State Pinning and In-Flight Request Drain Guardrail critical for AI coding assistants?

Enforces deterministic lifecycle management for shared resources within FastAPI ASGI applications to guarantee zero-downtime rolling deployments. Prevents premature connection pool termination during SIGTERM drain cycles by pinning resource lifecycles strictly to app.state and executing non-destructive drain barriers before client disconnections. 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 (Tearing down shared connection pools inside ASGI lifespan context before in-flight requests complete) 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