This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
backendcritical PriorityID: python313-freethreaded-fastapi-pool-thread-safety-standard

Python 3.13 Free-Threaded FastAPI Async Connection Pool Thread-Safety Standard

Enforces strict thread-isolation and lock-free synchronization invariants for async connection pools in non-GIL CPython (Python 3.13t). In free-threaded runtimes, standard asyncio pools shared across concurrent ASGI worker threads cause memory corruption, atomic pointer races, and deadlocks. All connection resources must be strictly bound to thread-local event loops or synchronized via thread-safe primitives.

Prohibited Anti-Pattern: Global Async Connection Pool Sharing Across Free-Threaded OS Worker Threads

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Python 3.13 Free-Threaded FastAPI Async Connection Pool Thread-Safety Standard - Enforces strict thread-isolation and lock-free synchronization invariants for async connection pools in non-GIL CPython (Python 3.13t). In free-threaded runtimes, standard asyncio pools shared across concurrent ASGI worker threads cause memory corruption, atomic pointer races, and deadlocks. All connection resources must be strictly bound to thread-local event loops or synchronized via thread-safe primitives.
globs: src/**/*.py, app/**/*.py, api/**/*.py
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/python313-freethreaded-fastapi-pool-thread-safety-standard"
---

# Python 3.13 Free-Threaded FastAPI Async Connection Pool Thread-Safety Standard

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Global Async Connection Pool Sharing Across Free-Threaded OS Worker Threads

## System Constraints
You are a Principal Software Architect enforcing thread-safety and lock-free memory invariants for Python 3.13 Free-Threaded (PEP 703 nogil) FastAPI runtimes. The GIL no longer protects internal pointer updates or standard asyncio structures across native OS threads. You must mandate strict per-thread event loop pool isolation, prevent cross-thread pool acquisitions, and enforce deterministic cleanup.

## Strict Guidelines
- Engineers MUST NEVER share mutable asyncio-bound connection pools (e.g., asyncpg.Pool, redis.asyncio.ConnectionPool, aiohttp.ClientSession) across multiple OS threads in Python 3.13t.
- Services MUST instantiate async connection pools isolated per native OS worker thread using `threading.local()` or worker-local ASGI lifespans bound strictly to `asyncio.get_running_loop()`.
- Engineers MUST NOT execute async connection acquisition routines inside worker threads via `asyncio.to_thread` or `starlette.concurrency.run_in_threadpool` using an event loop owned by a different thread.
- Code MUST employ double-checked locking using `threading.Lock` when initializing lazy process-wide managers that distribute thread-isolated pool allocations.
- Components NEVER store connection objects in shared module-level mutable variables or cross-thread request states without thread-local encapsulation.
- Services MUST gracefully close and drain thread-local connection pools during thread termination or ASGI lifespan shutdown using explicit event loop cleanups.

## Bad vs Good Implementations
### Async Connection Pool Allocation in Python 3.13 Free-Threaded Runtime

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: In Python 3.13 free-threaded builds (nogil), sharing a single asyncio-bound connection pool across multiple concurrent ASGI worker threads violates asyncio loop invariants. Internal queue pointers and deques lack OS-level atomic synchronization, producing race conditions, memory corruption, and unrecoverable deadlocks during concurrent connection acquisition.
```python
import asyncpg
from fastapi import FastAPI, Request

app = FastAPI()

# ANTI-PATTERN: Single global pool shared across free-threaded OS threads
GLOBAL_POOL: asyncpg.Pool | None = None

@app.on_event("startup")
async def startup():
    global GLOBAL_POOL
    # In Python 3.13t, if FastAPI runs with multiple worker threads sharing this loop
    # or threads cross-acquire connections, asyncio internal queues face atomic races and corrupt state
    GLOBAL_POOL = await asyncpg.create_pool("postgresql://user:pass@localhost/db", min_size=5, max_size=20)

@app.get("/data")
async def get_data():
    # Unsynchronized concurrent acquire across free threads without GIL causes deadlocks
    async with GLOBAL_POOL.acquire() as conn:
        return await conn.fetch("SELECT 1;")
```

