Кратко

  • Сетевой сбой 9 октября и отказ пулов базы данных 10 октября были разными инцидентами, но оба нарушили выполнение задач через общие зависимости.
  • Некоторые запуски, завершившиеся внутренней ошибкой, остались в Running; обработчики отказов и ожидавшие родительские функции не получили уведомление. Клиентам пришлось отменять их или запускать заново.

Ценность длительного выполнения в том, что процесс может ждать и продолжиться после сбоя, сохранив завершённые шаги. Но контрольные точки сами по себе не отвечают на рабочие вопросы: где хранится состояние запуска? Какие очереди используют общий тракт? Какой статус записывается при отказе внутренней зависимости? Кто узнаёт, что запуск сам уже не продолжится? В отчёте Inngest о событиях 9–11 октября эти границы видны на конкретных примерах.

9 октября в 21:05 UTC один из провайдеров потерял связь между своим центром обработки данных и остальными площадками Inngest, а также исходящий доступ в Интернет. Предварительное объяснение провайдера — ошибка при изменении конфигурации DDoS-защиты. Inngest сообщила, что полного отчёта пока не получила, поэтому причина оставалась предварительной. Связь восстановилась в 21:49. На следующий день два пула PgBouncer перед основной базой потеряли все подключения в 14:00 UTC и снова в 15:30. Планирование запусков и обработка очередей остановились для большинства аккаунтов.

Мера восстановления вернула обработку, но добавила периодические задержки, особенно для cron-функций, пока планировщик не вернули к исходным пулам в 04:17 UTC 11 октября. Маршрутизацию шлюзов улучшили 12 октября. Механизмы этих инцидентов и пути восстановления различались.

Первый инцидент показал, как локальная граница сети может расширить последствия. По словам Inngest, сервис работает в трёх центрах обработки данных: аккаунт может обрабатываться в одном центре, а его состояние храниться в другом; центральная база аккаунтов и конфигураций и пулы подключений находятся у ещё одного провайдера. После разрыва межцентрового соединения некоторые рабочие процессы не могли получить данные аккаунта или сохранить состояние запуска. Задачи, запущенные в других центрах, тоже не продвигались, если нужное им состояние находилось на изолированной площадке.

Ошибочная работа накапливалась в общей шине обработки событий и задерживала аккаунты без прямой зависимости от этого центра. Аккаунты с отдельными шинами событий не пострадали. Общий тракт обработки расширил радиус сбоя.

На следующий день отказ развивался иначе. Inngest связала схлопывание пулов с взаимодействием коротких сроков запросов, отмены со стороны клиента и PgBouncer. Некоторые запросы ещё ждали подключения, когда истекал их срок. Драйвер закрывал клиентское соединение; позже PgBouncer выполнял оставленный запрос, но закрывал соединение с базой вместо возврата в пул. Новые подключения устанавливались по одному, поэтому уничтожались быстрее, чем создавались. Сбои совпали с началом и концом часа, когда cron-нагрузка примерно утраивала число запросов, и с недавно развёрнутыми запросами, которые массово начали завершаться ошибкой перед выходными.

Перезапуск пулов не давал надёжного восстановления, а повышение лимита не ускоряло создание подключений.

Временный обход добавил новый сетевой путь. Перенос сервисов на пулы провайдера базы рядом с самой базой восстановил обработку между 16:10 и 16:25 UTC. Но этот маршрут проходил через NAT-шлюзы, которые молча отбрасывали некоторые соединения. Запрос мог зависнуть до 16 минут, прежде чем завершиться ошибкой; особенно заметно это было для cron-функций. В 04:17 UTC 11 октября Inngest вернула расписание cron к прежним пулам, а 12 октября улучшила маршрутизацию шлюзов. Обходная мера — тоже изменение топологии: она снимает одну точку задержки, но может создать другую, менее заметную границу ожидания.

Более важный пробел оказался в переходе состояния запуска. Inngest сообщила, что внутренние ошибки расходовали настроенные повторные попытки. Когда шаг окончательно завершался внутренней ошибкой, некоторые запуски оставались в Running, вместо перехода в Failed. Поэтому onFailure не срабатывал, а ожидавшая результат родительская функция через step.invoke не получала отказ. По отчёту, эти запуски сами не продолжались; клиентам требовалось отменить их или выполнить повторно. Восстановившийся пул рабочих процессов ещё не означает, что каждый прерванный бизнес-процесс достиг конечного состояния.

Это отличается от обычного возобновления. Документация Inngest описывает повторное использование результатов завершённых шагов при возобновлении, причём каждый шаг повторяется отдельно. Но описанный в отчёте клиентский replay начинает запуск с самого начала. Поэтому завершённые шаги могут выполниться снова. Данных о фактическом дублировании платежа, сообщения или записи в базе нет. Однако перед повторным запуском оператору нужно изучить trace и определить, безопасно ли повторить внешние эффекты. Внешняя система может принять операцию до того, как оркестратор сохранит результат; следующая попытка способна повторить вызов.

Руководство Inngest по идемпотентности рекомендует стабильные ключи для таких операций. Контрольные точки сохраняют внутренний прогресс, но сами по себе не обеспечивают ровно один внешний эффект.

Inngest сообщила о выпущенных исправлениях: запросы, ожидающие подключения к пулу, больше не отменяются вместе с клиентом; постоянная внутренняя ошибка переводит запуск в отказ; если состояние временно нельзя прочитать, работа остаётся в очереди и продолжается, когда состояние снова доступно. Компания также улучшила маршрутизацию шлюзов и обновление страницы статуса. Более крупное изменение — консолидация в одном центре обработки данных — ещё выполнялось. Отчёт не раскрывает конечную топологию и независимую схему аварийного восстановления.

Источники