engine/session updates
This commit is contained in:
@@ -63,7 +63,7 @@ The engine is a long-lived factory and pool, not a single database connection. T
|
||||
|
||||
- Create one `AsyncEngine` per process and database configuration in the normal case.
|
||||
- Dispose it explicitly in an awaitable shutdown path; garbage collection cannot reliably await async driver cleanup.
|
||||
- Configure `async_sessionmaker` once and call it to create short-lived sessions.
|
||||
- Configure `async_sessionmaker` once inside the engine lifecycle and call it to create short-lived sessions.
|
||||
- Close each session deterministically with `async with` or a FastAPI dependency that yields once.
|
||||
|
||||
See [engine lifecycle](references/engine.md) and [session management](references/session.md).
|
||||
@@ -92,7 +92,7 @@ See [transaction boundaries](references/transactions.md).
|
||||
|
||||
FastAPI lifespan owns resources shared by many requests. A dependency with one `yield` owns request-scoped resources and runs cleanup after use. These are related context-manager mechanisms but solve different lifetime problems.
|
||||
|
||||
Use `AsyncExitStack` when lifespan acquires a variable, conditional, or mixed collection of context-managed resources. It records cleanup as resources are acquired and unwinds callbacks in reverse order. A single engine with one cleanup callback can use a plain `try/finally`; `AsyncExitStack` is a composition tool, not a requirement.
|
||||
Use `AsyncExitStack` when lifespan acquires a variable, conditional, or mixed collection of context-managed resources. It records cleanup as resources are acquired and unwinds callbacks in reverse order. A single engine should use the direct engine context manager; `AsyncExitStack` is a composition tool, not a requirement.
|
||||
|
||||
See [engine lifecycle](references/engine.md).
|
||||
|
||||
@@ -138,34 +138,26 @@ See [database testing and fixture data](references/testing.md).
|
||||
This example shows the ownership boundaries. Adapt state storage and dependency wiring to the application's conventions.
|
||||
|
||||
```python
|
||||
from contextlib import AsyncExitStack, asynccontextmanager
|
||||
from collections.abc import AsyncGeneratorr
|
||||
from collections.abc import AsyncGenerator
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI
|
||||
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
|
||||
from fastapi import FastAPI, Request
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI) -> AsyncGenerator[None]:
|
||||
async with AsyncExitStack() as stack:
|
||||
engine = create_async_engine(settings.database_url)
|
||||
stack.push_async_callback(engine.dispose)
|
||||
|
||||
session_factory = async_sessionmaker(
|
||||
engine,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False,
|
||||
)
|
||||
app.state.session_factory = session_factory
|
||||
async with engine_scope(settings.database_url) as engine:
|
||||
app.state.session_factory = create_session_factory(engine)
|
||||
yield
|
||||
|
||||
|
||||
async def get_session() -> AsyncGenerator[AsyncSession]:
|
||||
async with app.state.session_factory() as session:
|
||||
yield session
|
||||
async def get_session(request: Request) -> AsyncGenerator[AsyncSession]:
|
||||
async with request.app.state.session_factory() as session:
|
||||
yield session
|
||||
```
|
||||
|
||||
For direct construction without `AsyncExitStack`, put `await engine.dispose()` in a `finally` block. For background work that outlives a request, create a new session inside that task instead of retaining the request's session.
|
||||
`engine_scope()` and `create_session_factory()` are defined in the engine and session references. For background work that outlives a request, inject the shared factory and create a new session inside that task instead of retaining the request's session.
|
||||
|
||||
## Explanation Procedure
|
||||
|
||||
|
||||
Reference in New Issue
Block a user