Background jobs на собеседовании системного аналитика
support_tickets поле resolved_at равно NULL, если тикет ещё не решён. Нужно вывести сначала нерешённые тикеты, а внутри них — по created_at по возрастанию (самые старые сверху). Какой ORDER BY подойдёт лучше всего?Содержание:
Зачем нужны фоновые задачи
Долгие операции нельзя выполнять прямо в момент запроса пользователя — иначе он будет ждать ответа секунды или минуты, а то и получит таймаут. Решение — фоновая обработка (background jobs): система принимает запрос, быстро возвращает идентификатор задачи, а тяжёлую работу выполняет асинхронно. Пользователь не блокируется, а результат забирает позже или получает уведомление.
Что обычно уносят в фон:
- Отправка писем и уведомлений. Слать письмо синхронно в момент регистрации рискованно: почтовый сервис может тормозить или быть недоступен.
- Генерация отчётов. Тяжёлый отчёт может считаться минуты — держать HTTP-соединение всё это время нельзя.
- Обработка изображений и видео. Ресайз, конвертация, распознавание — всё это долго и ресурсоёмко.
- Массовые импорты. Загрузка файла на десятки тысяч строк не должна блокировать интерфейс.
- Регулярные чистки. Удаление устаревших данных, пересчёт агрегатов, синхронизации по расписанию.
Базовый сценарий один: пользователь отправил запрос → система быстро вернула ID → фоновый обработчик доделал работу. На собесе SA проверяют, понимаете ли вы, когда операцию нужно уносить в фон, и умеете ли спроектировать надёжную асинхронную обработку — с очередью, воркерами, повторами при сбоях и защитой от дублей.
Технологии очередей
Очередь — это буфер между тем, кто ставит задачи (producer), и тем, кто их выполняет (worker). Она сглаживает пики нагрузки и позволяет обрабатывать задачи в своём темпе. Основные варианты, которые стоит различать на собесе:
- RabbitMQ — зрелый брокер на протоколе AMQP с гибкой маршрутизацией сообщений (exchange, routing keys). Хорош, когда нужна нетривиальная логика доставки: разные типы задач в разные очереди, приоритеты.
- Redis — лёгкое in-memory хранилище, которое часто используют как основу очереди в связке с фреймворками Celery, Sidekiq или BullMQ. Быстрый и простой, но по умолчанию менее устойчив к потере данных, чем полноценный брокер.
- Kafka — распределённый лог событий. Формально это не очередь, а журнал, но его применяют для потоковой обработки задач с большим throughput. Отличается тем, что сообщения не удаляются после чтения и порядок гарантируется внутри партиции.
- SQS (AWS), Yandex Message Queue (российская альтернатива), Cloud Tasks (GCP), Azure Service Bus — управляемые облачные очереди. Их плюс в том, что не нужно самим поднимать и обслуживать брокер: отказоустойчивость и масштабирование берёт на себя провайдер.
На собесе полезно уметь объяснить выбор: для простой асинхронщины в продукте на Redis/RabbitMQ хватит готового фреймворка, для потоков событий с высокой нагрузкой — Kafka, а в облаке разумно взять управляемый сервис и не тащить свой брокер.
Паттерны воркеров
Воркер — процесс, который забирает задачи из очереди и выполняет их. Есть две базовые модели доставки задач воркеру.
Pull-модель. Воркер сам опрашивает очередь и забирает следующую задачу, когда готов. Так работает большинство фреймворков — воркер не перегрузится, потому что берёт ровно столько, сколько может обработать.
while True:
job = queue.pop() # забираем следующую задачу
if job:
process(job) # обрабатываемPush-модель. Брокер сам отправляет задачу воркеру, как только она появляется. Ниже задержка, но нужно следить, чтобы воркеру не навалили больше, чем он тянет (тут помогает backpressure).
На практике фоновую обработку редко пишут с нуля — берут готовые фреймворки: Sidekiq (Ruby), RQ и Celery (Python), BullMQ (Node.js). Они уже умеют очереди, повторы и мониторинг.
Конкурентность и масштабирование. Несколько воркеров обрабатывают задачи параллельно, а их число обычно масштабируют по глубине очереди: очередь растёт — добавляем воркеров, разгребли — убираем. Именно про этот механизм («как поймёте, что воркеров не хватает?») любят спрашивать на собесе — правильный ответ обычно про метрику глубины очереди и время ожидания задачи.
Стратегии повторов
Задачи падают: внешний сервис недоступен, сеть моргнула, база под нагрузкой. Поэтому упавшую задачу не выбрасывают, а повторяют — но не сразу и не бесконечно, а с нарастающей паузой (exponential backoff), чтобы не добить и без того лежащий сервис.
Попытка 1: сразу
Попытка 2: через 30 секунд
Попытка 3: через 5 минут
Попытка 4: через 1 час
Дальше — стоп и отправка в DLQИдемпотентность. Ключевое требование к повторам: задачу должно быть безопасно выполнить повторно. Если письмо уже отправлено, а воркер упал до отметки «готово», повтор не должен слать второе письмо. Обычно это решают ключом идемпотентности или проверкой «уже сделано?» перед действием — на собесе это почти обязательный вопрос при обсуждении повторов.
Ядовитые сообщения (poison messages). Иногда задача падает не из-за временного сбоя, а потому что она в принципе битая — и тогда бесконечные повторы зациклятся. Чтобы этого не было, после N неудачных попыток задачу отправляют в очередь недоставленных сообщений — DLQ (dead letter queue), где её можно разобрать вручную, не блокируя остальные.
Планирование задач
Часть задач нужно запускать не по событию, а по времени. Тут различают три сценария:
- По расписанию (cron-like). Запуск через фиксированные интервалы: каждую ночь, каждый час, каждые 5 минут.
- Отложенные (delayed). Один запуск через заданную задержку — например, напоминание через 30 дней или повторное письмо через сутки, если пользователь не подтвердил почту.
- Повторяющиеся (recurring). Регулярные операции: ежедневная чистка данных, ежечасная сборка отчётов, ночной пересчёт агрегатов.
Инструменты для этого:
- Celery Beat — планировщик для Celery, запускает задачи по расписанию.
- BullMQ scheduled jobs — встроенное планирование в экосистеме Node.js.
- Cron в Kubernetes (CronJob) — запуск контейнера с задачей по расписанию на уровне инфраструктуры.
- Airflow — для сложной оркестрации, когда задачи зависят друг от друга и образуют DAG (граф зависимостей).
На собесе стоит проговорить разницу: обычный cron хорош для независимых периодических задач, а Airflow берут, когда между задачами есть зависимости и нужен контроль порядка, повторов и наблюдаемости всего пайплайна.
Частые ошибки
- Не делать задачи идемпотентными. Без этого повтор упавшей задачи приведёт к дублям — второму письму, повторному списанию, задвоенному импорту.
- Забывать про DLQ. Без очереди недоставленных сообщений битая задача будет вечно циклиться в повторах и мешать обработке остальных.
- Повторять без backoff. Мгновенные бесконечные повторы добивают уже лежащий внешний сервис вместо того, чтобы дать ему восстановиться.
- Синхронно делать то, что должно быть в фоне. Отправка письма или генерация отчёта прямо в HTTP-запросе — источник таймаутов и падений под нагрузкой.
- Не мониторить глубину очереди. Если не следить за тем, сколько задач копится и как долго они ждут, можно узнать о нехватке воркеров только по жалобам пользователей.
Связанные темы
- Webhook design для SA
- Idempotency key для SA
- Notification system для SA
- Backpressure для SA
- Подготовка к собесу системного аналитика
FAQ
Когда операцию нужно уносить в фон?
Когда она долгая, ресурсоёмкая или зависит от внешнего сервиса, который может тормозить: отправка писем, генерация отчётов, обработка медиа, массовые импорты. Признак простой — если пользователю не обязательно ждать результат прямо сейчас, операцию стоит сделать асинхронной и вернуть ID задачи.
Что такое идемпотентность и зачем она в фоновых задачах?
Идемпотентность — свойство операции давать один и тот же результат при повторном выполнении. В фоновых задачах она критична, потому что задачу могут повторить после сбоя: без идемпотентности повтор приведёт к дублю (второе письмо, повторное списание). Обычно её обеспечивают ключом идемпотентности или проверкой «уже выполнено?» перед действием.
Что такое DLQ и когда туда попадает задача?
DLQ (dead letter queue) — очередь недоставленных сообщений. Туда отправляют задачу, которая не выполнилась после заданного числа попыток. Это защищает от ядовитых сообщений: битая задача не зацикливается в бесконечных повторах, а откладывается для ручного разбора, не блокируя остальную обработку.
Чем очередь отличается от Kafka?
Классическая очередь (RabbitMQ, SQS) удаляет сообщение после успешной обработки и хороша для распределения задач по воркерам. Kafka — это лог событий: сообщения хранятся заданное время, не удаляются после чтения, а порядок гарантируется внутри партиции. Kafka берут под большой поток событий и потоковую обработку, обычную очередь — под задачи с подтверждением и маршрутизацией.
Это официальная информация?
Нет. Статья основана на индустриальных практиках работы с фоновыми задачами и опыте кандидатов. Конкретные инструменты и глубина вопросов зависят от компании и команды.
Тренируйте системный анализ — откройте тренажёр с 1500+ вопросами для собесов.