Blog

Naciśnięcie Publikuj dwa razy publikuje tylko raz

Trzy żądania publikacji tej samej aplikacji dotarły do naszego API w odstępie 84 milisekund, a każde uruchomiło pełne wdrożenie tej aplikacji. Ścigały się na tym samym hostingu, kilka powstałych wydań się nie udało, bo inne już tam dotarły, a jedynym widocznym śladem był e-mail o nieudanym wydaniu — sama aplikacja skończyła się dobrze. Teraz publikacja, która przychodzi, gdy inna dla tej samej aplikacji już trwa, nie uruchamia drugiego builda. Baza danych decyduje, które żądanie wygrywa, a pozostałe dostają prawdę: publikacja jest już w toku.

Co się stało

Gdy publikujesz aplikację w Mythex, API zapisuje dla niej wdrożenie i prosi nasz orkiestrator — usługę, która uruchamia buildy — o zbudowanie i wydanie jej. Publikować można z przycisku Publikuj, z innej karty przeglądarki albo z agenta, gdy o to poprosisz. Podwójne kliknięcie, dwie karty albo żądanie agenta, które dotarło dwa razy, mogą wygenerować więcej niż jedną publikację tej samej aplikacji w prawie tym samym momencie.

W tym przypadku trzy dotarły w ciągu 84 ms. Każda uruchomiła pełne wdrożenie. Na platformie hostingowej wydania dla tej samej aplikacji powstały w tej samej sekundzie, a trzy z nich się nie udały, bo inne były już w toku. Późniejsze wydanie utknęło, ale aplikacja działała potem.

Właśnie to sprawiło, że łatwo było to przeoczyć. Użytkownik widział jedną działającą aplikację. Błędy pojawiały się tylko jako e-maile o nieudanych wydaniach.

Dlaczego nic tego nie powstrzymało

Kod publikacji miał krok, który patrzył na wdrożenia w toku dla projektu. Ale robił coś innego: przed startem oznaczał każde wdrożenie w toku jako nieudane — „zastąpione przez nową publikację”. Miał sprzątać buildy pozostawione przez proces, który się zawiesił.

W praktyce oznaczało to, że druga publikacja, która przyszła w trakcie builda, ogłaszała pierwszą za martwą i startowała własną. Obie biegły do końca przeciwko tej samej aplikacji. Nie było blokady; był krok, który aktywnie czyścił drogę dla wyścigu.

Był też drugi, dziwniejszy objaw przy pierwszej publikacji aplikacji. Bez rekordu wdrożenia, który można by ponownie użyć, dwie równoległe pierwsze publikacje próbowały wstawić ten sam rekord. Baza danych ma unikalny indeks na nazwie linku aplikacji (jej slugu, jak twoja-aplikacja w twoja-aplikacja.mythex.ai), więc druga wstawka się nie udała. API zamieniło to na komunikat „ta nazwa linku jest zajęta” — co sugerowało użytkownikowi zmianę nazwy aplikacji, która właśnie w tym momencie publikowała się pod tą nazwą bez problemu.

Poprawka: roszczenie jest blokadą

Zajęcie rekordu wdrożenia to teraz jeden warunkowy zapis do bazy: weź ten wiersz i oznacz go jako budowany tylko wtedy, gdy nikt go nie publikuje. Ponieważ to jedna instrukcja, baza danych rozstrzyga żądania, które przychodzą w tej samej milisekundzie. Dokładnie jedno zmienia wiersz. Pozostałe widzą, że zmieniono zero wierszy, zatrzymują się i nigdy nie docierają do orkiestratora.

Blokada potrzebuje też wyjścia, gdy jej posiadacz umrze. Jeśli API zrestartowało się w trakcie publikacji, wiersz zostałby oznaczony jako „budowany” na zawsze, a aplikacji nie dałoby się opublikować. Dlatego wiersz, którego właściciel przestał wysyłać sygnały życia — uruchomiona publikacja aktualizuje wiersz co 30 sekund, a pięć minut ciszy liczy się jako martwe — może zostać zajęty ponownie. Ten próg pochodzi z sygnału życia, a nie ze zgadywania, jak długo trwa build. Budowanie własnego Dockerfile’a użytkownika może trwać długo, a wolny build, który nadal sygnalizuje życie, zachowuje swoje roszczenie.

Krok, który oznaczał jako nieudane wszystkie wdrożenia w toku, nie działa już przed publikacją. Czyszczone są tylko wiersze, które pozostawił martwy proces.

Mówienie przegranemu prawdy

Żądanie, które przegrywa, dostaje teraz jasną odpowiedź: publish_in_progress wraz z identyfikatorem wdrożenia, które trwa. Każde miejsce, które może uruchomić publikację, to obsługuje:

  • Przestrzeń roboczej podąża za buildem, który już trwa, zamiast pokazywać błąd nad nim.
  • Agent dostaje informację, że publikacja tej aplikacji już trwa i ma na nią poczekać, zamiast informacji, że coś się nie udało — co wcześniej mogło skłonić go do „naprawiania” Dockerfile’a i ponawiania.
  • Kolizja przy pierwszej publikacji na nazwie linku jest sprawdzana: jeśli rekord z tą nazwą należy do trwającej publikacji tego samego projektu, odpowiedź brzmi „już się publikuje”, a nie „ta nazwa jest zajęta”. Prawdziwa kolizja z innym projektem nadal dostaje komunikat o zmianie nazwy.

Jak to sprawdziliśmy

Napisaliśmy skrypt odtwarzający problem: uruchamia trzy publikacje jednej aplikacji naraz na prawdziwej bazie danych z atrapą orkiestratora i liczy, ile wdrożeń zostało zamówionych. Obejmuje pierwszą publikację i ponowną publikację żywej aplikacji. Przed poprawką liczył dwa wdrożenia. Po — jedno.

Skrypt obejmuje też wyjście awaryjne: oznacza wiersz aplikacji jako budowany bez sygnału życia przez dziesięć minut, publikuje ponownie i sprawdza, czy nowa publikacja zajmuje wiersz i się uruchamia.

Testy jednostkowe trzymają blokadę do dwóch rzeczy, które blokada musi robić: z trzech równoczesnych ponownych publikacji dokładnie jedna przechodzi, a pozostałe dwie dostają informację, że publikacja trwa; oraz build, którego właściciel umarł, może zostać zajęty. Inny test sprawdza, że nowa próba nie przenosi komunikatu o błędzie z poprzedniej.

Co z tego wynieśliśmy

  • Kod sprzątający może być wyścigiem w przebraniu. „Oznacz wszystko w toku jako nieudane, a potem zacznij” wygląda na higienę. Przy dwóch wywołujących to każdy anuluje drugiego.
  • Niech decyduje baza danych. Odczyt, po którym następuje zapis, zostawia lukę dla innego żądania. Pojedyncza warunkowa instrukcja jej nie zostawia.
  • Każda blokada potrzebuje wygaśnięcia powiązanego z życiem procesu. Bez tego awaria zamienia się w zablokowaną aplikację.
  • Komunikaty o błędach to instrukcje. „Ta nazwa jest zajęta” kazało ludziom zrobić złą rzecz. Właściwy komunikat był prostszy i prawdziwy.

Więcej o publikowaniu znajdziesz w dokumentacji: jak opublikować. A powiązana poprawka: opublikowana aplikacja zostaje online podczas ponownej publikacji.

Mythex · Pricing · Zbuduj wszystko z AI · Szablony stron i aplikacji · Mythex w porównaniu · Słownik budowania aplikacji z AI · Darmowe narzędzia do budowania aplikacji z 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 ਪੰਜਾਬੀ Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)