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

FastAPI Lifespan Async Resource Cleanup & Signal Drain Standard

Mandates strict async resource lifecycle management via FastAPI lifespan context managers to prevent socket leaks, aborted transactions, and dropped traces during Kubernetes SIGTERM rolling deployments. Guarantees that in-flight requests, connection pools, streaming tasks, and telemetry exporters safely drain and shut down within bounded timeouts.

Prohibited Anti-Pattern: Unmanaged background tasks and deprecated on_event hooks abruptly severed by SIGTERM causing orphaned connections and corrupted state

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: FastAPI Lifespan Async Resource Cleanup & Signal Drain Standard - Mandates strict async resource lifecycle management via FastAPI lifespan context managers to prevent socket leaks, aborted transactions, and dropped traces during Kubernetes SIGTERM rolling deployments. Guarantees that in-flight requests, connection pools, streaming tasks, and telemetry exporters safely drain and shut down within bounded timeouts.
globs: app/**/*.py, src/**/*.py, main.py
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/fastapi-lifespan-async-resource-drain-standard"
---

# FastAPI Lifespan Async Resource Cleanup & Signal Drain Standard

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Unmanaged background tasks and deprecated on_event hooks abruptly severed by SIGTERM causing orphaned connections and corrupted state

## System Constraints
You are a Principal Software Architect enforcing production-grade resilience standards for FastAPI services. You must forbid deprecated `@app.on_event('startup')` and `@app.on_event('shutdown')` hooks in favor of `@asynccontextmanager` lifespan handlers. You must mandate bounded drain timeouts for all asynchronous background workers, explicit disposal of asynchronous database connection pools (`engine.dispose()`), cache client closures (`redis.aclose()`), and synchronous flushes of distributed tracing providers during application shutdown.

## Strict Guidelines
- Engineers MUST ALWAYS use the `@asynccontextmanager` lifespan protocol passed to `FastAPI(lifespan=...)` instead of deprecated `@app.on_event('startup')` or `@app.on_event('shutdown')` handlers.
- Engineers MUST NOT spawn detached fire-and-forget `asyncio.create_task` coroutines without retaining task references in a managed task registry or structured `asyncio.TaskGroup` to ensure deterministic drain during shutdown.
- Code SHOULD wrap task draining in `asyncio.wait_for` with an explicit timeout configured to be strictly less than the container orchestration `terminationGracePeriodSeconds` (e.g., timeout = grace_period - 5s).
- Services MUST explicitly call `await engine.dispose()` on SQLAlchemy async engines and `await client.aclose()` on async Redis/HTTP clients during the lifespan cleanup phase.
- Components NEVER exit the lifespan context manager without flushing and shutting down OpenTelemetry tracer/meter providers via `tracer_provider.shutdown()` to prevent telemetry loss.

## Bad vs Good Implementations
### Deprecated Event Handlers with Leaked Connections and Unbounded Tasks

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Uses deprecated on_event handlers, spawns an untracked background task that gets hard-killed on SIGTERM, and neglects to dispose of SQLAlchemy connection pools or close Redis clients, leading to socket exhaustion and aborted transactions.
```python
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import create_async_engine
import asyncio
import redis.asyncio as aioredis

app = FastAPI()
engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
redis = aioredis.from_url("redis://localhost")

@app.on_event("startup")
async def startup_event():
    # ANTI-PATTERN: Fire-and-forget background task without tracking or cancellation mechanism
    asyncio.create_task(background_metrics_worker())

@app.on_event("shutdown")
async def shutdown_event():
    # ANTI-PATTERN: Deprecated hook; fails to drain in-flight queries or cancel worker
    # Engine and Redis clients are not safely drained or closed
    pass

async def background_metrics_worker():
    while True:
        await asyncio.sleep(10)
```

