Blog

Dvostruki pritisak na Publish sada objavljuje jednom

Tri zahteva za objavu iste aplikacije stigla su do našeg API-ja na razmaku od 84 milisekunde, i svaki je pokrenuo pun deploy te aplikacije. Trkali su se za isti hosting, nekoliko nastalih izdanja nije uspelo jer je drugi već stigao pre njih, a jedini vidljiv znak bio je email „release failed“ — sama aplikacija je na kraju bila u redu. Sada zahtev za objavu koji stigne dok se za istu aplikaciju već izvodi druga ne pokreće novu gradnju. Baza odlučuje koji zahtev pobeđuje, a ostali dobijaju istinu: objava je već u toku.

Šta se dogodilo

Kada objavite aplikaciju na Mythex-u, API beleži deployment za nju i traži od našeg orkestratora — servisa koji pokreće gradnje — da je izgradi i pusti u rad. Objava može da počne sa dugmeta Publish, iz drugog taba pregledača ili od agenta kada to zatražite. Dvostruki klik, dva taba ili zahtev agenta koji stigne dvaput mogu svi da proizvedu više objava iste aplikacije u skoro isto vreme.

U ovom slučaju, tri su stigla u roku od 84 ms. Svaka je pokrenula pun deploy. Na platformi za hosting, izdanja za istu aplikaciju kreirana su u istoj sekundi, a tri su otkazala jer su ostala već bila u toku. Jedno kasnije izdanje je zaglavilo, a aplikacija je kasnije radila.

To je i ono što je olakšavalo previđanje. Korisnik je video jednu radnu aplikaciju. Problemi su se pokazali samo kao emailovi o neuspelom izdanju.

Zašto ništa nije zaustavilo

Kod za objavu ipak je imao korak koji je gledao deploy-e koji su u toku za projekat. Ali on je pre pokretanja označavao svaki deploy u toku kao neuspeo — „zamenjen novom objavom“. Trebalo je da počisti gradnje koje je ostavio proces koji je pao.

U praksi, drugi zahtev koji stigne sred gradnje proglasio bi prvu mrtvom i pokrenuo svoju. Obe su išle do kraja protiv iste aplikacije. Nije bilo zaključavanja; postojao je korak koji je aktivno čistio put za trku.

Postojao je i drugi, čudniji simptom za prvu objavu aplikacije. Pošto još nije postojao zapis deploy-a za ponovnu upotrebu, dve istovremene prve objave pokušale su da upišu isti. Baza ima jedinstveni indeks na link-nazivu aplikacije (njenom slug-u, poput your-app u your-app.mythex.ai), pa je drugi upis propao. API je to preveo u „taj naziv linka je zauzet“ — savetujući korisnika da preimenuje aplikaciju koja je u tom trenutku uspešno bila objavljena pod tim imenom.

Ispravka: zahtev je zaključavanje

Preuzimanje zapisa deploy-a sada je jedno uslovno ažuriranje u bazi: uzmi ovaj red i označi ga kao u gradnji samo ako ga niko ne objavljuje. Pošto je to jedna naredba, baza rešava zahteve koji stignu u istoj milisekundi. Tačno jedan od njih menja red. Ostali vide da je promenjeno nula redova, zaustave se i nikad ne stignu do orkestratora.

Zaključavanju je potreban i način izlaska kada njegov nosilac umre. Ako se API ponovo pokrene usred objave, red bi zauvek ostao označen kao „building“ i aplikacija se ne bi mogla objaviti. Zato red čiji vlasnik prestane da šalje heartbeat — tekuća objava ažurira red svakih 30 sekundi, a pet minuta tišine znači da je mrtav — može ponovo da se preuzme. Taj prag dolazi od heartbeat-a, a ne od pogađanja koliko gradnja traje. Gradnja korisnikovog sopstvenog Dockerfile-a može legitimno dugo da traje, a spora gradnja koja i dalje šalje heartbeat zadržava svoje pravo.

Korak koji je označavao svaki deploy u toku kao neuspeo više se ne pokreće pre objave. Brišu se samo redovi koje je ostavio mrtav proces.

Govoreći gubitniku istinu

Zahtev koji gubi sada dobija jasan odgovor: publish_in_progress, zajedno sa ID-jem deployment-a koji se izvodi. Svako mesto koje može da pokrene objavu to obrađuje:

  • Radni prostor prati gradnju koja je već u toku umesto da prikazuje grešku preko nje.
  • Agent dobija poruku da objava ove aplikacije već traje i da sačeka, umesto da mu se kaže da nešto nije uspelo — što je ranije moglo da ga natera da „popravlja“ Dockerfile i pokuša ponovo.
  • Sudar pri prvoj objavi na link-nazivu se proverava: ako red koji drži naziv predstavlja sopstvenu objavu ovog projekta u toku, odgovor je „već se objavljuje“, a ne „taj naziv je zauzet“. Pravi sudar sa drugim projektom i dalje dobija poruku o preimenovanju.

Kako smo proverili

Napisali smo skriptu za reprodukciju koja pokreće tri objave jedne aplikacije istovremeno, na pravoj bazi i sa lažnim orkestratorom, i broji koliko deploy-a orkestrator dobija. Obuhvata prvu objavu i ponovnu objavu žive aplikacije. Pre ispravke brojala je dva deploy-a. Posle — jedan.

Skripta takođe pokriva izlaz za hitne slučajeve: označava red aplikacije kao u gradnji bez heartbeat-a deset minuta, ponovo objavljuje i proverava da nova objava preuzme red i pokrene se.

Jedinični testovi drže zaključavanje na dve stvari koje ono mora da radi: od tri istovremene ponovne objave tačno jedna prolazi, a ostale dve dobijaju poruku da objava traje; i gradnja čiji je vlasnik umro može da se preuzme. Drugi test proverava da novi pokušaj ne nosi poruku o grešci prethodnog pokušaja.

Šta smo izvukli iz ovoga

  • Kod za čišćenje može biti trka u prerušenom obliku. „Označi sve što je u toku kao neuspelo, pa počni“ zvuči kao higijena. Sa dva pozivaoca, to je svaki od njih otkazivanje onog drugog.
  • Neka baza bude arbitar. Čitanje pa upis ostavlja prostor za drugi zahtev. Jedno uslovno ažuriranje ne ostavlja.
  • Svako zaključavanje treba rok isteka vezan za živost. Bez toga, pad se pretvara u zaglavljenu aplikaciju.
  • Poruke o grešci su uputstva. „Taj naziv je zauzet“ navodilo je korisnike da urade pogrešnu stvar. Ispravna poruka bila je jednostavnija i tačna.

Više o objavi u dokumentaciji: kako objaviti. I srodna ispravka: vaša objavljena aplikacija ostaje aktivna dok je ponovo objavljujete.

Mythex · Pricing · Pravite bilo šta uz AI · Šabloni za sajtove i aplikacije · Mythex u poređenju · Rečnik pojmova za pravljenje aplikacija uz AI · Besplatni alati za pravljenje aplikacija uz AI · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)