Фоновые задачи не выполняются? Быстро исправляем очереди, cron и воркеры
Если фоновые задачи не выполняются, это ломает письма, биллинг и синхронизации. Узнайте, как понять, виноват ли cron, очередь, воркеры, повторы или идемпотентность, и быстро устранить проблему.

Как это выглядит, когда фоновые задачи перестают работать
Когда фоновые задачи не выполняются, приложение на первый взгляд может выглядеть нормально. Страницы открываются, пользователи кликают, формы отправляются. Но «после» работа не происходит.
Пользователи замечают отсутствующие побочные эффекты: приветственное письмо не приходит, импорт CSV зависает в статусе “processing”, вебхук об оплате получен, но заказ не обновился, или ночная синхронизация перестала менять цифры. Тикеты в поддержку часто выглядят расплывчато («всё застряло»), потому что ничего явно не падает.
Это особенно типично для прототипов, созданных с помощью ИИ. Локально они часто работают, потому что одна машина делает всё: обслуживает веб, запускает планировщик и воркеры. В продакшне эти части разделяются между сервисами, контейнерами или машинами. Отсутствие одной детали ломает цепочку.
Большинство проблем укладываются в четыре категории:
- Планирование: cron/таймеры не срабатывают, смещение по часовой зоне, или расписание работает в неправильной среде.
- Деплой воркера: воркер не запущен, не может достучаться до очереди или смотрит на неправильную очередь.
- Повторы: задачи падают и бесконечно повторяются, или падают один раз и исчезают без оповещений.
- Идемпотентность: задача выполняется дважды и портит состояние, поэтому она отключается или постоянно падает.
Прежде чем лезть в код, соберите несколько фактов. Это сэкономит часы на угадываниях и покажет, где именно ломается пайплайн:
- Метка времени, когда произошло действие пользователя (или когда должно было сработать расписание)
- Имя/тип задачи и любой job ID из логов
- Логи воркеров за это время (не только веб‑логи)
- Метрики очереди: ожидающие, в работе, упавшие
- Время последнего успешного запуска той же задачи
Если вы унаследовали прототип от инструментов вроде Replit, Cursor или Bolt, этот «пакет доказательств» — то, что использует команда FixMyMess, чтобы понять: отсутствует ли планировщик, мёртв воркер или задача тихо падает.
Cron, очереди и воркеры: простая модель в голове
Думайте о фоновой работе как о доставке. Что‑то решает, когда выполнить работу, что‑то хранит задачу, и что‑то её выполняет.
Cron (или планировщик) — часы. Он триггерит задачу по расписанию, например «каждые 5 минут» или «каждый понедельник в 9:00». Очередь — почтовый ящик. Она хранит задачи, пока воркер не будет готов. Воркер — почтальон. Он тянет задачи из очереди и запускает код.
Одноразовые задачи и периодические стартуют по‑разному. Одноразовая обычно создаётся приложением (например, «изменить размер изображения после загрузки»). Периодическая создаётся планировщиком (например, «отправить еженедельный отчёт»). Если вы пользуетесь очередью, обе попадают в одно место: запись задачи, ожидающая обработки.
В продакшне состояние должно храниться в общем и надёжном месте: таблица в базе для запланированных запусков, Redis для сообщений очереди или управляемый сервис очереди. Если прототип полагается на in‑memory состояние, он работает локально, но ломается при увеличении числа серверов или после перезапуска.
Локальная разработка скрывает проблемы, потому что всё выполняется в одном процессе. В проде они раздельны. Если cron не установлен, воркер не задеплоен или воркер не может достучаться до Redis или базы, вы получите классический симптом: фоновые задачи не выполняются, хотя приложение выглядит «здоровым».
Быстрая триажа: где ломается пайплайн?
Не догадывайтесь. Понять, какая часть пайплайна не работает: задачи не создаются, не подхватываются или выполняются и падают?
1) Задачи вообще создаются?
Начните с простого разделения: «нет записи задачи» vs «запись есть, но ничего не происходит». Ищите запись задачи в хранилище очереди/таблице или любую строку лога, подтверждающую постановку в очередь.
Если ничего нет, чаще всего проблема в планировании. Cron/таймеры могут не запускаться в проде, запускаться в неправильной среде или не иметь нужных прав. Типичная ошибка прототипа — полагаться на локальный планировщик (ваш ноутбук), не развернув реальный процесс планировщика.
2) Задачи созданы, но не обрабатываются?
Если задачи есть, но остаются в статусе «queued», скорее всего воркер не обрабатывает задачи. Подтвердите, что воркер действительно запущен в продакшне (а не только при деплое) и слушает ту же очередь, в которую публикует приложение.
Также проверьте подключение и креденшалы. Воркерам нужен доступ к бекенду очереди (Redis/SQS/БД) и те же env vars, что и у приложения. Одна несоответствующая переменная может сделать воркер «здоровым» на вид, но он не увидит задачи.
3) Задачи выполняются, но падают и повторяются бесконечно?
Если вы видите растущее число попыток, планирование, вероятно, в порядке. Значит — исполнение. Откройте последний ошибочный лог и найдите повторяющуюся причину (ошибки аутентификации, отсутствующие секреты, таймауты). Повторы должны иметь backoff и максимальное число попыток.
Если задача работает с деньгами, письмами или данными пользователей, предполагаем, что её могут выполнить дважды. Отсутствие идемпотентности — как раз путь от «задача падает» к «задача падает громко», с дублированными списаниями или письмами.
Исправление проблем с планированием (cron и таймеры)
Сначала проверьте базовые вещи: действительно ли что‑то триггерит задачу и в ожидаемое ли время?
Подтвердите, что расписание такое, как вы думаете
Cron‑выражения могут выглядеть верно и быть неверными. Часовые пояса — самая частая ловушка. Ваш ноутбук может быть в локальном времени, а прод — в UTC. Это превращает «каждый день в 9:00» в «каждый день в 1:00» или просто не попадает в окно ваших тестов.
Также проверьте, как платформа интерпретирует расписание. Некоторые системы используют 5‑поле cron, другие — 6 (с секундами). Дополнительное поле сдвигает всё.
Быстрые проверки, которые ловят большинство ошибок:
- Залогируйте следующие 5 времён запуска из планировщика и подтвердите, что они соответствуют ожиданию.
- Проверьте настройку часового пояса в конфиге приложения и в хостинге.
- Валидируйте cron‑строку против той библиотеки, которую вы реально используете.
- Убедитесь, что задача включена в проде (фич-флаги и env vars часто отличаются).
- Подтвердите, что активен только один экземпляр планировщика (иначе получите дубли).
Сделайте планировщик безопасным в проде
Планировщик, запущенный на ноутбуке, не будет автоматически работать после деплоя. В прототипах часто ставят планировщик внутри веб‑процесса. Тогда он останавливается при рестарте dyno, при скейлинге до нуля или во время сна.
Пропущенные запуски — тихая проблема. Если приложение перезапускается в 9:01, задание на 9:00 не выполнится, если вы это не учли. Для важных задач подумайте о catch‑up подходе: при старте проверять, что должно было выполниться, и ставить это в очередь.
Наконец, обработайте перекрытия. Если задача может выполняться дольше интервала, нужна блокировка или правило «не пересекаться». Иначе увидите двойные отправки, дублирование биллинга или конкурентные обновления.
Исправление деплоя воркеров и проблем с подключением
Если расписание в порядке, но задачи не обрабатываются, смотрите на воркер. Очередь двигается только тогда, когда воркер онлайн, подключён и способен постоянно работать.
Убедитесь, что в той же среде, где развернуто приложение, есть процесс воркера (а не только на ноутбуке). Хотите доказательства, что он стартует, держится и смотрит на ту же очередь. Проверьте список процессов или дашборд сервиса, затем логи на наличие строки при старте вроде "listening" или "connected".
Частые проблемы при деплое в прототипах, сгенерированных ИИ:
- Воркера никогда не задеплоили (только веб‑приложение).
- Команда запуска воркера неправильная (запускает dev‑скрипт или сразу выходит).
- Переменные окружения отсутствуют у воркера (URL очереди, креденшалы, NODE_ENV).
- Воркер смотрит на другую очередь/регион, чем веб.
- Воркер падает и перезапускается в цикле (из‑за лимитов памяти или отсутствующих зависимостей).
Проблемы с подключением выглядят похоже: задачи накапливаются и никто их не потребляет. Частые причины — неверный хост, заблокированные порты, истёкшие креденшалы или сервис очереди доступен только внутри приватной сети, где воркер не находится.
Также важна конкуренция. Слишком мало воркеров — медленная обработка; слишком много — таймауты, ограничение по скорости или OOM. Простой старт — 1–2 воркера с небольшой конкуренцией, увеличивать только при стабильной памяти и предсказуемом времени задач.
Пример: стартап деплоит задачу «отправить счет по почте». Веб ставит в очередь нормально, но воркер запускается без креденшалов очереди в проде и сразу выходит. Исправление окружения воркера (и явная лог‑строка «connected to queue») делает проблему очевидной в следующий раз.
Повторы, backoff и обработка ошибок, которая не выходит из‑под контроля
Иногда жалоба «фоновые задачи не работают» означает: задача запускается, падает мгновенно и столь быстро повторяется, что не продвигается вперёд. Хорошие правила повторов сохраняют систему в спокойствии и логи читаемыми.
Разделяйте ошибки на две категории:
- Транзиентные ошибки обычно проходят: таймауты, rate limits, временные блокировки базы, краткие сетевые сбои. Их стоит повторять.
- Постоянные ошибки не улучшатся со временем: отсутствующие данные, неверный API‑ключ, «пользователь не найден», ошибки валидации или баги, которые всегда кидают на одной строке. Их нужно фейлить быстро и отправлять в «dead» состояние для просмотра.
Backoff предотвращает превращение небольшой проблемы в шторм. Разумное значение по умолчанию — небольшое число попыток (например, 3–10) с увеличивающимися задержками. Добавьте рандомный джиттер, чтобы тысяча задач не повторялась в одну секунду.
Фиксируйте базовые данные при каждом падении, чтобы воспроизвести позже:
- Имя задачи, уникальный job ID и номер попытки
- Точные входные данные (или безопасное резюме) и ID релевантных записей
- Сообщение об ошибке и stack trace, плюс код ответа от внешнего сервиса, если был вызов
- Время выполнения задачи и время до следующей попытки
Оповещения могут быть простыми. Если глубина очереди растёт 10 минут или возраст самой старой задачи пересёк порог, оповестите дежурного или хотя бы отправьте сообщение в общий канал. Это ловит «тихие» падения, когда воркер жив, но каждая задача встала.
Пример: еженедельная почтовая задача попала под rate limit у провайдера в понедельник утром. Без backoff она тут же повторяется, исчерпывает попытки и письма теряются. С backoff и понятным логом «rate limited» задача ждёт, восстанавливается, и вы можете подтвердить исправление.
Идемпотентность: сделать задачи безопасными при повторном запуске
Когда задачи снова начнут выполняться (или повторы начнут срабатывать), часто появляется новая проблема: одна и та же задача срабатывает дважды. Если задача не идемпотентна, «дважды» означает двойное снятие денег, дублирование писем или создание повторных записей.
Идемпотентность — это способность безопасно выполнить ту же логическую операцию несколько раз, получив один корректный результат. Представьте задачу «списать с карты и отправить чек». Сбой сети может случиться после успешной оплаты, но до сохранения результата. Очередь повторит, и клиент спишется снова.
Где ключи идемпотентности особенно помогают
Для всего, что общается с внешним миром (платежи, сервисы отправки почты, SMS, вебхуки), добавляйте idempotency‑ключ, который остаётся одинаковым для логического действия, а не для попытки. Хороший ключ привязан к стабильному бизнес‑ID, например invoice_1234 или order_987.
Также защитите базу от дубликатов. Самая простая страховка — уникальное ограничение, например «только один чек на счёт‑фактуру». Тогда задача может пытаться вставить запись и считать «уже есть» успехом.
Практичные шаблоны, которые хорошо работают при переходе от прототипа к проду:
- Сначала запишите состояние задачи (например:
payment_pending), затем выполните внешний вызов, затем пометьтеpayment_completed. - Храните «processed key» (job ID или бизнес‑ID) и проверяйте его перед побочными эффектами.
- Используйте уникальные ограничения для одноразовых вещей (чеки, письма, подписки).
- Возвращайте success, если результат уже существует.
Если вы унаследовали ИИ‑созданный прототип, этого часто не хватает: задача делает побочный эффект сначала, а потом пытается сохранить. Исправление порядка уменьшит большинство двойных списаний и дублирующих писем.
Пошаговый отлаживающий рабочий процесс
Перестаньте догадываться и тестируйте по одному звену пайплайна. Цель — превратить расплывчатый симптом (письма не отправляются, отчёты не генерируются) в ясную точку отказа, которую можно исправить.
Возьмите одну маленькую тестовую задачу с известным payload и держите всё остальное как в проде:
- Доказать, что планировщик триггерит: запустите планировщик вручную один раз (или временно поставьте cron каждую минуту) и подтвердите попытку поставить задачу в очередь.
- Доказать, что задача создана: проверьте очередь, таблицу задач или брокера на новую запись с вашим payload и меткой времени.
- Доказать, что воркер её видит: запустите воркер в foreground и наблюдайте, как он берёт задачу.
- Прочитать реальную ошибку: найдите первую причину падения (отсутствующая переменная окружения, неверный URL базы, заблокенная отправка почты, права).
- Запустить ту же задачу снова: подтвердите, что она завершается, и что при повторном запуске изменения не применяются дважды.
Когда вы это сделаете, вы будете знать, какой слой сломался: планирование, постановка в очередь, деплой/подключение воркера или логика самой задачи.
После того как один раз заработало, добавьте защиты
Задача, которая выполняется только когда вы рядом, всё ещё не исправлена. Добавьте несколько защит, чтобы ошибки не накапливались молча:
- Таймауты и лимиты (чтобы задачи не висели вечно)
- Чёткие правила повторов с backoff (чтобы не было бесконечных циклов)
- Dead‑letter очередь (чтобы проблемные задачи не блокировали нормальные)
- Идемпотентность (чтобы retry не дублировал списания/письма)
- Логи с job ID и ключевыми входами
Частые ловушки, из‑за которых задачи никогда не завершаются
Часто причина — не в системе очередей сама по себе, а в маленьком несоответствии между тем, где запускается планировщик, где запускается воркер и что нужно задаче для успешного выполнения.
Одна обычная ошибка — случайно разделить пайплайн. Например, cron запускается на веб‑сервере, а воркер задеплоен на другой машине или контейнере, который в проде не запущен. Планировщик ставит задачи, но некому их забирать.
Другие повторяющиеся ловушки:
- Использование in‑memory очереди (или dev‑адаптера), так что задачи исчезают при рестарте или при scale‑out.
- Поймать ошибку и вернуть «успех» — задача выглядит выполненной, но ничего не сделала.
- Отсутствие видимости ошибок, поэтому сломанные задачи копятся молча днями.
- Секреты и эндпойнты захардкожены и отличаются между локалом, стейджингом и продом.
- Задачи зависят от локальных файлов или временной папки, которой нет у воркера.
Небольшой пример: задача отправляет счета и делает внешний API‑вызов. Локально всё работает: API‑ключ в .env. В проде контейнер воркера не имеет этой переменной, код ловит исключение и помечает задачу как выполненную. Вы заметите проблему, когда клиенты начнут жаловаться.
Если вы подозреваете одну из этих проблем, ответьте на два вопроса: где выполняется задача (какой сервис) и где вы видите её логи? Если вы не можете найти и то, и другое, сбои могут прятаться долго.
Быстрый чеклист перед очередным релизом
Перед пушем убедитесь во всём пайплайне. Большинство отчётов «фоновые задачи не работают» — это не одна баг‑фича, а набор небольших несоответствий между планированием, постановкой и настройкой воркера.
Начните с планировщика. Во многих прототипах процесс cron никогда не запускается в проде или выполняется в контейнере, который масштабируется до нуля. Убедитесь, что он запущен прямо сейчас (а не просто «сконфигурирован») и что у него те же переменные окружения, что и у приложения.
Далее подтвердите, что задачи реально создаются. Проверьте метку времени задачи (run at / scheduled for) — соответствует ли она часовому поясу продакшна, и есть ли в payload реальные ID (не пустые строки, тестовые emails или null user IDs). Если задачи создаются, но запланированы далеко в будущем, они будут выглядеть «застрявшими», хотя ничего не сломано.
Затем проверьте воркер. Убедитесь, что хотя бы один процесс воркера непрерывно работает и слушает точно то имя очереди, которое использует приложение. Одна опечатка или несовпадающий default queue создаёт молчаливые накопления.
Наконец, сделайте ошибки видимыми и безопасными для повтора:
- Логируйте ошибки с контекстом (job name, ID, ключевые поля)
- Ограничьте повторы и используйте backoff, чтобы ошибки не зацикливались
- Сделайте задачи идемпотентными, чтобы повторы не дублировали списания или письма
Пример: еженедельная почта, которая работала локально, но не в проде
Типичный случай «работало на моём ноутбуке»: прототип отправляет еженедельный отчёт по почте каждый понедельник в 9:00. После деплоя ничего не приходит. Дашборд в порядке, пользователи зашли, явных ошибок нет.
Первое, что проверить: реально ли расписание в проде? Локально dev‑сервер мог автоматически запускать планировщик (или вы один раз его запустили). В проде запись cron есть в конфиге, но нет живого процесса планировщика. Код верный, но никто не триггерит очередь.
После деплоя планировщика задачи начали появляться. Это обнаружило вторую проблему: воркер постоянно повторял ту же задачу, потому что в проде не был задан env var с API‑ключом почтового провайдера. Каждая попытка падала, повторялась и создавала бэклог. Очередь выглядела «застрявшей», хотя воркер был занят падениями.
Что исправляет это надолго:
- Добавить health‑лог при постановке задач планировщиком (чтобы можно было доказать, что триггеры происходят).
- На старте воркера падать громко при отсутствии критичных env vars (чтобы он не уходил в тихие повторы).
- Ограничить повторы с backoff (например, 5 попыток в течение 30 минут), затем переводить задачу в dead‑letter для ручного разбора.
- Сделать почтовую задачу идемпотентной: сохранять уникальный ключ вроде
report:teamId:weekStartDateдо отправки и пропускать, если он уже есть.
С такими изменениями вы сможете перезапустить воркеры, задеплоить или восстановиться после краша, не отправляя дубликаты и не создавая вечных циклов повторов.
Следующие шаги: укрепить пайплайн и держать его в стабильном состоянии
Когда задачи снова работают, цель — не дать той же ошибке вернуться на следующей неделе. Рассматривайте фоновые задачи как продакшн‑риск, а не как разовую багу.
Оформите короткий ранбуk, чтобы любой мог пройти шаги:
- Где определены расписания (cron config, platform scheduler, таймеры в приложении)
- Как запускаются воркеры (имя процесса, команда, требуемые env vars)
- Как выглядит «здоровье» (глубина очереди, время последнего успешного запуска)
- Топовые причины падений, которые вы видели (таймауты, auth, плохие payloads)
- Безопасный способ проиграть задачи и как избежать двойных отправок
Добавьте минимальный мониторинг, который отвечает на два вопроса: накапливаются ли задачи и падают ли они? Даже базовые алерты по росту глубины очереди и ежедневный отчёт по числу упавших задач ловят большинство проблем на ранней стадии.
Если ваш прототип смешивает код запросов и логику задач, запланируйте небольшой рефактор. Вынесите основную работу задачи в отдельную функцию, которую может вызывать воркер, а веб‑запрос пусть только ставит в очередь и валидирует вход. Это делает повторы безопаснее и убирает скрытые зависимости от состояния запроса.
Если вы унаследовали приложение, сгенерированное ИИ, и нужен быстрый внешний взгляд, FixMyMess (fixmymess.ai) специализируется на диагностике и ремонте таких прототипов, чтобы очереди, воркеры, повторы и поведение при деплое совпадали с тем, что вы видите локально — с человеческой проверкой перед релизом.
Часто задаваемые вопросы
How do I know it’s a background job problem and not a normal app bug?
Если страницы открываются, но «последующие» действия не происходят (нет писем, импорты висят, вебхуки получены, но обновлений нет), предположите проблему с фоновыми задачами. Проверьте, была ли задача поставлена в очередь и пытался ли воркер её выполнить около времени действия пользователя.
Why do background jobs work locally but not in production?
Потому что в локальной разработке часто всё запускается в одном процессе: веб-сервер, планировщик и воркер. В продакшне это разделено на отдельные процессы или сервисы, и отсутствие любой части (не запущен планировщик, не развернут воркер, неверное подключение к очереди) ломает цепочку, хотя веб-приложение выглядит нормально.
What’s the fastest way to triage where the pipeline is breaking?
Решите, на каком этапе проблема: создание задачи, захват задач воркером или выполнение. Сначала найдите лог постановки в очередь или запись задачи; если её нет — скорее всего планировщик. Если задачи есть, но они остаются в состоянии queued — это обычно воркер или подключение. Если увеличивается число попыток, значит задача выполняется и падает — смотрите ошибки и правила повторов.
What are the most common scheduling (cron) mistakes?
Планировщик ломается, когда задача не создаётся в ожидаемое время. Частые причины: планировщик не запущен в проде, разница в часовом поясе (локальный vs UTC), неправильная среда исполнения, или несоответствие формата cron (5-польный vs 6-польный). Быстрая проверка — залогируйте ближайшие запускы и подтвердите ожидание.
What’s the difference between the queue and the worker, and what breaks most often?
Очередь — место, где задачи ждут; воркер — процесс, который берёт задачи и выполняет их. Если задачи накапливаются как “queued”, обычно воркер не запущен, смотрит на другую очередь или не может достучаться до бекенда очереди из‑за неверных учётных данных или сети.
Why does my worker look “healthy” but still never processes jobs?
Потому что у веб-приложения и воркера часто разные наборы переменных окружения. Приложение может ставить задачи в очередь, а воркер не видит URL очереди, базу данных или API-ключ — он стартует, но не обрабатывает задачи. Самое простое исправление — убедиться, что у воркера те же критичные env vars и что при старте он логирует понятное сообщение вроде "connected to queue".
If retries keep happening, does that mean scheduling is fine?
Не обязательно; это может означать, что задача падает мгновенно и сразу повторяется. Проверьте, увеличивается ли номер попытки и повторяется ли одна и та же ошибка. Добавьте ограничение числа попыток и backoff, чтобы ошибки не уходили в бесконечный цикл, а постоянные ошибки переводились в состояние для ручного рассмотрения.
How do I prevent duplicate emails or double charges when jobs retry?
Идемпотентность означает, что задача может выполниться дважды, но результат будет корректным и не продублируется. Используйте стабильные idempotency-ключи для внешних действий (платежи, письма, SMS) и защитите базу уникальными ограничениями, чтобы «уже выполнено» считалось успехом. Это предотвращает двойные списания и повторные письма при повторах.
What information should I collect before changing code?
Зафиксируйте время действия пользователя (или ожидаемое время срабатывания), имя/тип задачи и её ID, логи воркера за это окно, глубину очереди и число ошибок, а также время последнего успешного запуска той же задачи. Этот «пакет доказательств» быстро покажет, не создаются ли задачи, не потребляются ли они или падают при выполнении.
When should I bring in FixMyMess instead of debugging longer?
Если вы унаследовали прототип, сгенерированный ИИ, и задачи застряли, тихо падают или дублируют побочные эффекты, это чаще проблема деплоя и пайплайна, а не одна мелкая ошибка. FixMyMess может сделать бесплатный аудит кода, чтобы точно сказать, отсутствует ли планировщик, мёртв воркер или проблемы с секретами и правилами повторов, и затем быстро починить и укрепить настройку с ручной верификацией.