Фоновые задачи и расписания
Всё, что сервер делает, когда его никто не ждёт: ночные отчёты, повторные попытки, напоминания, уборка мусора — и вообще всё, что слишком медленно, чтобы держать ради этого открытый запрос. Сложность не в том, чтобы поставить работу в расписание, а в том, что произойдёт, когда она упадёт, выполнится дважды или процесс умрёт на середине.
Почему это важно. Фоновая работа по определению ломается молча. В момент поломки никто не смотрит на экран, поэтому задача, которая падает три недели подряд, выглядит ровно так же, как задача, которая работает.
Что нужно понимать
- Должно ли это произойти сейчас, скоро или когда-нибудь
- Что будет, если работа выполнится дважды, и допустимо ли это
- Что будет, если она не выполнится вообще, и кто об этом узнает
- С какого места работа продолжится, если процесс умер на середине
- Что происходит, когда два экземпляра одновременно решают, что настала их очередь
Ключевые темы
Виды работы
- Отложенная: начата запросом, завершается уже после ответа
- По расписанию: в заданное время или с заданным интервалом
- Реактивная: запускается изменением где-то ещё
- Долгая: пакеты, которые не успевают закончиться за один проход
Корректность
- Идемпотентность: повтор не должен давать второй эффект
- 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 — глава о том, на что вообще стоит ставить алерты. Прямо применима к отказу вида «тихо перестало работать».