#### Production Standard (Correct)
> **Standard**: Implements modern FastAPI lifespan context management. Accurately tracks active asynchronous tasks, cancels and awaits them within a bounded timeout, disposes of async database pools and Redis connections, and flushes OpenTelemetry tracers before process exit.
```python
from contextlib import asynccontextmanager
from typing import AsyncIterator, Set
import asyncio
import logging
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine
import redis.asyncio as aioredis
from opentelemetry.trace import get_tracer_provider

logger = logging.getLogger(__name__)

class ApplicationState:
    def __init__(self) -> None:
        self.db_engine: AsyncEngine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
        self.redis_client: aioredis.Redis = aioredis.from_url("redis://localhost")
        self.active_tasks: Set[asyncio.Task] = set()

state = ApplicationState()

async def managed_background_worker(tasks: Set[asyncio.Task]) -> None:
    try:
        while True:
            await asyncio.sleep(10)
    except asyncio.CancelledError:
        logger.info("Background worker cancelled, cleaning up state...")
        raise

@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[ApplicationState]:
    # 1. Startup: Initialize background tasks with explicit tracking
    worker_task = asyncio.create_task(managed_background_worker(state.active_tasks))
    state.active_tasks.add(worker_task)
    worker_task.add_done_callback(state.active_tasks.discard)

    yield state

    # 2. Shutdown: Bounded drain of background tasks
    logger.info("Initiating graceful shutdown drain...")
    for task in list(state.active_tasks):
        task.cancel()

    if state.active_tasks:
        drain_timeout_seconds = 10.0
        try:
            await asyncio.wait_for(
                asyncio.gather(*state.active_tasks, return_exceptions=True),
                timeout=drain_timeout_seconds,
            )
        except asyncio.TimeoutError:
            logger.error("Active tasks exceeded shutdown timeout of %s seconds", drain_timeout_seconds)

    # 3. Shutdown: Async database connection pool disposal
    await state.db_engine.dispose()

    # 4. Shutdown: Redis client teardown
    await state.redis_client.aclose()

    # 5. Shutdown: Telemetry provider flush
    tracer_provider = get_tracer_provider()
    if hasattr(tracer_provider, "shutdown"):
        tracer_provider.shutdown()
    logger.info("Shutdown complete: all connections disposed and traces flushed.")

app = FastAPI(lifespan=lifespan)
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: FastAPI Lifespan Async Resource Cleanup & Signal Drain Standard

FastAPI Lifespan Async Resource Cleanup & Signal Drain Standard defines an industrial-grade behavioral standard for backend engineering. Mandates strict async resource lifecycle management via FastAPI lifespan context managers to prevent socket leaks, aborted transactions, and dropped traces during Kubernetes SIGTERM rolling deployments. Guarantees that in-flight requests, connection pools, streaming tasks, and telemetry exporters safely drain and shut down within bounded timeouts. 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: Unmanaged background tasks and deprecated on_event hooks abruptly severed by SIGTERM causing orphaned connections and corrupted state 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 DriftUnmanaged background tasks and deprecated on_event hooks abruptly severed by SIGTERM causing orphaned connections and corrupted stateCritical
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 use the `@asynccontextmanager` lifespan protocol passed to `FastAPI(lifespan=...)` instead of deprecated `@app.on_event('startup')` or `@app.on_event('shutdown')` handlers.

Engineers MUST ALWAYS use the `@asynccontextmanager` lifespan protocol passed to `FastAPI(lifespan=...)` instead of deprecated `@app.on_event('startup')` or `@app.on_event('shutdown')` handlers.

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 spawn detached fire-and-forget `asyncio.create_task` coroutines without retaining task references in a managed task registry or structured `asyncio.TaskGroup` to ensure deterministic drain during shutdown.

Engineers MUST NOT spawn detached fire-and-forget `asyncio.create_task` coroutines without retaining task references in a managed task registry or structured `asyncio.TaskGroup` to ensure deterministic drain during 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.
3

Code SHOULD wrap task draining in `asyncio.wait_for` with an explicit timeout configured to be strictly less than the container orchestration `terminationGracePeriodSeconds` (e.g., timeout = grace_period - 5s).

Code SHOULD wrap task draining in `asyncio.wait_for` with an explicit timeout configured to be strictly less than the container orchestration `terminationGracePeriodSeconds` (e.g., timeout = grace_period - 5s).

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

Services MUST explicitly call `await engine.dispose()` on SQLAlchemy async engines and `await client.aclose()` on async Redis/HTTP clients during the lifespan cleanup phase.

Services MUST explicitly call `await engine.dispose()` on SQLAlchemy async engines and `await client.aclose()` on async Redis/HTTP clients during the lifespan cleanup phase.

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 exit the lifespan context manager without flushing and shutting down OpenTelemetry tracer/meter providers via `tracer_provider.shutdown()` to prevent telemetry loss.

Components NEVER exit the lifespan context manager without flushing and shutting down OpenTelemetry tracer/meter providers via `tracer_provider.shutdown()` to prevent telemetry loss.

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 Async Resource Cleanup & Signal Drain Standard critical for AI coding assistants?

Mandates strict async resource lifecycle management via FastAPI lifespan context managers to prevent socket leaks, aborted transactions, and dropped traces during Kubernetes SIGTERM rolling deployments. Guarantees that in-flight requests, connection pools, streaming tasks, and telemetry exporters safely drain and shut down within bounded timeouts. 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 (Unmanaged background tasks and deprecated on_event hooks abruptly severed by SIGTERM causing orphaned connections and corrupted state) 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