Улавяне на приложения, които се срутват веднага след публикуване
На 25 септември три публикации бяха отбелязани като успешни, въпреки че API-то на приложението се срутваше няколко секунди след всяко стартиране. Тъй като бяха „успешни“, не се пусна връщане назад; фонова проверка ги обяви за неуспешни няколко минути по-късно и целият сайт върна 404. Проверката на състоянието броеше сривовете в списъка със събития на хостинг платформата — който съдържа само последните пет събития на машина, така че цикъл на срив го запълва и броят никога не нараства. Сега тя сравнява времето на най-новия срив, а публикация с цикъл на срив се проваля и възстановява предишната версия.
Какво чака една публикация
Когато публикуваш приложение с бекенд в Mythex, новата версия се разгръща върху машини в хостинг платформата ни, а проверка на състоянието решава дали публикацията е станала активна, или се е провалила. Ако се е провалила, връщане назад възстановява предишната версия.
Трудното е, че „стартирана“ не е същото като „обслужва“. Платформата отчита машината като стартирана веднага щом е вдигната. Приложение, което се срутва една секунда след старта, се рестартира веднага и миг по-късно се чете отново като стартирано. Затова проверката на състоянието беше натрупала няколко слоя с времето:
- Погледни два пъти. След като машината се отчете като стартирана, изчакай кратко и погледни отново. Срив при старт често проваля втория поглед.
- Търси цикли на срив. Ако платформата съобщи, че машина е сривала многократно и е престанала да я рестартира, провали.
- Брой сривовете от началото на публикацията. Два „стартирани“ погледа може да обхващат рестартиране. Затова ако е имало срив след началото на тази публикация, погледни трети път; ако сривовете са повече, отколкото при предишния поглед, приложението не остава живо.
Третата проверка е тази, която се провали тук.
Защо броят остана равен
Информацията за сривовете идва от списъка със събития на всяка машина: стартирания, спирания, рестартирания. Платформата пази само последните пет събития на машина.
Представи си приложение, което се срива 2,5 секунди след всяко стартиране, както едно от тях. Всеки цикъл добавя срив и стартиране. След няколко цикъла списъкът е пълен. От този момент всяко ново събитие изтласква стар. Списъкът се плъзга напред, но винаги съдържа приблизително една и съща смес: два-три срива, два-три стартирания.
Затова „колко сривове от началото на публикацията“ връщаше едно и също число при всеки поглед. Вторият поглед виждаше, да речем, два срива. Третият също виждаше два — други сривове, но същият брой. Проверката заключи, че сриването е спряло, и пропусна публикацията.
Това се случи три пъти на 25 септември, при две приложения. Всеки път:
- публикацията се отбелязваше като активна,
- не се пускаше връщане назад, защото нищо не се беше провалило,
- нашият reconciler — фонова задача, която проверява внедряванията след факта — провалваше внедряването няколко минути по-късно,
- и целият сайт връщаше 404.
Публикация, която се проваля навреме, може да се възстанови: връщането назад връща предишната версия. Публикация, която се проваля няколко минути по-късно, след като е била обявена за успешна, вече е пропуснала връщането си назад.
Как го открихме
Възпроизведохме го локално: взехме работещо API, оставихме го да заспи и го публикувахме отново с промяна, която се срива при стартиране. Проверката на състоянието го отбеляза като активно.
Това съвпадна с продукцията: при всяка от трите публикации приложението умираше секунди след всяко стартиране, а броят на сривовете, който проверката сравняваше, никога не нарастваше между погледите.
Корекцията: сравни най-новия срив, а не броя
Вместо да брои сривовете, проверката сега гледа времето на най-скорошния срив от началото на публикацията.
Ако вторият поглед покаже някакъв срив след началото на публикацията, проверката чака и поглежда трети път. Ако най-новият срив при третия поглед е по-късен от най-новия срив при втория, машината се е сринала отново междувременно. Тя не остава жива и публикацията се проваля със съобщение, че машината е сривала многократно след публикуването и още се рестартира.
Това работи, колкото и кратък да е списъкът със събития. Плъзгащ се прозорец от пет събития може да скрие колко сривове е имало, но не може да скрие, че току-що се е случил нов — най-новото събитие винаги е запазено.
С тази промяна локалното възпроизвеждане се държи правилно: публикацията се проваля и предишният образ се възстановява. Има и тест, който симулира поведението на платформата — всеки поглед добавя срив и стартиране и подрязва списъка до пет — и проверява, че присъдата е провал, а не успех.
Какво взехме от това
- Знай формата на данните, които броиш. Броенето върху ограничен списък престава да означава нещо, щом списъкът се напълни. Ограничението не беше скрито; просто не бяхме проектирали проверката около него.
- Предпочитай „стана ли нещо ново?“ пред „колко се случиха?“. Най-новата времева марка оцелява при отрязване. Общият брой не.
- Късният провал е по-лош от ранния. Същият счупен билд е безвреден, ако публикацията се провали и се върне назад, и сваля сайт, ако първо е обявен за успешен.
- Възпроизвеждай при реалните условия. Бъгът изискваше машина, която вече се е сринала достатъчно пъти, за да запълни списъка си със събития. Един бърз срив в тест нямаше да го покаже.
Тази корекция се допълва от друга от същия ден, която гарантира, че връщането назад възстановява старата версия, вместо да я изтрива: неуспешно повторно публикуване никога не изтрива работещ бекенд. Ако твоето приложение не се публикува, защото се срива при старт, отстраняване на проблеми при публикуване в документацията и нашето ръководство за отстраняване на грешки в приложение, изградено с ИИ са добри места за начало.