Không tự động với response thường. Starlette chỉ phát hiện ngắt kết nối cho StreamingResponse; các endpoint khác vẫn chạy tới hết rồi mới ghi vào một socket đã đóng. Với endpoint nặng (báo cáo, gọi LLM), điều này nghĩa là tài nguyên vẫn bị tiêu tốn cho một client đã bỏ đi.
Cách kiểm tra thủ công trong vòng lặp xử lý:
@app.get("/report")
async def report(request: Request):
rows = []
for batch in batches:
if await request.is_disconnected():
logger.info("client gone, aborting")
return Response(status_code=499)
rows.extend(await process(batch))
return rowsCách chủ động hơn: chạy công việc trong asyncio.TaskGroup cùng một task theo dõi request.receive() để bắt message http.disconnect, thấy thì huỷ cả nhóm.
Bốn lưu ý khi vận hành:
- is_disconnected() không đáng tin khi có BaseHTTPMiddleware ở phía trên.
- Chỉ hỏi ở các mốc an toàn; huỷ giữa một transaction đang mở phải đảm bảo rollback trong finally.
- Client thường thử lại sau khi timeout, nên endpoint ghi dữ liệu vẫn cần idempotency key, không chỉ dựa vào phát hiện ngắt kết nối.
- Với tác vụ dài đúng nghĩa, giải pháp bền hơn là đổi sang mô hình bất đồng bộ: nhận job trả 202 kèm id, client hỏi trạng thái sau — client rời đi không còn ảnh hưởng gì.