Что такое фоновые задачи (background tasks) в FastAPI и когда их следует использовать?
BackgroundTasks в FastAPI позволяет запустить функцию после отправки ответа клиенту, не блокируя его ожидание; подходит для лёгких задач (email, аудит), но не заменяет очередь задач типа ARQ или Celery.
Механизм работы
BackgroundTasks — класс из fastapi, реализованный поверх starlette.background.BackgroundTasks. FastAPI внедряет его через DI: достаточно добавить параметр типа BackgroundTasks в сигнатуру обработчика. Задача добавляется методом background_tasks.add_task(func, *args, **kwargs) и выполняется после того, как ответ отправлен клиенту — в том же процессе и том же event loop (для async-функций) или в пуле потоков (для sync-функций).
Базовый пример
import logging
from fastapi import FastAPI, BackgroundTasks
from pydantic import BaseModel, EmailStr
app = FastAPI()
log = logging.getLogger(__name__)
def send_welcome_email(email: str, username: str) -> None:
# Синхронная функция — FastAPI выполнит её в ThreadPoolExecutor
log.info("Sending welcome email to %s", email)
# ... вызов SMTP или Resend API ...
class UserCreate(BaseModel):
email: EmailStr
username: str
@app.post("/users", status_code=201)
async def create_user(
body: UserCreate,
background_tasks: BackgroundTasks,
):
# Сохраняем пользователя в БД (здесь опущено для краткости)
background_tasks.add_task(send_welcome_email, body.email, body.username)
return {"id": 42, "username": body.username}
Async-вариант
import httpx
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
async def notify_webhook(url: str, payload: dict) -> None:
async with httpx.AsyncClient() as client:
await client.post(url, json=payload, timeout=10.0)
@app.post("/events")
async def publish_event(
payload: dict,
background_tasks: BackgroundTasks,
):
background_tasks.add_task(notify_webhook, "https://hooks.example.com", payload)
return {"status": "accepted"}
Когда использовать BackgroundTasks
- Отправка email или push-уведомления после регистрации/заказа.
- Запись в audit log, когда задержка ответа нежелательна.
- Инвалидация кэша или обновление счётчиков, не требующее транзакции.
- Лёгкие webhook-уведомления во внешние системы.
Когда BackgroundTasks недостаточно
BackgroundTasks выполняется в том же процессе. Если процесс завершится (crash, deploy), задача будет потеряна. Для надёжной доставки, повторных попыток и мониторинга используйте очередь задач: ARQ (asyncio + Redis), Celery (с broker Redis или RabbitMQ), RQ.
# Пример с ARQ вместо BackgroundTasks для надёжной доставки
import arq
from fastapi import FastAPI
app = FastAPI()
@app.post("/orders", status_code=201)
async def create_order(body: dict, redis: arq.ArqRedis):
order_id = 99 # сохранить в БД
await redis.enqueue_job("send_order_confirmation", order_id=order_id)
return {"id": order_id}
Подводные камни
- Потеря задачи при сбое процесса. BackgroundTasks не персистентны — если Uvicorn упадёт, задача не выполнится и не будет повторена.
- Исключения не доходят до клиента. Ошибка в фоновой задаче логируется, но не влияет на HTTP-ответ; нужно явно настроить обработку ошибок (try/except + logging).
- Shared state и DB-сессии. Нельзя использовать SQLAlchemy-сессию, созданную в обработчике, внутри фоновой задачи — она может быть закрыта к моменту выполнения. Создавайте новую сессию внутри задачи.
- Нет контроля конкурентности. При высокой нагрузке множество фоновых задач выполняются одновременно без ограничений; для ограничения используйте Semaphore или выносите задачи в worker.
- Нет retry-логики. BackgroundTasks не поддерживает повторные попытки при ошибке сети или SMTP; реализуйте её вручную или переходите на ARQ/Celery.
- Тестирование. В тестах с
TestClientфоновые задачи выполняются синхронно до возврата из вызова; сAsyncClient(httpx) — тоже, но это поведение не задокументировано и может измениться. - Зависимости недоступны. Параметры с
Depends()не передаются в функцию задачи автоматически — нужно явно передавать нужные объекты через аргументыadd_task.
Common mistakes
- Описывать background tasks только как термин и не показывать механизм на минимальном примере.
- Игнорировать ошибки, пустые данные, конкурентный доступ или границы транзакции.
- Не связывать поведение с официальным контрактом FastAPI и реальной эксплуатацией.
What the interviewer is testing
- Объясняет background tasks через последовательность действий, а не через набор ключевых слов.
- Приводит короткий кодовый пример или production-сценарий с ожидаемым поведением.
- Называет хотя бы один риск: производительность, безопасность, транзакции, память или сопровождение.