Как поймать приложения, которые падают сразу после публикации
25 сентября три публикации были отмечены как успешные, хотя API приложений падал через несколько секунд после каждого запуска. Поскольку публикации «прошли», откат не запускался; фоновая проверка провалила их несколько минут спустя, и весь сайт стал отдавать 404. Проверка работоспособности считала падения в списке событий хостинг-платформы, а этот список хранит только последние пять событий на каждую машину, поэтому цикл падений постоянно его заполняет, и счётчик не растёт. Теперь проверка сравнивает время самого нового падения, и публикация, которая падает по кругу, проваливается и возвращает предыдущую версию.
Чего ждёт публикация
Когда вы публикуете приложение с бэкендом в Mythex, новая версия разворачивается на машинах нашей хостинг-платформы, и проверка работоспособности решает, стала ли публикация активной или провалилась. Если она провалилась, откат возвращает предыдущую версию.
Сложность в том, что «запустилась» не значит «обслуживает запросы». Платформа отмечает машину как запущенную, как только она поднялась. Приложение, которое падает через секунду после старта, немедленно перезапускается, и через мгновение снова числится запущенным. Поэтому проверка работоспособности со временем обросла несколькими слоями:
- Смотреть дважды. Когда машина отмечена как запущенная, подождать немного и посмотреть снова. Падение при старте часто проваливает вторую проверку.
- Следить за циклами падений. Если платформа сообщает, что машина падала снова и снова и она перестала её перезапускать, публикация проваливается.
- Считать падения с начала публикации. Два просмотра «запущено» могут прийтись на разные стороны перезапуска. Поэтому, если с начала этой публикации было хоть одно падение, смотрим третий раз; если падений больше, чем при предыдущем просмотре, приложение не держится.
Именно третья проверка и провалилась в этом случае.
Почему счётчик не менялся
Информация о падениях берётся из списка событий каждой машины: запуски, выходы, перезапуски. Платформа хранит только последние пять событий на машину.
Представьте приложение, которое падает через 2,5 секунды после каждого запуска, как и одно из них. Каждый цикл добавляет падение и запуск. Через пару циклов список заполняется. С этого момента каждое новое событие вытесняет старое. Список сдвигается вперёд, но всегда содержит примерно одну и ту же смесь: пару падений и пару запусков.
Поэтому вопрос «сколько падений с начала публикации» возвращал одно и то же число при каждом просмотре. Второй просмотр видел, скажем, два падения. Третий тоже видел два — другие падения, но то же количество. Проверка решила, что падения прекратились, и пропустила публикацию.
Это случилось трижды 25 сентября, в двух приложениях. Каждый раз:
- публикация отмечалась как активная,
- откат не запускался, потому что ничего не провалилось,
- наш сверщик, фоновая задача, которая проверяет деплои постфактум, провалил деплой через несколько минут,
- и весь сайт возвращал 404.
Публикацию, которая проваливается вовремя, можно восстановить: откат возвращает предыдущую версию. Публикация, которая проваливается через несколько минут после того, как её объявили успешной, уже пропустила свой откат.
Как мы это нашли
Мы воспроизвели проблему локально: взяли рабочий API, дали ему уснуть, затем опубликовали его заново с изменением, которое падало при запуске. Проверка работоспособности отметила публикацию как активную.
Это совпало с продакшеном: в каждой из трёх публикаций приложение умирало через несколько секунд после каждого запуска, но счётчик падений, который сравнивала проверка, между просмотрами не рос.
Исправление: сравнивать самое новое падение, а не количество
Вместо подсчёта падений проверка теперь смотрит на время самого последнего падения с начала публикации.
Если второй просмотр показывает хоть одно падение с начала публикации, проверка ждёт и смотрит в третий раз. Если самое новое падение на третьем просмотре позже, чем самое новое падение на втором, машина упала снова за это время. Она не держится, и публикация проваливается с сообщением, что машина многократно падала после публикации и всё ещё перезапускается.
Это работает, сколько бы ни было событий в списке. Скользящее окно из пяти событий может скрыть, сколько было падений, но не может скрыть, что только что произошло новое: самое новое событие всегда остаётся в списке.
После этого изменения локальное воспроизведение ведёт себя правильно: публикация проваливается, и восстанавливается предыдущий образ. Есть также тест, который имитирует поведение платформы — при каждом просмотре добавляется падение и запуск, а список обрезается до пяти, — и проверяет, что вердикт именно провал, а не успех.
Что мы из этого вынесли
- Знайте, как устроены данные, которые вы считаете. Подсчёт по ограниченному списку перестаёт что-либо значить, когда список заполнен. Ограничение не было скрыто; мы просто не продумали проверку с его учётом.
- Предпочитайте вопрос «случилось ли что-то новое?» вопросу «сколько всего случилось?». Самая новая отметка времени переживает обрезку. Общее количество — нет.
- Поздний провал хуже раннего. Одна и та же сломанная сборка безвредна, если публикация провалилась и откатилась, и валит сайт, если её сначала объявили успешной.
- Воспроизводите в реальных условиях. Для ошибки нужна была машина, которая уже упала достаточно раз, чтобы заполнить свой список событий. Одиночное быстрое падение в тесте этого бы не показало.
Это исправление дополняет другое, сделанное в тот же день, которое гарантировало, что откат возвращает старую версию, а не удаляет её: неудачная повторная публикация никогда не удаляет работающий бэкенд. Если ваше приложение не публикуется, потому что падает при старте, хорошая отправная точка — документация устранение неполадок публикации и наш гайд о том, как отлаживать приложение, созданное с ИИ.