At fange apps, der crasher lige efter udgivelse
Den 25. september blev tre udgivelser markeret som vellykkede, selvom appens API crashede sekunder inde i hver opstart. Fordi de »lykkedes«, kørte ingen rollback; et baggrundstjek fejlede dem et par minutter senere, og hele siden gik til en 404. Sundhedstjekket talte crashes i hostingplatformens hændelsesliste — som kun ever holder de sidste fem hændelser pr. maskine, så en crash-løkke holder den fuld, og tallet vokser aldrig. Det sammenligner nu tidspunktet for det seneste crash i stedet, og en udgivelse i en crash-løkke fejler og gendanner den forrige version.
Hvad en udgivelse venter på
Når du udgiver en app med en backend på Mythex, rulles den nye version ud på maskiner hos vores hostingplatform, og et sundhedstjek afgør, om udgivelsen gik live eller fejlede. Hvis den fejlede, sætter en rollback den forrige version tilbage.
Det svære er, at »startet« ikke er det samme som »svarer«. Platformen rapporterer en maskine som startet, så snart den er oppe. En app, der crasher et sekund inde i opstarten, genstartes med det samme, og et øjeblik senere står den igen som startet. Så sundhedstjekket var over tid vokset til flere lag:
- Kig to gange. Efter at en maskine står som startet, vent kort og kig igen. Et crash ved opstart fejler ofte det andet kig.
- Hold øje med crash-løkker. Hvis platformen rapporterer, at en maskine blev ved med at crashe, og den holdt op med at genstarte den, så fejl.
- Tæl crashes siden udgivelsen startede. To »startet«-kig kan strække sig over en genstart. Så hvis der var crashes, siden denne udgivelse startede, kig en tredje gang; hvis der er flere crashes end ved det forrige kig, bliver appen ikke oppe.
Det tredje tjek er det, der fejlede her.
Hvorfor tallet blev stående
Crash-information kommer fra hver maskines hændelsesliste: starter, afslutninger, genstarter. Platformen gemmer kun de sidste fem hændelser pr. maskine.
Forestil dig en app, der crasher 2,5 sekunder inde i hver opstart, som en af dem gjorde. Hver cyklus tilføjer et crash og en start. Efter et par cyklusser er listen fuld. Derfra skubber hver ny hændelse en gammel ud. Listen glider fremad, men den rummer altid cirka den samme blanding: et par crashes, et par starter.
Så »hvor mange crashes siden udgivelsen startede« gav det samme tal ved hvert kig. Det andet kig så måske to crashes. Det tredje kig så også to — andre crashes, men det samme antal. Tjekket konkluderede, at crashingen var holdt op, og godkendte udgivelsen.
Det skete tre gange den 25. september, på tværs af to apps. Hver gang:
- blev udgivelsen markeret som live,
- kørte ingen rollback, fordi intet var fejlet,
- fejlede vores reconciler, et baggrundsjob, der tjekker deployments bagefter, deployment'et nogle minutter senere,
- og hele siden returnerede 404.
En udgivelse, der fejler i tide, kan gendannes: rollbacken sætter den forrige version tilbage. En udgivelse, der fejler nogle minutter for sent, efter at være erklæret en succes, har allerede sprunget sin rollback over.
Sådan fandt vi det
Vi reproducerede det lokalt: tog en fungerende API, lod den gå i dvale og udgav den igen med en ændring, der crashede ved opstart. Sundhedstjekket markerede den som live.
Det passede med produktion: i hver af de tre udgivelser crashede appen sekunder inde i hver opstart, men crashtallet, tjekket sammenlignede, steg aldrig mellem kigene.
Rettelsen: sammenlign det seneste crash, ikke tallet
I stedet for at tælle crashes kigger tjekket nu på tidspunktet for det seneste crash siden udgivelsen startede.
Hvis det andet kig viser et crash siden udgivelsen startede, venter tjekket og kigger en tredje gang. Hvis det seneste crash ved det tredje kig er senere end det seneste ved det andet, crashede maskinen igen i mellemtiden. Den bliver ikke oppe, og udgivelsen fejler med en besked om, at maskinen crashede gentagne gange siden udgivelsen og stadig genstarter.
Dette virker, uanset hvor kort hændelseslisten er. Et glidende vindue med fem hændelser kan skjule, hvor mange crashes der var, men det kan ikke skjule, at et nyt lige er sket — den nyeste hændelse er altid den, der gemmes.
Med denne ændring opfører den lokale reproduktion sig korrekt: udgivelsen fejler, og det forrige image gendannes. Der er også en test, der simulerer platformens adfærd — hvert kig tilføjer et crash og en start og trimmer listen til fem — og kontrollerer, at dommen er en fejl, ikke en godkendelse.
Det vi lærte
- Kend formen på de data, du tæller. En optælling over en begrænset liste betyder ikke længere noget, når listen er fuld. Begrænsningen var ikke skjult; vi havde bare ikke designet tjekket med den i tankerne.
- Foretræk »skete der noget nyt?« frem for »hvor mange skete der?« Det seneste tidsstempel overlever afkortning. En total gør ikke.
- En sen fejl er værre end en tidlig. Den samme ødelagte build er harmløs, hvis udgivelsen fejler og ruller tilbage, og tager en side ned, hvis den først erklæres en succes.
- Reproducér under de rigtige betingelser. Fejlen krævede en maskine, der allerede havde crashet nok gange til at fylde sin hændelsesliste. Et enkelt hurtigt crash i en test ville ikke have vist den.
Denne rettelse hænger sammen med en anden fra samme dag, som sikrede, at rollbacken sætter den gamle version tilbage i stedet for at slette den: en mislykket genudgivelse sletter aldrig en fungerende backend. Hvis din egen app ikke kan udgives, fordi den crasher ved start, er fejlfinding ved udgivelse i dokumentationen og vores guide til fejlfinding af en AI-bygget app gode steder at starte.