FastAPI sqlalchemy session per request handling #10622
Replies: 2 comments
-
the 2 logged connect calls maybe just pure python calls? |
Beta Was this translation helpful? Give feedback.
-
A warning/tip in sqlalchemy docs:
We store the database session maker in the app state like this: class DatabaseState:
def __init__(self):
self.engine = create_async_engine(
current_config.SQLALCHEMY_DATABASE_URI,
connect_args={
"cursor_factory": AsyncClientCursor,
"connect_timeout": 2,
"keepalives_idle": 60,
},
# NullPool because we are using pgbouncer, if connecting directly to postgresql, use a pool
poolclass=NullPool,
echo=False,
)
# https://docs.sqlalchemy.org/en/20/orm/session_api.html#sqlalchemy.orm.Session.params.expire_on_commit
self.session_maker: async_sessionmaker[AsyncSession] = async_sessionmaker(
self.engine, expire_on_commit=False
) And then we have a dependency injection for database session: async def get_db_session(request: Request) -> AsyncSession:
async with request.app.state.database.session_maker() as session:
try:
yield session
except Exception as e:
logger.debug(f"Rolling back due to exception {e}.")
await session.rollback()
raise e
finally:
await session.close() In our app, when we need the database session we use the dependency injection: @router.get('/event/{event_id}')
async def test(event_id: str, event_service: EventService = Depends(EventService):
event1 = await event_service.get_event(event_id)
event2 = await event_service.get_event(event_id)
return BaseResponse[List[GetEventResponse]](data=[event1, event2])
class EventService:
def __init__(self, db_session: AsyncSession = Depends(get_db_session)) -> None:
self.db_session = db_session It works pretty well with multiple threads, the only issue is when pgbouncer goes to shutdown state (graceful shutdown) the connect step stays stuck until connection timeout. However, all active connection finish the operation successfully. We need a reconnect implementation, but we haven't had time to add that yet. |
Beta Was this translation helpful? Give feedback.
-
First Check
Commit to Help
Example Code
EventService
:The
EventRepository
is just a subclass of theBaseRepository
:Database manager:
And our dependency injector:
Description
We want to share the database session per request. Right now when we call the
/event/{event_id}
test route 2 sessions will be created, which is not what we want. We also see this in our traces on GCP on other routes (connect
):So what could be a solution to create a database session per request?
Operating System
Linux
Operating System Details
No response
FastAPI Version
0.103.2
Pydantic Version
2.4.2
Python Version
Python 3.11.5
Additional Context
No response
Beta Was this translation helpful? Give feedback.
All reactions