Julkaisun painaminen kahdesti julkaisee nyt kerran
Kolme julkaisupyyntöä samalle sovellukselle saapui API:lemme 84 millisekunnin välein, ja jokainen käynnisti sovelluksen täyden julkaisun. Ne kilpailivat keskenään samalle hostaukselle, ja useat tuloksena olleet julkaisut epäonnistuivat, koska joku toinen oli ehtinyt ensin. Ainoa näkyvä merkki oli „julkaisu epäonnistui” -sähköposti — sovellus itse oli lopulta kunnossa. Nyt julkaisu, joka saapuu, kun toinen julkaisu on jo käynnissä samalle sovellukselle, ei käynnistä toista rakennusta. Tietokanta päättää, mikä pyyntö voittaa, ja muut kerrotaan totuus: julkaisu on jo käynnissä.
Mitä tapahtui
Kun julkaiset sovelluksen Mythexissä, API tallentaa julkaisutietueen ja pyytää orkestraattoriamme — palvelua, joka ajaa rakennukset — rakentamaan ja julkaisemaan sen. Julkaisu voi alkaa Julkaise-painikkeesta, toisesta selainvälilehdestä tai agentilta, kun pyydät sitä julkaisemaan. Kaksoisnapsautus, kaksi välilehteä tai agentin pyyntö, joka saapuu kahdesti, voi kaikki tuottaa useamman julkaisun samalle sovellukselle lähes samaan aikaan.
Tässä tapauksessa kolme saapui 84 ms:n sisällä. Jokainen ajoi täyden julkaisun. Hostausalustalla samalle sovellukselle luotiin julkaisuja samassa sekunnissa, ja kolme niistä epäonnistui, koska muut olivat jo käynnissä. Yksi myöhempi julkaisu jäi jumiin, mutta sovellus toimi sen jälkeen.
Siksi tämä oli helppo jättää huomaamatta. Käyttäjä näki yhden toimivan sovelluksen. Epäonnistumiset näkyivät vain julkaisun epäonnistumissähköposteissa.
Miksi mikään ei pysäyttänyt sitä
Julkaisukoodissa oli vaihe, joka tarkisti projektin käynnissä olevat julkaisut. Mutta se merkitsi jokaisen käynnissä olevan julkaisun epäonnistuneeksi ennen uuden aloittamista — „korvattu uudella julkaisulla”. Tarkoitus oli siivota rakennukset, jotka kaatunut prosessi oli jättänyt jälkeensä.
Käytännössä toinen julkaisu, joka saapui kesken rakennuksen, julisti ensimmäisen kuolleeksi ja aloitti oman. Molemmat ajoivat loppuun samaa sovellusta vastaan. Lukkoa ei ollut; oli vaihe, joka aktiivisesti raivasi tietä kilpailutilanteelle.
Sovelluksen ensimmäisessä julkaisussa oli toinen, oudompi oire. Koska julkaisutietuetta ei ollut vielä olemassa uudelleenkäytettäväksi, kaksi samanaikaista ensimmäistä julkaisua yritti molemmat lisätä yhden. Tietokannassa on yksilöllinen indeksi sovelluksen linkkinimelle (sen slugille, kuten your-app osoitteessa your-app.mythex.ai), joten toinen lisäys epäonnistui. API muutti sen viestiksi „tuo linkkinimi on varattu” — ja neuvoi käyttäjää nimeämään sovelluksen uudelleen, vaikka se juuri silloin julkaistiin onnistuneesti tuolla nimellä.
Korjaus: lukko on vaatimus itsessään
Julkaisutietueen varaaminen on nyt yksi ehdollinen tietokantapäivitys: merkitse tämä rivi rakentumaan vain jos kukaan ei julkaise sitä. Koska se on yksi lause, tietokanta ratkaisee samassa millisekunnissa saapuvat pyynnöt. Täsmälleen yksi niistä muuttaa riviä. Muut näkevät, että nolla riviä muuttui, pysähtyvät eivätkä koskaan saavuta orkestraattoria.
Lukolla täytyy myös olla tie ulos, kun sen haltija kuolee. Jos API käynnistyi uudelleen julkaisun kesken, rivi jäisi merkityksi „rakentumaan” ikuisesti, ja sovellusta ei voisi julkaista. Siksi rivin, jonka omistaja on lakannut lähettämästä sykettä, voi varata uudelleen — käynnissä oleva julkaisu päivittää riviä 30 sekunnin välein, ja viisi minuuttia hiljaisuutta lasketaan kuolleeksi. Tuo kynnys tulee sykkeestä, ei arvauksesta siitä, kuinka kauan rakennus kestää. Käyttäjän oman Dockerfilen rakentaminen voi perustellusti kestää pitkään, ja hidas rakennus, joka yhä sykkii, pitää varauksensa.
Vaihe, joka epäonnistutti kaikki käynnissä olevat julkaisut ennen julkaisua, ei enää aja. Vain prosessin jättämät jäänteet siivotaan.
Kerrotaan häviäjälle totuus
Pyyntö, joka häviää, saa nyt selkeän vastauksen: publish_in_progress ja käynnissä olevan julkaisun tunnus. Jokainen paikka, josta julkaisu voi alkaa, käsittelee sen:
- Työtila seuraa jo käynnissä olevaa rakennusta sen sijaan, että näyttäisi virheen sen päällä.
- Agentille kerrotaan, että tämän sovelluksen julkaisu on jo käynnissä ja että sen pitää odottaa sitä, eikä sille kerrota, että jokin epäonnistui — mikä aiemmin saattoi saada sen „korjaamaan” Dockerfilea ja yrittämään uudelleen.
- Ensimmäisen julkaisun törmäys linkkinimessä tarkistetaan: jos nimen varannut rivi on tämän projektin oma käynnissä oleva julkaisu, vastaus on „julkaistaan jo”, ei „nimi on varattu”. Todellinen törmäys toisen projektin kanssa saa yhä nimenmuutosviestin.
Miten tarkistimme sen
Kirjoitimme toistoskriptin, joka ajaa kolme julkaisua samasta sovelluksesta samaan aikaan todellista tietokantaa ja väärennettyä orkestraattoria vastaan, ja laskee, kuinka monta julkaisua orkestraattorilta pyydetään. Se kattaa ensimmäisen julkaisun ja elävän sovelluksen uudelleenjulkaisun. Ennen korjausta se laski kaksi julkaisua, korjauksen jälkeen yhden.
Skripti kattaa myös pakotien: se merkitsee sovelluksen rivin rakentumaan ilman sykettä kymmenen minuutin ajan, julkaisee uudelleen ja tarkistaa, että uusi julkaisu varaa rivin ja ajaa.
Yksikkötestit pitävät lukon kahdessa asiassa, jotka lukon täytyy tehdä: kolmesta samanaikaisesta uudelleenjulkaisusta täsmälleen yksi pääsee läpi, ja kaksi muuta kerrotaan, että julkaisu on käynnissä; ja rakennuksen, jonka omistaja kuoli, voi varata. Toinen testi tarkistaa, ettei uusi yritys peri edellisen yrityksen virheviestiä.
Mitä opimme
- Siivouskoodi voi olla kilpailutilanne naamioituna. „Merkitse kaikki käynnissä oleva epäonnistuneeksi ja aloita sitten” kuulostaa hygienialta. Kahdella kutsujalla se on jokaisen peruuttamista toisen toimesta.
- Anna tietokannan päättää. Luku ja sen jälkeinen kirjoitus jättää aukon toiselle pyynnölle. Yksi ehdollinen päivitys ei jätä.
- Jokaisella lukolla on oltava vanhenemisaika, joka on sidottu eloon. Ilman sitä kaatuminen muuttuu jumiin jääneeksi sovellukseksi.
- Virheviestit ovat ohjeita. „Tuo nimi on varattu” kehotti ihmisiä tekemään väärän asian. Oikea viesti oli yksinkertaisempi ja totta.
Lisää julkaisusta dokumentaatiossa: miten julkaistaan. Ja aiheeseen liittyvä korjaus: julkaistu sovelluksesi pysyy pystyssä, kun julkaiset sen uudelleen.