Prinderea aplicațiilor care se blochează imediat după publicare
Pe 25 septembrie, trei publicări au fost marcate ca reușite, deși API-ul aplicației se prăbușea la câteva secunde după fiecare pornire. Pentru că fuseseră marcate ca reușite, nu a rulat niciun rollback; o verificare în fundal le-a eșuat câteva minute mai târziu, iar întregul site a ajuns la o eroare 404. Verificarea de sănătate număra crash-urile din lista de evenimente a platformei de hosting — care păstrează mereu doar ultimele cinci evenimente pe mașină, deci o buclă de crash-uri o ține plină și numărul nu mai crește niciodată. Acum compară momentul celui mai recent crash, iar o publicare cu bucle de crash eșuează și restaurează versiunea anterioară.
Pentru ce așteaptă o publicare
Când publici o aplicație cu backend pe Mythex, noua versiune este pusă pe mașinile platformei noastre de hosting, iar o verificare de sănătate decide dacă publicarea a intrat în funcțiune sau a eșuat. Dacă a eșuat, un rollback pune la loc versiunea anterioară.
Partea grea este că „pornit” nu înseamnă același lucru cu „servește cereri”. Platforma raportează o mașină ca pornită imediat ce este sus. O aplicație care se prăbușește la o secundă după pornire este repornită imediat și, puțin mai târziu, apare din nou ca pornită. Așa că verificarea de sănătate a crescut în timp, pe câteva straturi:
- Verifică de două ori. După ce o mașină apare ca pornită, așteaptă puțin și verifică din nou. Un crash la pornire eșuează adesea a doua verificare.
- Urmărește buclele de crash. Dacă platforma raportează că o mașină a tot crăpat și că a renunțat să o repornească, eșuează.
- Numără crash-urile de la începutul publicării. Două verificări „pornit” pot cădea de o parte și de alta a unei reporniri. Deci, dacă a avut loc vreun crash de când a început publicarea, verifică a treia oară; dacă sunt mai multe crash-uri decât la verificarea anterioară, aplicația nu rămâne în picioare.
A treia verificare este cea care a eșuat aici.
De ce numărul rămânea constant
Informațiile despre crash vin din lista de evenimente a fiecărei mașini: porniri, opriri, reporniri. Platforma păstrează doar ultimele cinci evenimente pe mașină.
Gândește-te la o aplicație care se prăbușește la 2,5 secunde după fiecare pornire, cum făcea una dintre ele. Fiecare ciclu adaugă un crash și o pornire. După câteva cicluri, lista este plină. De atunci, fiecare eveniment nou împinge un eveniment vechi afară. Lista avansează, dar păstrează mereu aproximativ același amestec: câteva crash-uri, câteva porniri.
Așadar, „câte crash-uri au fost de când a început publicarea” dădea de fiecare dată același număr. A doua verificare vedea, să zicem, două crash-uri. A treia vedea tot două — alte crash-uri, dar același număr. Verificarea concluziona că prăbușirile se opriseră și trecea publicarea.
Asta s-a întâmplat de trei ori pe 25 septembrie, la două aplicații. De fiecare dată:
- publicarea era marcată ca live,
- nu rula niciun rollback, pentru că nimic nu eșuase,
- reconcilierul nostru, o sarcină în fundal care verifică deployment-urile după fapt, a eșuat deployment-ul câteva minute mai târziu,
- și tot site-ul returna 404.
O publicare care eșuează la timp poate fi recuperată: rollback-ul restaurează versiunea anterioară. O publicare care eșuează cu câteva minute întârziere, după ce a fost declarată un succes, și-a sărit deja rollback-ul.
Cum l-am găsit
Am reprodus local: am luat un API funcțional, l-am lăsat să adoarmă, apoi l-am republicat cu o modificare care se prăbușea la pornire. Verificarea de sănătate l-a marcat ca live.
Asta corespundea producției: în fiecare dintre cele trei publicări, aplicația murea la câteva secunde după fiecare pornire, dar numărul de crash-uri pe care îl compara verificarea nu creștea niciodată între verificări.
Soluția: compară cel mai recent crash, nu numărul
În loc să numere crash-urile, verificarea se uită acum la momentul celui mai recent crash de la începutul publicării.
Dacă a doua verificare arată vreun crash de la începutul publicării, verificarea așteaptă și mai verifică o dată. Dacă cel mai recent crash de la a treia verificare este mai târziu decât cel mai recent de la a doua, mașina a crăpat din nou între timp. Nu rămâne în picioare, iar publicarea eșuează cu un mesaj care spune că mașina a crăpat în mod repetat de la publicare și încă se repornește.
Asta funcționează indiferent cât de scurtă este lista de evenimente. O fereastră glisantă de cinci evenimente poate ascunde câte crash-uri au fost, dar nu poate ascunde că tocmai a mai apărut unul — evenimentul cel mai nou este mereu cel păstrat.
Cu această schimbare, reproducerea locală se comportă corect: publicarea eșuează, iar imaginea anterioară este restaurată. Există și un test care simulează comportamentul platformei — fiecare verificare adaugă un crash și o pornire și taie lista la cinci — și verifică faptul că verdictul este eșec, nu trecere.
Ce am învățat
- Știi forma datelor pe care le numeri. O numărătoare peste o listă limitată nu mai înseamnă nimic odată ce lista e plină. Limita nu era ascunsă; doar nu proiectasem verificarea în jurul ei.
- Preferă „s-a întâmplat ceva nou?” în locul „câte s-au întâmplat?”. Cel mai nou timestamp supraviețuiește trunchierii. Un total nu.
- Un eșec târziu este mai rău decât unul timpuriu. Același build defect este inofensiv dacă publicarea eșuează și face rollback, dar doboară un site dacă este declarat succes mai întâi.
- Reproduce în condițiile reale. Bug-ul avea nevoie de o mașină care crăpase deja suficient cât să-i umple lista de evenimente. Un singur crash rapid într-un test nu l-ar fi arătat.
Această remediere se leagă de alta din aceeași zi, care a asigurat că rollback-ul pune la loc versiunea veche, în loc să o șteargă: o republicare eșuată nu șterge niciodată un backend funcțional. Dacă aplicația ta nu se publică pentru că se prăbușește la pornire, documentația de depanare a publicării și ghidul nostru despre depanarea unei aplicații construite cu AI sunt locuri bune de început.