Vì từ FastAPI 0.106.0, phần code sau yield của dependency chạy trước khi background task chạy — session đã đóng khi task chạm tới nó.
Trước 0.106.0, exit code chạy sau khi response được gửi và sau background task, nên nhiều người dùng lại session cũ trong task. Thay đổi này nhằm không giữ tài nguyên (connection DB) trong lúc response còn đang đi qua mạng, và để exception handler nhìn thấy được lỗi ném ra sau yield.
# broken on >= 0.106.0
@app.post("/orders")
async def create(bg: BackgroundTasks, db: Annotated[AsyncSession, Depends(get_db)]):
order = await repo.create(db, ...)
bg.add_task(send_receipt, db, order) # db is already closed
return {"id": order.id}
# correct: pass the id, open a new session inside the task
async def send_receipt(order_id: int):
async with SessionLocal() as db:
order = await db.get(Order, order_id)
...
bg.add_task(send_receipt, order.id)Nguyên tắc: background task phải tự quản lý tài nguyên của nó — truyền id chứ không truyền object ORM hay session.
Giới hạn cần nói thêm khi được hỏi tiếp: BackgroundTasks chạy trong cùng tiến trình web. Deploy lại hoặc process chết là mất task, không có retry, không quan sát được. Việc quan trọng (gửi mail thanh toán, đối soát) nên đẩy sang hàng đợi ngoài như Celery hoặc RQ.