[{"data":1,"prerenderedAt":929},["ShallowReactive",2],{"nav":3,"page-\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002F":310,"surround-\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002F":927},[4,96,212],{"title":5,"path":6,"stem":7,"children":8},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[9,12,30,42,54,66,90],{"title":10,"path":6,"stem":11},"Advanced Pydantic Validation and Serialization","advanced-pydantic-validation-serialization\u002Findex",{"title":13,"path":14,"stem":15,"children":16},"Custom Validators and Field Constraints in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Findex",[17,18,24],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Creating Reusable Custom Validators in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002Findex",[23],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":28},"Pydantic v2 Async Custom Validator: What to Do Instead","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator\u002Findex",[29],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"JSON Schema Customization in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[35,36],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"Customizing OpenAPI Schema Generation in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi\u002Findex",[41],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[47,48],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"Handling Deeply Nested JSON Models Efficiently","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently\u002Findex",[53],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Performance Optimization for Pydantic Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[59,60],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"Pydantic Model Serialization Performance in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance\u002Findex",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Pydantic V2 Migration Guide for FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[71,72,78,84],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Migrate @validator to @field_validator in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator\u002Findex",[77],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":82},"Migrating from Pydantic v1 to v2 Without Breaking APIs","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis\u002Findex",[83],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87,"children":88},"model_config vs class Config in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config\u002Findex",[89],{"title":85,"path":86,"stem":87},{"title":91,"path":92,"stem":93,"children":94},"Type Hinting and IDE Integration in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[95],{"title":91,"path":92,"stem":93},{"title":97,"path":98,"stem":99,"children":100},"Async Background Tasks Observability","\u002Fasync-background-tasks-observability","async-background-tasks-observability",[101,104,122,140,158,176,194],{"title":102,"path":98,"stem":103},"Async, Background Tasks, and Observability in FastAPI","async-background-tasks-observability\u002Findex",{"title":105,"path":106,"stem":107,"children":108},"Async Correctness and Concurrency in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Findex",[109,110,116],{"title":105,"path":106,"stem":107},{"title":111,"path":112,"stem":113,"children":114},"FastAPI async def vs def: Performance and When to Use Each","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance\u002Findex",[115],{"title":111,"path":112,"stem":113},{"title":117,"path":118,"stem":119,"children":120},"Fixing Blocking Calls in Async FastAPI Routes","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes\u002Findex",[121],{"title":117,"path":118,"stem":119},{"title":123,"path":124,"stem":125,"children":126},"Async Database Sessions in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions","async-background-tasks-observability\u002Fasync-database-sessions\u002Findex",[127,128,134],{"title":123,"path":124,"stem":125},{"title":129,"path":130,"stem":131,"children":132},"Async SQLAlchemy Session per Request in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request","async-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002Findex",[133],{"title":129,"path":130,"stem":131},{"title":135,"path":136,"stem":137,"children":138},"Fixing asyncpg Connection Pool Exhaustion in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion","async-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002Findex",[139],{"title":135,"path":136,"stem":137},{"title":141,"path":142,"stem":143,"children":144},"Background Task Processing in FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing","async-background-tasks-observability\u002Fbackground-task-processing\u002Findex",[145,146,152],{"title":141,"path":142,"stem":143},{"title":147,"path":148,"stem":149,"children":150},"FastAPI BackgroundTasks vs Celery vs ARQ","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq","async-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002Findex",[151],{"title":147,"path":148,"stem":149},{"title":153,"path":154,"stem":155,"children":156},"Running ARQ Workers with FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi","async-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi\u002Findex",[157],{"title":153,"path":154,"stem":155},{"title":159,"path":160,"stem":161,"children":162},"Caching Strategies in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies","async-background-tasks-observability\u002Fcaching-strategies\u002Findex",[163,164,170],{"title":159,"path":160,"stem":161},{"title":165,"path":166,"stem":167,"children":168},"Cache Invalidation Patterns in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi\u002Findex",[169],{"title":165,"path":166,"stem":167},{"title":171,"path":172,"stem":173,"children":174},"Redis Response Caching in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi\u002Findex",[175],{"title":171,"path":172,"stem":173},{"title":177,"path":178,"stem":179,"children":180},"Observability and Tracing in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing","async-background-tasks-observability\u002Fobservability-and-tracing\u002Findex",[181,182,188],{"title":177,"path":178,"stem":179},{"title":183,"path":184,"stem":185,"children":186},"Instrumenting FastAPI with OpenTelemetry","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry","async-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry\u002Findex",[187],{"title":183,"path":184,"stem":185},{"title":189,"path":190,"stem":191,"children":192},"Structured JSON Logging with Request IDs in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids\u002Findex",[193],{"title":189,"path":190,"stem":191},{"title":195,"path":196,"stem":197,"children":198},"Rate Limiting and Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Findex",[199,200,206],{"title":195,"path":196,"stem":197},{"title":201,"path":202,"stem":203,"children":204},"FastAPI Rate Limiting with Redis and SlowAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi","async-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi\u002Findex",[205],{"title":201,"path":202,"stem":203},{"title":207,"path":208,"stem":209,"children":210},"Per-User Token Bucket Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling\u002Findex",[211],{"title":207,"path":208,"stem":209},{"title":213,"path":214,"stem":215,"children":216},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[217,220,232,250,268,280,292],{"title":218,"path":214,"stem":219},"FastAPI Core Architecture and Routing Patterns","core-architecture-routing-patterns\u002Findex",{"title":221,"path":222,"stem":223,"children":224},"Application Factory Patterns in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[225,226],{"title":221,"path":222,"stem":223},{"title":227,"path":228,"stem":229,"children":230},"FastAPI App Factory Pattern for Testing and Deployment","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment\u002Findex",[231],{"title":227,"path":228,"stem":229},{"title":233,"path":234,"stem":235,"children":236},"Configuration Management in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[237,238,244],{"title":233,"path":234,"stem":235},{"title":239,"path":240,"stem":241,"children":242},"Managing Environment Variables with Pydantic Settings","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings\u002Findex",[243],{"title":239,"path":240,"stem":241},{"title":245,"path":246,"stem":247,"children":248},"Pydantic Settings vs Dynaconf vs python-decouple","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple\u002Findex",[249],{"title":245,"path":246,"stem":247},{"title":251,"path":252,"stem":253,"children":254},"Dependency Injection Strategies in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[255,256,262],{"title":251,"path":252,"stem":253},{"title":257,"path":258,"stem":259,"children":260},"Best Practices for FastAPI Dependency Injection","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection\u002Findex",[261],{"title":257,"path":258,"stem":259},{"title":263,"path":264,"stem":265,"children":266},"Fixing FastAPI Dependency Injection Circular Imports","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix\u002Findex",[267],{"title":263,"path":264,"stem":265},{"title":269,"path":270,"stem":271,"children":272},"Error Handling and Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[273,274],{"title":269,"path":270,"stem":271},{"title":275,"path":276,"stem":277,"children":278},"Global Exception Handlers for Consistent API Responses","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses\u002Findex",[279],{"title":275,"path":276,"stem":277},{"title":281,"path":282,"stem":283,"children":284},"Middleware Implementation in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[285,286],{"title":281,"path":282,"stem":283},{"title":287,"path":288,"stem":289,"children":290},"Implementing Custom Middleware for Request Tracing","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002Findex",[291],{"title":287,"path":288,"stem":289},{"title":293,"path":294,"stem":295,"children":296},"Modular Router Organization in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[297,298,304],{"title":293,"path":294,"stem":295},{"title":299,"path":300,"stem":301,"children":302},"APIRouter Prefix vs Sub-Application Mounting in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting\u002Findex",[303],{"title":299,"path":300,"stem":301},{"title":305,"path":306,"stem":307,"children":308},"How to Structure Large FastAPI Projects for Scale","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale\u002Findex",[309],{"title":305,"path":306,"stem":307},{"id":311,"title":135,"body":312,"description":879,"extension":880,"meta":881,"navigation":425,"path":136,"seo":925,"stem":137,"__hash__":926},"content\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002Findex.md",{"type":313,"value":314,"toc":866},"minimark",[315,319,326,354,363,368,371,375,383,387,392,507,511,631,635,708,712,732,736,756,760,833,837,862],[316,317,135],"h1",{"id":318},"fixing-asyncpg-connection-pool-exhaustion-in-fastapi",[320,321,322],"p",{},[323,324,325],"strong",{},"Key takeaways:",[327,328,329,333,341,344,351],"ul",{},[330,331,332],"li",{},"Exhaustion means connections are not returned fast enough — a leak or a sizing issue.",[330,334,335,336,340],{},"Scope every session through a ",[337,338,339],"code",{},"yield"," dependency so it always returns to the pool.",[330,342,343],{},"Keep slow, non-database work outside the transaction.",[330,345,346,347,350],{},"Size ",[337,348,349],{},"pool_size + max_overflow"," so total connections across workers fit the database limit.",[330,352,353],{},"Monitor checkouts and add a timeout to surface leaks early.",[320,355,356,357,362],{},"This guide is the failure-mode deep dive for ",[358,359,361],"a",{"href":360},"\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002F","Async Database Sessions",". Read that page for the engine-and-session setup.",[364,365,367],"h2",{"id":366},"the-problem-this-solves","The Problem This Solves",[320,369,370],{},"Under load, requests start timing out with \"QueuePool limit reached\" or connection-acquire timeouts, even though the database itself is healthy. The pool is drained because connections are being held too long or never returned. This guide finds which and fixes it.",[364,372,374],{"id":373},"prerequisites","Prerequisites",[327,376,377,380],{},[330,378,379],{},"An async SQLAlchemy engine on asyncpg.",[330,381,382],{},"Visibility into pool metrics or the ability to add them.",[364,384,386],{"id":385},"step-by-step-implementation","Step-by-Step Implementation",[388,389,391],"h3",{"id":390},"_1-ensure-yield-scoped-sessions","1. Ensure yield-scoped sessions",[393,394,399],"pre",{"className":395,"code":396,"language":397,"meta":398,"style":398},"language-python shiki shiki-themes github-light","from collections.abc import AsyncGenerator\n\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\n\nasync def get_session(request) -> AsyncGenerator[AsyncSession, None]:\n    async with request.app.state.session_factory() as session:\n        # Context manager guarantees the connection returns, even on error.\n        yield session\n","python","",[337,400,401,420,427,440,445,450,473,491,498],{"__ignoreMap":398},[402,403,406,410,414,417],"span",{"class":404,"line":405},"line",1,[402,407,409],{"class":408},"sD7c4","from",[402,411,413],{"class":412},"sgsFI"," collections.abc ",[402,415,416],{"class":408},"import",[402,418,419],{"class":412}," AsyncGenerator\n",[402,421,423],{"class":404,"line":422},2,[402,424,426],{"emptyLinePlaceholder":425},true,"\n",[402,428,430,432,435,437],{"class":404,"line":429},3,[402,431,409],{"class":408},[402,433,434],{"class":412}," sqlalchemy.ext.asyncio ",[402,436,416],{"class":408},[402,438,439],{"class":412}," AsyncSession\n",[402,441,443],{"class":404,"line":442},4,[402,444,426],{"emptyLinePlaceholder":425},[402,446,448],{"class":404,"line":447},5,[402,449,426],{"emptyLinePlaceholder":425},[402,451,453,456,459,463,466,470],{"class":404,"line":452},6,[402,454,455],{"class":408},"async",[402,457,458],{"class":408}," def",[402,460,462],{"class":461},"s7eDp"," get_session",[402,464,465],{"class":412},"(request) -> AsyncGenerator[AsyncSession, ",[402,467,469],{"class":468},"sYu0t","None",[402,471,472],{"class":412},"]:\n",[402,474,476,479,482,485,488],{"class":404,"line":475},7,[402,477,478],{"class":408},"    async",[402,480,481],{"class":408}," with",[402,483,484],{"class":412}," request.app.state.session_factory() ",[402,486,487],{"class":408},"as",[402,489,490],{"class":412}," session:\n",[402,492,494],{"class":404,"line":493},8,[402,495,497],{"class":496},"sAwPA","        # Context manager guarantees the connection returns, even on error.\n",[402,499,501,504],{"class":404,"line":500},9,[402,502,503],{"class":408},"        yield",[402,505,506],{"class":412}," session\n",[388,508,510],{"id":509},"_2-keep-transactions-short","2. Keep transactions short",[393,512,514],{"className":395,"code":513,"language":397,"meta":398,"style":398},"# Bad: external call inside the transaction holds the connection for its duration.\nasync def bad(session, order_id):\n    async with session.begin():\n        order = await session.get(Order, order_id)\n        await charge_external_api(order)   # Connection idle-but-held during this.\n\n# Good: do external work outside the transaction.\nasync def good(session, order_id):\n    order = await session.get(Order, order_id)\n    await charge_external_api(order)       # Connection already free.\n    async with session.begin():\n        order.status = \"charged\"\n",[337,515,516,521,533,542,556,567,571,576,587,598,610,619],{"__ignoreMap":398},[402,517,518],{"class":404,"line":405},[402,519,520],{"class":496},"# Bad: external call inside the transaction holds the connection for its duration.\n",[402,522,523,525,527,530],{"class":404,"line":422},[402,524,455],{"class":408},[402,526,458],{"class":408},[402,528,529],{"class":461}," bad",[402,531,532],{"class":412},"(session, order_id):\n",[402,534,535,537,539],{"class":404,"line":429},[402,536,478],{"class":408},[402,538,481],{"class":408},[402,540,541],{"class":412}," session.begin():\n",[402,543,544,547,550,553],{"class":404,"line":442},[402,545,546],{"class":412},"        order ",[402,548,549],{"class":408},"=",[402,551,552],{"class":408}," await",[402,554,555],{"class":412}," session.get(Order, order_id)\n",[402,557,558,561,564],{"class":404,"line":447},[402,559,560],{"class":408},"        await",[402,562,563],{"class":412}," charge_external_api(order)   ",[402,565,566],{"class":496},"# Connection idle-but-held during this.\n",[402,568,569],{"class":404,"line":452},[402,570,426],{"emptyLinePlaceholder":425},[402,572,573],{"class":404,"line":475},[402,574,575],{"class":496},"# Good: do external work outside the transaction.\n",[402,577,578,580,582,585],{"class":404,"line":493},[402,579,455],{"class":408},[402,581,458],{"class":408},[402,583,584],{"class":461}," good",[402,586,532],{"class":412},[402,588,589,592,594,596],{"class":404,"line":500},[402,590,591],{"class":412},"    order ",[402,593,549],{"class":408},[402,595,552],{"class":408},[402,597,555],{"class":412},[402,599,601,604,607],{"class":404,"line":600},10,[402,602,603],{"class":408},"    await",[402,605,606],{"class":412}," charge_external_api(order)       ",[402,608,609],{"class":496},"# Connection already free.\n",[402,611,613,615,617],{"class":404,"line":612},11,[402,614,478],{"class":408},[402,616,481],{"class":408},[402,618,541],{"class":412},[402,620,622,625,627],{"class":404,"line":621},12,[402,623,624],{"class":412},"        order.status ",[402,626,549],{"class":408},[402,628,630],{"class":629},"sYBdl"," \"charged\"\n",[388,632,634],{"id":633},"_3-size-the-pool-deliberately","3. Size the pool deliberately",[393,636,638],{"className":395,"code":637,"language":397,"meta":398,"style":398},"from sqlalchemy.ext.asyncio import create_async_engine\n\n# Per worker: at most 20 in-flight connections. Across 4 workers that is 80 —\n# keep it under the database's max_connections with headroom.\nengine = create_async_engine(url, pool_size=15, max_overflow=5, pool_timeout=10)\n",[337,639,640,651,655,660,665],{"__ignoreMap":398},[402,641,642,644,646,648],{"class":404,"line":405},[402,643,409],{"class":408},[402,645,434],{"class":412},[402,647,416],{"class":408},[402,649,650],{"class":412}," create_async_engine\n",[402,652,653],{"class":404,"line":422},[402,654,426],{"emptyLinePlaceholder":425},[402,656,657],{"class":404,"line":429},[402,658,659],{"class":496},"# Per worker: at most 20 in-flight connections. Across 4 workers that is 80 —\n",[402,661,662],{"class":404,"line":442},[402,663,664],{"class":496},"# keep it under the database's max_connections with headroom.\n",[402,666,667,670,672,675,679,681,684,687,690,692,695,697,700,702,705],{"class":404,"line":447},[402,668,669],{"class":412},"engine ",[402,671,549],{"class":408},[402,673,674],{"class":412}," create_async_engine(url, ",[402,676,678],{"class":677},"sqxcx","pool_size",[402,680,549],{"class":408},[402,682,683],{"class":468},"15",[402,685,686],{"class":412},", ",[402,688,689],{"class":677},"max_overflow",[402,691,549],{"class":408},[402,693,694],{"class":468},"5",[402,696,686],{"class":412},[402,698,699],{"class":677},"pool_timeout",[402,701,549],{"class":408},[402,703,704],{"class":468},"10",[402,706,707],{"class":412},")\n",[388,709,711],{"id":710},"_4-surface-leaks-with-a-timeout-and-metrics","4. Surface leaks with a timeout and metrics",[393,713,715],{"className":395,"code":714,"language":397,"meta":398,"style":398},"# pool_timeout raises promptly instead of hanging, making leaks visible.\n# Export the pool's checked-out count as a gauge for alerting.\ngauge.set(engine.pool.checkedout())\n",[337,716,717,722,727],{"__ignoreMap":398},[402,718,719],{"class":404,"line":405},[402,720,721],{"class":496},"# pool_timeout raises promptly instead of hanging, making leaks visible.\n",[402,723,724],{"class":404,"line":422},[402,725,726],{"class":496},"# Export the pool's checked-out count as a gauge for alerting.\n",[402,728,729],{"class":404,"line":429},[402,730,731],{"class":412},"gauge.set(engine.pool.checkedout())\n",[364,733,735],{"id":734},"edge-cases-and-gotchas","Edge Cases and Gotchas",[327,737,738,744,750],{},[330,739,740,743],{},[323,741,742],{},"Background workers."," They have their own pool; include them in the connection budget.",[330,745,746,749],{},[323,747,748],{},"Long-lived sessions."," A session stored beyond a request never returns; keep them request-scoped.",[330,751,752,755],{},[323,753,754],{},"Migrations."," A migration tool opening connections during a deploy can tip a tight budget over; leave headroom.",[364,757,759],{"id":758},"verification","Verification",[393,761,763],{"className":395,"code":762,"language":397,"meta":398,"style":398},"async def test_connections_return_under_load(app):\n    pool = app.state.engine.pool\n    before = pool.checkedout()\n    # Fire many concurrent requests, then confirm checkouts return to baseline.\n    await hammer(app, n=100)\n    assert pool.checkedout() == before\n",[337,764,765,777,787,797,802,819],{"__ignoreMap":398},[402,766,767,769,771,774],{"class":404,"line":405},[402,768,455],{"class":408},[402,770,458],{"class":408},[402,772,773],{"class":461}," test_connections_return_under_load",[402,775,776],{"class":412},"(app):\n",[402,778,779,782,784],{"class":404,"line":422},[402,780,781],{"class":412},"    pool ",[402,783,549],{"class":408},[402,785,786],{"class":412}," app.state.engine.pool\n",[402,788,789,792,794],{"class":404,"line":429},[402,790,791],{"class":412},"    before ",[402,793,549],{"class":408},[402,795,796],{"class":412}," pool.checkedout()\n",[402,798,799],{"class":404,"line":442},[402,800,801],{"class":496},"    # Fire many concurrent requests, then confirm checkouts return to baseline.\n",[402,803,804,806,809,812,814,817],{"class":404,"line":447},[402,805,603],{"class":408},[402,807,808],{"class":412}," hammer(app, ",[402,810,811],{"class":677},"n",[402,813,549],{"class":408},[402,815,816],{"class":468},"100",[402,818,707],{"class":412},[402,820,821,824,827,830],{"class":404,"line":452},[402,822,823],{"class":408},"    assert",[402,825,826],{"class":412}," pool.checkedout() ",[402,828,829],{"class":408},"==",[402,831,832],{"class":412}," before\n",[364,834,836],{"id":835},"related-reading","Related Reading",[327,838,839,848],{},[330,840,841,844,845,847],{},[323,842,843],{},"Up to the topic:"," ",[358,846,361],{"href":360},".",[330,849,850,844,853,857,858,847],{},[323,851,852],{},"Related guides:",[358,854,856],{"href":855},"\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002F","Async SQLAlchemy Session per Request"," and ",[358,859,861],{"href":860},"\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002F","Async Correctness and Concurrency",[863,864,865],"style",{},"html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}",{"title":398,"searchDepth":422,"depth":422,"links":867},[868,869,870,876,877,878],{"id":366,"depth":422,"text":367},{"id":373,"depth":422,"text":374},{"id":385,"depth":422,"text":386,"children":871},[872,873,874,875],{"id":390,"depth":429,"text":391},{"id":509,"depth":429,"text":510},{"id":633,"depth":429,"text":634},{"id":710,"depth":429,"text":711},{"id":734,"depth":422,"text":735},{"id":758,"depth":422,"text":759},{"id":835,"depth":422,"text":836},"Diagnose and fix asyncpg pool exhaustion in FastAPI: symptoms, correct pool sizing across workers, yield-scoped sessions, short transactions, and detecting leaked connections.","md",{"slug":882,"type":883,"breadcrumb":884,"datePublished":895,"dateModified":896,"howto":897,"faq":915},"fixing-asyncpg-pool-exhaustion","long_tail",[885,888,891,892],{"label":886,"path":887},"Home","\u002F",{"label":889,"path":890},"Async, Background Tasks & Observability","\u002Fasync-background-tasks-observability\u002F",{"label":361,"path":360},{"label":893,"path":894},"Fixing asyncpg Pool Exhaustion","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002F","2026-02-23","2026-06-18",{"name":898,"steps":899},"Fix asyncpg connection pool exhaustion in FastAPI",[900,903,906,909,912],{"name":901,"text":902},"Confirm the symptom","Identify timeouts waiting for a connection or QueuePool limit errors under load.",{"name":904,"text":905},"Scope sessions with yield","Ensure every session is acquired and released through a yield dependency.",{"name":907,"text":908},"Shorten transactions","Keep slow work outside the database transaction so connections return quickly.",{"name":910,"text":911},"Size the pool to fit","Set pool_size and max_overflow so total connections across workers stay under the database limit.",{"name":913,"text":914},"Detect leaks","Monitor pool checkout counts and add a checkout timeout to surface leaks early.",[916,919,922],{"q":917,"a":918},"What causes asyncpg pool exhaustion in FastAPI?","The pool runs out of free connections because they are not returned fast enough. The usual causes are sessions opened without yield-based cleanup, transactions held open while doing slow non-database work, or simply more concurrent requests than pool_size plus max_overflow can serve. The first two are bugs; the third is a sizing problem.",{"q":920,"a":921},"How do I size pool_size and max_overflow correctly?","The maximum connections one worker can open is pool_size plus max_overflow. Multiply that by the number of workers and keep the total under the database's max_connections, leaving headroom for migrations and other clients. Start from your real concurrency needs rather than guessing high.",{"q":923,"a":924},"Why does holding a transaction during an external API call exhaust the pool?","The connection stays checked out for the whole transaction. If you await a slow external call inside the transaction, that connection is unavailable to other requests for the entire call. Under load, connections pile up waiting and the pool drains. Do external work outside the transaction.",{"title":135,"description":879},"JxOYtmCIno5u8ZktVJjJwDDdHdvu105V3y1JqkIu2AI",[928,928],null,1781809863598]