Как реализуются фоновые задачи и шедулеры в Python
Что проверяют
Различаете ли вы три разные задачи: сделать работу вне запроса, выполнять её по расписанию и гарантировать, что она не потеряется при падении процесса. Следом спрашивают про повторы, идемпотентность и что будет с расписанием при нескольких репликах.
Как отвечать
Инструмент выбирается по требованиям к надёжности:
- внутри процесса:
asyncio.create_task()для короткой работы в async-приложении,ThreadPoolExecutorдля блокирующей. Ссылку на задачу изcreate_taskнужно хранить: цикл событий держит только слабую ссылку, и задачу может собрать сборщик мусора. В FastAPI естьBackgroundTasks, они выполняются после отправки ответа. Всё это умирает вместе с процессом; - очереди задач: Celery с брокером Redis или RabbitMQ, лёгкие альтернативы RQ, Dramatiq, arq, Taskiq. Задача переживает рестарт веб-процесса, воркеры масштабируются отдельно;
- расписание: Celery beat, APScheduler внутри приложения или обычный cron либо Kubernetes CronJob, который запускает скрипт.
Для Celery назовите настройки надёжности: autoretry_for, retry_backoff, max_retries на задаче и acks_late=True, чтобы сообщение подтверждалось после выполнения, а не до. Для result.get() нужен настроенный result backend, одного брокера мало. Вызывать .get() внутри другой задачи нельзя, Celery по умолчанию бросит ошибку.
Про расписание скажите главное: если APScheduler запущен в каждом из четырёх воркеров Gunicorn, задача выполнится четыре раза. Нужен единственный планировщик, например отдельный процесс beat, или распределённая блокировка в Redis или базе.
Где спотыкаются
Используют BackgroundTasks или create_task для важной работы вроде письма об оплате. При деплое или падении процесса задача пропадает без следа.
Не делают задачи идемпотентными. С acks_late и повторами одна задача может выполниться дважды, и письмо уйдёт два раза, если перед отправкой не проверять, что оно уже отправлено.
Передают в задачу ORM-объект вместо идентификатора. Он сериализуется со старым состоянием, а к моменту выполнения данные в базе уже другие.