#### Production Standard (Correct)
> **Standard**: ThreadLocalConnectionPoolManager isolates async connection pools to their respective OS worker threads and running event loops using `threading.local()`. Thread registration is protected with a standard `threading.Lock`, preventing cross-thread pointer mutations and race conditions in free-threaded Python 3.13t while maintaining clean resource teardown.
```python
import asyncio
import threading
from typing import Optional
import asyncpg
from fastapi import FastAPI, Depends

app = FastAPI()

class ThreadLocalConnectionPoolManager:
    """Thread-safe manager isolating asyncpg pools to dedicated OS worker threads in Python 3.13t."""
    def __init__(self, dsn: str, min_size: int = 2, max_size: int = 10):
        self._dsn = dsn
        self._min_size = min_size
        self._max_size = max_size
        self._local = threading.local()
        self._lock = threading.Lock()
        self._pools: list[asyncpg.Pool] = []

    async def get_pool(self) -> asyncpg.Pool:
        loop = asyncio.get_running_loop()
        pool: Optional[asyncpg.Pool] = getattr(self._local, "pool", None)
        
        if pool is None or pool._loop is not loop or pool._closed:
            # Lock-guarded pool registration for graceful shutdown tracking
            new_pool = await asyncpg.create_pool(
                self._dsn,
                min_size=self._min_size,
                max_size=self._max_size,
                loop=loop
            )
            self._local.pool = new_pool
            with self._lock:
                self._pools.append(new_pool)
            return new_pool
            
        return pool

    async def close_all(self):
        with self._lock:
            pools = list(self._pools)
            self._pools.clear()
        for pool in pools:
            if not pool._closed:
                await pool.close()

pool_manager = ThreadLocalConnectionPoolManager("postgresql://user:pass@localhost/db")

async def get_db_connection():
    pool = await pool_manager.get_pool()
    async with pool.acquire() as conn:
        yield conn

@app.get("/data")
async def get_data(conn: asyncpg.Connection = Depends(get_db_connection)):
    return await conn.fetch("SELECT 1;")

@app.on_event("shutdown")
async def shutdown():
    await pool_manager.close_all()
```

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: Python 3.13 Free-Threaded FastAPI Async Connection Pool Thread-Safety Standard

Python 3.13 Free-Threaded FastAPI Async Connection Pool Thread-Safety Standard defines an industrial-grade behavioral standard for backend engineering. Enforces strict thread-isolation and lock-free synchronization invariants for async connection pools in non-GIL CPython (Python 3.13t). In free-threaded runtimes, standard asyncio pools shared across concurrent ASGI worker threads cause memory corruption, atomic pointer races, and deadlocks. All connection resources must be strictly bound to thread-local event loops or synchronized via thread-safe primitives. 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: Global Async Connection Pool Sharing Across Free-Threaded OS Worker Threads 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 DriftGlobal Async Connection Pool Sharing Across Free-Threaded OS Worker ThreadsCritical
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 NEVER share mutable asyncio-bound connection pools (e.g., asyncpg.Pool, redis.asyncio.ConnectionPool, aiohttp.ClientSession) across multiple OS threads in Python 3.13t.

Engineers MUST NEVER share mutable asyncio-bound connection pools (e.g., asyncpg.Pool, redis.asyncio.ConnectionPool, aiohttp.ClientSession) across multiple OS threads in Python 3.13t.

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

Services MUST instantiate async connection pools isolated per native OS worker thread using `threading.local()` or worker-local ASGI lifespans bound strictly to `asyncio.get_running_loop()`.

Services MUST instantiate async connection pools isolated per native OS worker thread using `threading.local()` or worker-local ASGI lifespans bound strictly to `asyncio.get_running_loop()`.

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 NOT execute async connection acquisition routines inside worker threads via `asyncio.to_thread` or `starlette.concurrency.run_in_threadpool` using an event loop owned by a different thread.

Engineers MUST NOT execute async connection acquisition routines inside worker threads via `asyncio.to_thread` or `starlette.concurrency.run_in_threadpool` using an event loop owned by a different thread.

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 MUST employ double-checked locking using `threading.Lock` when initializing lazy process-wide managers that distribute thread-isolated pool allocations.

Code MUST employ double-checked locking using `threading.Lock` when initializing lazy process-wide managers that distribute thread-isolated pool allocations.

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 store connection objects in shared module-level mutable variables or cross-thread request states without thread-local encapsulation.

Components NEVER store connection objects in shared module-level mutable variables or cross-thread request states without thread-local encapsulation.

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 gracefully close and drain thread-local connection pools during thread termination or ASGI lifespan shutdown using explicit event loop cleanups.

Services MUST gracefully close and drain thread-local connection pools during thread termination or ASGI lifespan shutdown using explicit event loop cleanups.

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 Python 3.13 Free-Threaded FastAPI Async Connection Pool Thread-Safety Standard critical for AI coding assistants?

Enforces strict thread-isolation and lock-free synchronization invariants for async connection pools in non-GIL CPython (Python 3.13t). In free-threaded runtimes, standard asyncio pools shared across concurrent ASGI worker threads cause memory corruption, atomic pointer races, and deadlocks. All connection resources must be strictly bound to thread-local event loops or synchronized via thread-safe primitives. 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 (Global Async Connection Pool Sharing Across Free-Threaded OS Worker Threads) 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

Python