Как управлялась обработка ресурсозатратных задач в Django с учетом синхронности
Что проверяют
Это вопрос о вашем опыте: как в проекте на Django уводили тяжёлую работу из обработки запроса и понимаете ли вы, чем очередь задач отличается от async-представлений. Следом спрашивают про надёжность: что будет с задачей, если воркер упал посередине.
Как отвечать
Расскажите свой случай по схеме: какая была задача, почему она мешала, что сделали и что показали замеры. Опирайтесь на свои цифры, а не на шаблонные проценты. Технический каркас обычно такой:
- проблема: под WSGI синхронный воркер gunicorn занят всё время, пока генерируется отчёт или обрабатывается файл. Пара таких запросов, и пул исчерпан, остальные пользователи ловят таймаут;
- очередь задач: Celery с брокером Redis или RabbitMQ, для проектов попроще RQ или Dramatiq. В Django 6.0 появился встроенный фреймворк задач
django.tasks, но исполнять их в продакшене всё равно приходится сторонним бэкендом; - постановка после коммита:
transaction.on_commit(lambda: build_report.delay(report.id)). В задачу передаётся id, а не объект модели; - надёжность:
acks_late=Trueвместе с идемпотентной задачей,autoretry_forиretry_backoffдля сетевых ошибок,soft_time_limitиtime_limitпротив зависаний,worker_prefetch_multiplier = 1для длинных задач; - разделение очередей: тяжёлые задачи в свою очередь со своими воркерами, чтобы отправка писем не стояла за рендером PDF;
- статус для пользователя: поле статуса в модели или result backend, фронт опрашивает эндпоинт или получает событие через Django Channels.
Отдельно про async: представления async def под ASGI помогают, когда запрос ждёт ввода-вывода, например внешних API. CPU-тяжёлую работу они не ускоряют, а синхронные вызовы внутри них оборачивают в sync_to_async.
Где спотыкаются
Ставят задачу внутри транзакции без on_commit. Воркер берёт её раньше, чем запись закоммичена, и получает DoesNotExist.
Передают в задачу объект модели. В JSON он не сериализуется, а с pickle уезжает устаревший снимок данных, который к моменту выполнения уже неверен.
Считают async-представления заменой очереди. Долгий расчёт в async-представлении блокирует цикл событий, и страдают все запросы этого процесса.