Перейти к основному содержимому

Фоновые задачи и расписания

Всё, что сервер делает, когда его никто не ждёт: ночные отчёты, повторные попытки, напоминания, уборка мусора — и вообще всё, что слишком медленно, чтобы держать ради этого открытый запрос. Сложность не в том, чтобы поставить работу в расписание, а в том, что произойдёт, когда она упадёт, выполнится дважды или процесс умрёт на середине.

Почему это важно. Фоновая работа по определению ломается молча. В момент поломки никто не смотрит на экран, поэтому задача, которая падает три недели подряд, выглядит ровно так же, как задача, которая работает.

Что нужно понимать

  • Должно ли это произойти сейчас, скоро или когда-нибудь
  • Что будет, если работа выполнится дважды, и допустимо ли это
  • Что будет, если она не выполнится вообще, и кто об этом узнает
  • С какого места работа продолжится, если процесс умер на середине
  • Что происходит, когда два экземпляра одновременно решают, что настала их очередь

Ключевые темы

Виды работы

  • Отложенная: начата запросом, завершается уже после ответа
  • По расписанию: в заданное время или с заданным интервалом
  • Реактивная: запускается изменением где-то ещё
  • Долгая: пакеты, которые не успевают закончиться за один проход

Корректность

  • Идемпотентность: повтор не должен давать второй эффект
  • Exactly-once как выдумка и проектирование в расчёте на at-least-once
  • Блокировки, чтобы задачу по расписанию выполнял только один экземпляр
  • Границы транзакций и почему нельзя фиксировать её раньше побочного эффекта

Отказы

  • Повторы с нарастающей задержкой и с ограничением их числа
  • Dead letters — работа, упавшая слишком много раз, которой нужен человек
  • Частичный сбой внутри пакета: продолжать или останавливаться
  • Таймауты и способ отличить зависшую задачу от упавшей

Эксплуатация

  • Наблюдаемость: что выполнялось, когда, сколько длилось и что сделало
  • Алерты не только на упавшую задачу, но и на ту, которая не запустилась
  • Накопившееся отставание (backlog) как метрика
  • Часовые пояса и перевод часов, которые ломают расписания каждый год

Уровни

УровеньКак это выглядит
JuniorСтавит задачу в расписание и пишет то, что она делает.
MiddleДелает задачи идемпотентными, добавляет повторы и логирование, обрабатывает частичный сбой.
SeniorПроектирует в расчёте на at-least-once и несколько экземпляров: блокировки, dead letters, алерты и на ошибку, и на молчание.

Практика

Для начала

  • Вынесите работу из запроса Возьмите что-нибудь медленное в обработчике запроса и отложите — так, чтобы пользователь при этом оставался в курсе.

  • Записывайте запуски Дайте задаче журнал того, что она сделала, чтобы вчерашний запуск можно было разобрать.

  • Сделайте её идемпотентной Возьмите задачу, которая при двойном запуске спишет деньги или отправит письмо дважды, и почините это.

Глубже

  • Заблокируйте расписание Добейтесь, чтобы задача по расписанию выполнилась один раз, когда запущено два экземпляра.

  • Обработайте «отравленный» элемент Добавьте повторы с нарастающей задержкой и путь в dead letter для элемента, который падает всегда.

  • Поднимайте тревогу на тишину Сделайте так, чтобы алерт вызывала и задача, которая перестала запускаться, а не только та, которая упала с ошибкой.

Проверьте себя

  • Какая из ваших задач сломается, если два её экземпляра стартуют в один момент?
  • Как вы узнаете, что ночная задача перестала запускаться три недели назад?
  • Что произойдёт, если процесс убьют на середине задачи?
  • Куда попадает элемент после десятого неудачного повтора?
  • Какие из ваших расписаний сломаются при переводе часов?
  • Насколько велико отставание прямо сейчас и видите ли вы его вообще?

Материалы

  • Serverpod: scheduling — отложенные вызовы и работа по расписанию в Dart-бэкенде, включая то, что переживает перезапуск.
  • Idempotency keys — образцовый разбор того, как сделать повторную работу безопасной. Фоновые задачи — тот случай, где это важнее всего.
  • Timeouts, retries and backoff with jitter — почему наивные повторы превращают мелкий сбой в полноценную аварию; написано людьми, которые это наблюдали.
  • Avoiding insurmountable queue backlogs — что происходит, когда работа приходит быстрее, чем обрабатывается, и как проектировать так, чтобы очередь восстанавливалась.
  • Google SRE Book: Monitoring Distributed Systems — глава о том, на что вообще стоит ставить алерты. Прямо применима к отказу вида «тихо перестало работать».