Problems with await in async dependencies (when using dependencies and middleware simultaneously)
See original GitHub issueFirst Check
- I added a very descriptive title to this issue.
- I used the GitHub search to find a similar issue and didn’t find it.
- I searched the FastAPI documentation, with the integrated search.
- I already searched in Google “How to X in FastAPI” and didn’t find any information.
- I already read and followed all the tutorial in the docs and didn’t find an answer.
- I already checked if it is not related to FastAPI but to Pydantic.
- I already checked if it is not related to FastAPI but to Swagger UI.
- I already checked if it is not related to FastAPI but to ReDoc.
Commit to Help
- I commit to help with one of those options 👆
Example Code
import fastapi
import datetime as dt
import sqlalchemy as sa
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.orm import sessionmaker
class _Conn:
radius_engine: AsyncEngine
radius_session_maker: sessionmaker
def init_pg_connections() -> None:
_Conn.radius_engine = create_async_engine(
'postgresql+asyncpg://postgres:password@host:port/db_name',
connect_args={
'server_settings': {
'application_name': 'test_app'
}
},
pool_size=2, max_overflow=10
)
_Conn.radius_session_maker = sessionmaker(
bind=_Conn.radius_engine,
autoflush=False, autocommit=False,
expire_on_commit=False,
class_=AsyncSession
)
async def get_db():
db: AsyncSession = _Conn.radius_session_maker()
try:
yield db
finally:
print('finally get_db. Before close')
await db.close()
print('finally get_db. After close')
def get_app():
app = fastapi.FastAPI(
on_startup=[init_pg_connections]
)
@app.middleware('http')
async def add_process_time_header(request: fastapi.Request, call_next):
start_time = dt.datetime.now()
response = await call_next(request)
process_time = (dt.datetime.now() - start_time).total_seconds()
response.headers["X-Process-Time"] = str(process_time)
return response
@app.post('/test')
async def test(
data: int = fastapi.Body(5, embed=True),
db: AsyncSession = fastapi.Depends(get_db)
):
res = (await db.execute(sa.text('Select * FROM radacct'))).scalars().all()
print('test', f'{data=} {res=}')
return 'OK'
return app
if __name__ == '__main__':
import uvicorn
_app = get_app()
uvicorn.run(_app, host='10.10.10.98', port=7776)
Description
FastAPI version>=0.74 has a very strange problem with dependency (func get_db in example).
-
If you run this code as it is and send request from swagger every thing is ok (Connection with db will close and you see message ‘finally get_db. After close’ in console).
-
If you run this code as it is and use curl or aiohttp to send request, then “await db.close()” is never finish and you will not see message ‘finally get_db. After close’ in console. And if you continue to send requests, then you get warnings from Garbage collector and SQLAlchemy:
The garbage collector is trying to clean up connection <AdaptedConnection <asyncpg.connection.Connection object at 0x7fd3626a4740>>. This feature is unsupported on async dbapi, since no IO can be performed at this stage to reset the connection. Please close out all connections when they are no longer used, calling "close()" or using a context manager to manage their lifetime.
/usr/lib/python3.10/ipaddress.py:45: SAWarning: The garbage collector is trying to clean up connection <AdaptedConnection <asyncpg.connection.Connection object at 0x7fd3626a4740>>. This feature is unsupported on async dbapi, since no IO can be performed at this stage to reset the connection. Please close out all connections when they are no longer used, calling "close()" or using a context manager to manage their lifetime.
return IPv4Address(address)
-
If you comment middleware and send requests from any client (swagger, curl and aiohttp), then every thing is ok.
-
If you run this code as it is with FastAPI version <= 0.73, then every thing is ok.
In each cases clients has no problems in getting 200 responses from server. Unfortunately i have no idea why this happening.
Requirements: anyio==3.5.0 asgiref==3.5.0 asyncpg==0.25.0 click==8.0.4 coloredlogs==15.0.1 fastapi==0.75.0 | 0.73.0 greenlet==1.1.2 h11==0.13.0 httptools==0.3.0 humanfriendly==10.0 idna==3.3 mypy==0.931 mypy-extensions==0.4.3 pydantic==1.9.0 python-dateutil==2.8.2 python-dotenv==0.19.2 PyYAML==6.0 six==1.16.0 sniffio==1.2.0 SQLAlchemy==1.4.32 sqlalchemy2-stubs==0.0.2a20 starlette==0.17.1 tomli==2.0.1 typing_extensions==4.1.1 uvicorn==0.17.5 uvloop==0.16.0 watchgod==0.7 websockets==10.2
Operating System
Linux
Operating System Details
No response
FastAPI Version
=0.74
Python Version
3.9 and 3.10
Additional Context
No response
Issue Analytics
- State:
- Created a year ago
- Reactions:9
- Comments:34 (9 by maintainers)
Top GitHub Comments
Same here with
async_session
used inside a context manager, pinning fastapi to0.73.0
fixes it. Usingasyncmy
as driver.BaseHTTPMiddleware is fundamentally broken, I would not count on it being fixed.