Nepavykusi pakartotinė publikacija niekada neištrina veikiančio backend'o
Rugsėjo 25 d. nepavykusi pakartotinė publikacija ištrynė programėlės backend'ą, kuris buvo gyvas ir veikė apie dvi su puse valandos. Kodas, kuris atšaukia nepavykusią publikaciją, pašalino kiekvieną serverio programėlę, kurią publikacija palietė, — įskaitant tas, kurios jau veikė prieš jai prasidedant. Dabar ištrinamos tik tos programėlės, kurias sukūrė publikacija; egzistuojanti programėlė grąžinama į tiksliai tą versiją, kurią veikė prieš tai. Dar tos pačios dienos du tolesni pataisymai užtikrino, kad atšaukimas veiktų ir tada, kai pats platformos diegimo įrankis nepavyksta, ir kad sveika programėlė nebūtų pažymėta kaip nepavykusi vien todėl, kad miegojo.
Kaip publikuojamas backend'as
Kai jūsų Mythex programėlė turi backend'ą — API arba serverį, o ne vien statinius failus — publikavimas jį sukuria iš Dockerfile (recepto, kaip programėlė supakuojama į vaizdą) ir išskleidžia tą vaizdą ant mašinų mūsų hostingo platformoje. Tada health check'as laukia, kol nauja versija iš tikrųjų pasileis ir išliks veikianti, ir tik tada publikacija pažymima kaip gyva.
Jei kur nors pakeliui kas nors nepavyksta, paleidžiamas rollback'as, kad tai atšauktų. Teisingai atlikti rollback'ą svarbiau beveik už viską kitą publikavime, nes jis veikia būtent tada, kai jau kas nors vyksta ne taip.
1 problema: rollback'as ištrynė tai, ką turėjo atkurti
Rollback'as laikė sąrašą serverio programėlių, į kurias publikacija buvo išskleista, ir nepavykus sunaikindavo jas visas. Pirmajai publikacijai tai teisinga: programėlė sukurta ką tik, niekas nuo jos nepriklauso, o ištrynus viskas grįžta į buvusią padėtį.
Pakartotinei publikacijai tai visiškai neteisinga. Programėlė jau egzistavo ir aptarnavo tikrus lankytojus. Rugsėjo 25 d. nepavykusi pakartotinė publikacija ištrynė programėlės API, kuri buvo gyva apie dvi su puse valandos.
Tame pačiame kode slypėjo ir antras gedimo variantas. Jei ištrynimas neįvykdavo, sugedusi nauja versija likdavo veikti mašinose, o savininkui vis tiek sakydavome, kad jo dabartinė svetainė vis dar gyva. Kai health check'as gali nepavykti, nauja versija jau būna išskleista ant kiekvienos mašinos. Nieko nedaryti nėra tas pats, kas atšaukti.
Pataisymas: ištrinti tik tai, ką sukūrei, o kita atkurti
Dabar kiekviena publikacija, kiekvienai serverio programėlei, kurią palietė, įrašo:
- ar šią publikaciją sukūrė ji, ir
- jei ne, kurį vaizdą ji veikė prieš tai.
Nepavykus kiekviena programėlė gauna vieną iš trijų veiksmų:
- Sukurta šios publikacijos: ištrinti, kaip anksčiau.
- Jau egzistavo, ankstesnis vaizdas žinomas: iš naujo išskleisti ankstesnį vaizdą su ta pačia konfigūracija. Jokio build'o — tik vaizdas grįžta atgal.
- Jau egzistavo, ankstesnis vaizdas nežinomas: nieko nedaryti. Egzistuojanti programėlė niekada neištrinama, net kai negalime jos atkurti.
Šios taisyklės — maža gryna funkcija su savais testais, todėl sprendimas negali vėl nukrypti į „ištrinti viską“, kol nepažeidžiamas testas.
2 problema: diegimo įrankis galėjo nepavykti, kol mes dar nebuvome užrašę, kaip atšaukti
Netrukus po šios pataisos radome spragą. Platformos diegimo įrankis vykdo savo smoke patikras: jei nauji mašinos sugenda, jis baigiasi klaida. Bet tai daro po to, kai nauja versija jau yra mašinose.
Mūsų kodas programėlę įtraukdavo į rollback'o sąrašą tik tada, kai įrankis grąžindavo sėkmę. Kai pats įrankis nepavykdavo, programėlės dar nebuvo rollback'o sąraše, todėl nieko neatkūrė. Mūsų staging aplinkoje sugedusi pakartotinė publikacija paliko veikti sugedusį vaizdą.
Pataisymas: įrašyti atšaukimą prieš pradedant
Programėlė dabar įtraukiama į rollback'o sąrašą — su tuo, ar ji buvo sukurta ir kokį vaizdą veikė — kai tik paruošiama, dar prieš bet kokį vaizdo išskleidimą. Kas bepasitaikytų vėliau, rollback'as žino, ką atkurti. Patikrinome lokaliai: sugedusi pakartotinė publikacija nepavyko diegimo įrankio smoke patikrose, o ankstesnė versija toliau atsakinėjo.
3 problema: miegančią programėlę pažymėjome kaip „niekada nepasileido“
Mythex publikuotos programėlės, kad taupytų kreditus, neveikimo metu užmiega. Pakartotinė publikacija palieka miegančią mašiną sustabdytą, todėl health check'as ją turi pažadinti. Jis vieną kartą paprašydavo platformos paleisti mašiną ir atsakymo nepaisydavo.
Rugsėjo 25 d. tas vienas paleidimo prašymas nuėjo niekur. Sveika API liko sustabdyta visą penkių minučių health check'o laiką ir buvo pažymėta kaip „1 mašina niekada nepasileido (paskutinė būsena: stopped)“.
Platforma kartais tikrai atsisako paleisti. Atkurdami šią problemą ji be mūsų provokavimo grąžino konfliktą „lease currently held“, o mašinai atnaujinant ji atsisako paleidimų.
Pataisymas: klausti vėl ir pabelsti į duris
Health check'as dabar:
- kol laukia, kas 20 sekundžių prašo paleisti sustabdytą mašiną iš naujo,
- įrašo, ką platforma atsakė, kai atsisako, ir
- išsiunčia vieną įprastą užklausą programėlei, kaip ją siųstų lankytojas — platformos proxy pažadina miegančią mašiną, kai ateina užklausa.
Atkūrėme tai tikrojoje platformoje su laikina programėle, kurios pirmasis paleidimas buvo atmestas. Prieš pataisą publikacija nepavyko po 91 sekundės. Po pataisos ji publikuota per 20 sekundžių. Programėlė, kuri tikrai sugenda paleidžiant, vis tiek nepavyksta, kaip ir turi.
Ką iš to pasiėmėme
- Rollback'as turi skirti naujas ir egzistuojančias programėles. „Atšaukti“ tam, ką sukūrei, reiškia ištrinti. „Atšaukti“ tam, kas jau buvo, reiškia atkurti. Su tuo elgtis vienodai — tai kaip apsauginis mechanizmas ištrina veikiančią programėlę.
- Užrašyk, kaip atšaukti, prieš veikdamas. Jei atšaukimas įrašomas tik po to, kai žingsnis pavyksta, žingsnis, nepavykęs per pusę, nepalieka ko atšaukti.
- Niekada neignoruok atsakymo į užklausą, nuo kurios priklausai. Vienas ignoruotas „ne“ pavertė sveiką programėlę nepavykusia publikacija.
Susijęs tos pačios dienos pataisymas: kaip sugaunamos programėlės, kurios sugenda iš karto po publikavimo. Kas nutinka, kai publikacija nepavyksta jūsų pusėje, žr. publikavimo trikčių šalinimą dokumentacijoje.