Inngest лёг дважды за двое суток: сбой дата-центра и пулер БД, который рвал соединения быстрее, чем их строил
Второй сбой Inngest случился по собственной вине: два обвала суммарно заняли около 1 ч 43 мин, а перезапуски и больший пул не помогли — помог переезд на пулеры рядом с базой.
Inngest — сервис, который запускает фоновые функции и расписания (cron) для чужих приложений: если он стоит, у клиентов не уходят письма, не пересчитываются заказы и не выполняются задачи по таймеру. 9 и 10 октября 2026 года он стоял дважды. Подробности компания опубликовала в отчёте об инциденте от 15 октября; все времена ниже — UTC, как в отчёте.
Первый сбой случился по вине арендованного дата-центра, второй — из-за связки драйвера БД и пулера соединений. Второй интереснее: он показывает, как система может сама себя добивать под нагрузкой.
9 октября: дата-центр потерял сеть, а упали даже те, кто в нём не жил
Inngest работает в трёх дата-центрах, и выполнение функций зависит от всех трёх. В 21:05 один из них потерял связь с остальными и с интернетом. Предварительное объяснение его провайдера — ошибка при изменении конфигурации защиты от DDoS; полный отчёт провайдера ещё не опубликован, а его имя Inngest не называет.
Связь вернулась в 21:49, то есть простой составил 44 минуты. Досталось не только тем, чьи данные лежали в пострадавшем дата-центре: аккаунты без всякой привязки к нему делили с остальными общий конвейер обработки событий, и накопившийся backlog (очередь необработанного) задел почти всех. Аккаунты с выделенными конвейерами не пострадали.
10 октября: пулер соединений уничтожал соединения быстрее, чем создавал
Сервисы Inngest ходят в основную базу через пару PgBouncer в AWS. PgBouncer — прокси, который держит ограниченный набор соединений с базой и раздаёт их клиентам, чтобы база не тонула в тысячах подключений. Корневую причину компания описывает как давно описанное взаимодействие драйвера pgx и PgBouncer (issue #1780 в репозитории pgx). Цепочка такая:
- у частых служебных запросов короткие дедлайны (предельное время ожидания);
- если дедлайн истекал, пока запрос ещё стоял в очереди PgBouncer за соединением, драйвер отменял запрос и закрывал клиентское соединение;
- PgBouncer отменять было нечего — запрос не выполнялся, — поэтому позже он всё равно запускал брошенный запрос, а затем закрывал соединение с базой вместо возврата в пул;
- каждый пулер открывает новые соединения по одному, поэтому закрывались они быстрее, чем открывались;
- пулы опустели, клиенты встали в очередь, их дедлайны тоже истекали — круг замкнулся.
Команда воспроизвела поведение на тестовом стенде. Спусковыми крючками стали два фактора: оба обвала начались в начале и середине часа, когда запуски по расписанию примерно втрое увеличивают поток запросов, а новый набор служебных запросов, выкаченный на всех перед выходными, массово падал и добавлял отмены.
Перезапуски не спасли, помог переезд пулеров
Первый обвал начался в 14:00 и закончился к 14:48 (48 минут), второй — с 15:30 до 16:25 (55 минут). Пулеры перезапустили в 14:30–14:33, и это не восстанавливало их надёжно. Увеличение размера пула тоже не работало: узким местом был сам процесс открытия соединений, а не лимит.
Рабочим оказался переезд: с 16:09 до 16:28 все сервисы перевели на пулеры, которые провайдер базы данных держит рядом с самой базой. Однако у обходного пути была цена. Трафик пошёл через NAT-шлюзы (устройства, подменяющие адреса), и те молча теряли часть соединений; запросы на них висели до 16 минут. Из-за этого с 21:07 10 октября до 04:17 11 октября случались задержки, в основном у cron-функций. В 04:17 планирование вернули на прежние пулеры, а маршрутизацию через шлюзы 12 октября улучшили.
Отдельная неприятность: при внутренних сбоях шаги функций тратили настроенные повторные попытки, а прогон, упавший навсегда по внутренней причине, оставался в статусе Running вместо failed. Обработчики onFailure об этом не узнавали. Часть прогонов сама не продолжится: Inngest предлагает отменить и перезапустить их (шаги начнутся с начала, завершённые выполнятся повторно) или обратиться в поддержку.
- дедлайн запроса теперь только прекращает ожидание вызывающей стороны и не отменяет запрос в пуле; остальные вызовы БД и их таймауты команда пересматривает;
- постоянный внутренний сбой шага помечает прогон как упавший; если состояние прогона не прочитать, работа остаётся в очереди;
- статус-страница обновляется автоматически, как только начинается внутреннее расследование;
- в ближайшие недели Inngest сводит всё в один дата-центр как единую ячейку, чтобы убрать сам класс межцентровых сбоев.
Оценка простая: оба сбоя — следствие скрытой связности. В первом случае общий конвейер связал аккаунты, которые не зависели от упавшего дата-центра. Во втором короткий таймаут при очереди за ресурсом превратился в генератор нагрузки. Версии Inngest, PgBouncer и pgx, число затронутых аккаунтов и размеры пулов в отчёте не указаны.
Что проверить у себя
- Выяснить, что происходит с запросом, когда у клиента истёк таймаут: отменяется ли он на стороне пулера и базы или всё равно выполняется и отнимает соединение.
- Проверить, не зависит ли приложение от пары версий драйвера БД и пулера с известными проблемами, и прочитать issue-трекеры обоих.
- Не считать увеличение пула универсальным лекарством: сначала посмотреть, что ограничивает — лимит или скорость открытия соединений.
- Посмотреть на пики в начале и середине часа: расписания, которые стартуют разом, дают кратный всплеск запросов. Разнести их по времени.
- Проверить, что при внутренних сбоях задачи уходят в статус «ошибка» и о них узнают обработчики, а не висят вечно в «выполняется».
- Если в аварийный обход добавляется NAT или другой промежуточный узел, выставить таймауты на соединения: иначе молчаливая потеря пакетов превращается в 16-минутные зависания.
Разборы аварий и практику для админов публикуем в Telegram-канале «Полный Ops».